fix: default pinned models stop applying after a user's first page load (#28069)
Changing "Default Pinned Models" in admin settings had no effect for anyone who had already opened Open WebUI once. The sidebar copied the admin default into that user's own settings the first time it rendered and saved it to the server, which marked them as having customized their pins, so every later change to the default was ignored for them. Merely loading the page was enough, the user never had to touch a pin. The default is now resolved for display only, through a shared store that falls back to the admin list while the user has no pins of their own, the same way default models already work. Nothing is written to the user's settings until they actually pin, unpin or reorder something, at which point their choice takes over for good. Unpinning everything still persists an empty list rather than snapping back to the default. Users whose settings were already overwritten by the old behaviour keep that copy, since a stored pin list cannot be told apart from a deliberate one. Fixes a drag-reorder path that mixed sidebar positions with stored ones, and stops the sidebar section reopening itself after any unrelated settings change.
This commit is contained in:
1 parent
1a376ac17f
commit
1756c9d5d2
10 files changed
+99
-129
No files matched your search
@@ -1758,6 +1758,7 @@ export interface ModelConfig {
|
|||||||
export interface ModelMeta {
|
export interface ModelMeta {
|
||||||
toolIds: never[];
|
toolIds: never[];
|
||||||
description?: string;
|
description?: string;
|
||||||
|
hidden?: boolean;
|
||||||
capabilities?: object;
|
capabilities?: object;
|
||||||
profile_image_url?: string;
|
profile_image_url?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,14 @@
|
|||||||
import { onMount, onDestroy, getContext, tick } from 'svelte';
|
import { onMount, onDestroy, getContext, tick } from 'svelte';
|
||||||
const i18n = getContext('i18n');
|
const i18n = getContext('i18n');
|
||||||
|
|
||||||
import { config, models as _models, settings, showSettings, user } from '$lib/stores';
|
import {
|
||||||
|
config,
|
||||||
|
models as _models,
|
||||||
|
pinnedModels,
|
||||||
|
settings,
|
||||||
|
showSettings,
|
||||||
|
user
|
||||||
|
} from '$lib/stores';
|
||||||
import {
|
import {
|
||||||
createNewModel,
|
createNewModel,
|
||||||
deleteAllModels,
|
deleteAllModels,
|
||||||
@@ -607,15 +614,12 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
const pinModelHandler = async (modelId) => {
|
const pinModelHandler = async (modelId) => {
|
||||||
let pinnedModels = $settings?.pinnedModels ?? [];
|
settings.set({
|
||||||
|
...$settings,
|
||||||
if (pinnedModels.includes(modelId)) {
|
pinnedModels: $pinnedModels.includes(modelId)
|
||||||
pinnedModels = pinnedModels.filter((id) => id !== modelId);
|
? $pinnedModels.filter((id) => id !== modelId)
|
||||||
} else {
|
: [...$pinnedModels, modelId]
|
||||||
pinnedModels = [...new Set([...pinnedModels, modelId])];
|
});
|
||||||
}
|
|
||||||
|
|
||||||
settings.set({ ...$settings, pinnedModels: pinnedModels });
|
|
||||||
await updateUserSettings(localStorage.token, { ui: $settings });
|
await updateUserSettings(localStorage.token, { ui: $settings });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
import GlobeAlt from '$lib/components/icons/GlobeAlt.svelte';
|
import GlobeAlt from '$lib/components/icons/GlobeAlt.svelte';
|
||||||
import LockClosed from '$lib/components/icons/LockClosed.svelte';
|
import LockClosed from '$lib/components/icons/LockClosed.svelte';
|
||||||
|
|
||||||
import { config, settings } from '$lib/stores';
|
import { config, pinnedModels, settings } from '$lib/stores';
|
||||||
import Link from '$lib/components/icons/Link.svelte';
|
import Link from '$lib/components/icons/Link.svelte';
|
||||||
|
|
||||||
const i18n = getContext('i18n');
|
const i18n = getContext('i18n');
|
||||||
@@ -173,14 +173,14 @@
|
|||||||
class="select-none flex w-full gap-2 items-center h-[1.6875rem] px-2 text-[0.8125rem] font-normal cursor-pointer hover:bg-gray-50/40 dark:hover:bg-gray-800/40 rounded-xl"
|
class="select-none flex w-full gap-2 items-center h-[1.6875rem] px-2 text-[0.8125rem] font-normal cursor-pointer hover:bg-gray-50/40 dark:hover:bg-gray-800/40 rounded-xl"
|
||||||
on:click={() => runAndClose(() => pinModelHandler(model?.id))}
|
on:click={() => runAndClose(() => pinModelHandler(model?.id))}
|
||||||
>
|
>
|
||||||
{#if ($settings?.pinnedModels ?? []).includes(model?.id)}
|
{#if $pinnedModels.includes(model?.id)}
|
||||||
<PinSlash />
|
<PinSlash />
|
||||||
{:else}
|
{:else}
|
||||||
<Pin />
|
<Pin />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
{#if ($settings?.pinnedModels ?? []).includes(model?.id)}
|
{#if $pinnedModels.includes(model?.id)}
|
||||||
{$i18n.t('Hide from Sidebar')}
|
{$i18n.t('Hide from Sidebar')}
|
||||||
{:else}
|
{:else}
|
||||||
{$i18n.t('Keep in Sidebar')}
|
{$i18n.t('Keep in Sidebar')}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { models, settings, user } from '$lib/stores';
|
import { models, pinnedModels, settings, user } from '$lib/stores';
|
||||||
import { getContext } from 'svelte';
|
import { getContext } from 'svelte';
|
||||||
import { toast } from 'svelte-sonner';
|
import { toast } from 'svelte-sonner';
|
||||||
import Selector from './ModelSelector/Selector.svelte';
|
import Selector from './ModelSelector/Selector.svelte';
|
||||||
@@ -35,15 +35,12 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
const pinModelHandler = async (modelId) => {
|
const pinModelHandler = async (modelId) => {
|
||||||
let pinnedModels = $settings?.pinnedModels ?? [];
|
settings.set({
|
||||||
|
...$settings,
|
||||||
if (pinnedModels.includes(modelId)) {
|
pinnedModels: $pinnedModels.includes(modelId)
|
||||||
pinnedModels = pinnedModels.filter((id) => id !== modelId);
|
? $pinnedModels.filter((id) => id !== modelId)
|
||||||
} else {
|
: [...$pinnedModels, modelId]
|
||||||
pinnedModels = [...new Set([...pinnedModels, modelId])];
|
});
|
||||||
}
|
|
||||||
|
|
||||||
settings.set({ ...$settings, pinnedModels: pinnedModels });
|
|
||||||
await updateUserSettings(localStorage.token, { ui: $settings });
|
await updateUserSettings(localStorage.token, { ui: $settings });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
import PinSlash from '$lib/components/icons/PinSlash.svelte';
|
import PinSlash from '$lib/components/icons/PinSlash.svelte';
|
||||||
import Link from '$lib/components/icons/Link.svelte';
|
import Link from '$lib/components/icons/Link.svelte';
|
||||||
import Pencil from '$lib/components/icons/Pencil.svelte';
|
import Pencil from '$lib/components/icons/Pencil.svelte';
|
||||||
import { config, settings, showSettings, user } from '$lib/stores';
|
import { config, pinnedModels, settings, showSettings, user } from '$lib/stores';
|
||||||
import GlobeAlt from '$lib/components/icons/GlobeAlt.svelte';
|
import GlobeAlt from '$lib/components/icons/GlobeAlt.svelte';
|
||||||
|
|
||||||
const i18n = getContext('i18n');
|
const i18n = getContext('i18n');
|
||||||
@@ -104,7 +104,7 @@
|
|||||||
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
aria-pressed={($settings?.pinnedModels ?? []).includes(model?.id)}
|
aria-pressed={$pinnedModels.includes(model?.id)}
|
||||||
class="select-none flex h-[1.6875rem] w-full items-center gap-2 rounded-xl px-2 text-[0.8125rem] hover:bg-gray-50/40 dark:hover:bg-gray-800/40 transition"
|
class="select-none flex h-[1.6875rem] w-full items-center gap-2 rounded-xl px-2 text-[0.8125rem] hover:bg-gray-50/40 dark:hover:bg-gray-800/40 transition"
|
||||||
on:click={(e) => {
|
on:click={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@@ -114,14 +114,14 @@
|
|||||||
show = false;
|
show = false;
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{#if ($settings?.pinnedModels ?? []).includes(model?.id)}
|
{#if $pinnedModels.includes(model?.id)}
|
||||||
<PinSlash className="size-3.5" />
|
<PinSlash className="size-3.5" />
|
||||||
{:else}
|
{:else}
|
||||||
<Pin className="size-3.5" />
|
<Pin className="size-3.5" />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
{#if ($settings?.pinnedModels ?? []).includes(model?.id)}
|
{#if $pinnedModels.includes(model?.id)}
|
||||||
{$i18n.t('Hide from Sidebar')}
|
{$i18n.t('Hide from Sidebar')}
|
||||||
{:else}
|
{:else}
|
||||||
{$i18n.t('Keep in Sidebar')}
|
{$i18n.t('Keep in Sidebar')}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
socket,
|
socket,
|
||||||
config,
|
config,
|
||||||
isApp,
|
isApp,
|
||||||
models,
|
visiblePinnedModels,
|
||||||
selectedFolder,
|
selectedFolder,
|
||||||
WEBUI_NAME,
|
WEBUI_NAME,
|
||||||
sidebarWidth
|
sidebarWidth
|
||||||
@@ -120,9 +120,7 @@
|
|||||||
|
|
||||||
let showCreateFolderModal = false;
|
let showCreateFolderModal = false;
|
||||||
|
|
||||||
let pinnedModels = [];
|
let showPinnedModels = true;
|
||||||
|
|
||||||
let showPinnedModels = false;
|
|
||||||
let showPinnedNotes = false;
|
let showPinnedNotes = false;
|
||||||
let showChannels = false;
|
let showChannels = false;
|
||||||
let showFolders = false;
|
let showFolders = false;
|
||||||
@@ -683,12 +681,6 @@
|
|||||||
navElement.style['-webkit-app-region'] = 'drag';
|
navElement.style['-webkit-app-region'] = 'drag';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
|
||||||
settings.subscribe((value) => {
|
|
||||||
if (pinnedModels != value?.pinnedModels ?? []) {
|
|
||||||
pinnedModels = value?.pinnedModels ?? [];
|
|
||||||
showPinnedModels = pinnedModels.length > 0;
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -1285,7 +1277,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if ($models ?? []).length > 0 && (($settings?.pinnedModels ?? []).length > 0 || $config?.default_pinned_models)}
|
{#if $visiblePinnedModels.length > 0}
|
||||||
<SidebarSection
|
<SidebarSection
|
||||||
id="sidebar-models"
|
id="sidebar-models"
|
||||||
bind:open={showPinnedModels}
|
bind:open={showPinnedModels}
|
||||||
|
|||||||
@@ -1,18 +1,23 @@
|
|||||||
<script>
|
<script>
|
||||||
import Sortable from 'sortablejs';
|
import Sortable from 'sortablejs';
|
||||||
|
|
||||||
import { onDestroy, onMount, tick } from 'svelte';
|
import { onMount, tick } from 'svelte';
|
||||||
|
|
||||||
import { chatId, config, mobile, models, settings, showSidebar } from '$lib/stores';
|
import {
|
||||||
import { WEBUI_BASE_URL } from '$lib/constants';
|
chatId,
|
||||||
|
mobile,
|
||||||
|
models,
|
||||||
|
pinnedModels,
|
||||||
|
settings,
|
||||||
|
showSidebar,
|
||||||
|
visiblePinnedModels
|
||||||
|
} from '$lib/stores';
|
||||||
import { updateUserSettings } from '$lib/apis/users';
|
import { updateUserSettings } from '$lib/apis/users';
|
||||||
import PinnedModelItem from './PinnedModelItem.svelte';
|
import PinnedModelItem from './PinnedModelItem.svelte';
|
||||||
|
|
||||||
export let selectedChatId = null;
|
export let selectedChatId = null;
|
||||||
export let shiftKey = false;
|
export let shiftKey = false;
|
||||||
|
|
||||||
let pinnedModels = [];
|
|
||||||
|
|
||||||
const initPinnedModelsSortable = () => {
|
const initPinnedModelsSortable = () => {
|
||||||
const pinnedModelsList = document.getElementById('pinned-models-list');
|
const pinnedModelsList = document.getElementById('pinned-models-list');
|
||||||
if (pinnedModelsList && !$mobile) {
|
if (pinnedModelsList && !$mobile) {
|
||||||
@@ -28,91 +33,49 @@
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
onUpdate: async (event) => {
|
onUpdate: async (event) => {
|
||||||
const modelId = event.item.dataset.id;
|
const reorderedIds = [...$visiblePinnedModels];
|
||||||
const newIndex = event.newIndex;
|
const [movedId] = reorderedIds.splice(event.oldIndex, 1);
|
||||||
|
reorderedIds.splice(event.newIndex, 0, movedId);
|
||||||
|
|
||||||
const pinnedModels = $settings.pinnedModels;
|
// Keep pins for models the user cannot see
|
||||||
const oldIndex = pinnedModels.indexOf(modelId);
|
settings.set({
|
||||||
|
...$settings,
|
||||||
pinnedModels.splice(oldIndex, 1);
|
pinnedModels: [
|
||||||
pinnedModels.splice(newIndex, 0, modelId);
|
...reorderedIds,
|
||||||
|
...$pinnedModels.filter((id) => !$visiblePinnedModels.includes(id))
|
||||||
settings.set({ ...$settings, pinnedModels: pinnedModels });
|
]
|
||||||
|
});
|
||||||
await updateUserSettings(localStorage.token, { ui: $settings });
|
await updateUserSettings(localStorage.token, { ui: $settings });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let unsubscribeSettings;
|
|
||||||
|
|
||||||
const cleanupStalePinnedModels = async (modelIds) => {
|
|
||||||
const validModels = modelIds.filter((id) => {
|
|
||||||
const model = $models.find((m) => m.id === id);
|
|
||||||
// Remove if model not found (deleted) or if hidden
|
|
||||||
return model && !(model?.info?.meta?.hidden ?? false);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (validModels.length !== modelIds.length) {
|
|
||||||
pinnedModels = validModels;
|
|
||||||
settings.set({ ...$settings, pinnedModels: validModels });
|
|
||||||
await updateUserSettings(localStorage.token, { ui: $settings });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
pinnedModels = $settings?.pinnedModels ?? [];
|
|
||||||
|
|
||||||
if (pinnedModels.length === 0 && $config?.default_pinned_models) {
|
|
||||||
const defaultPinnedModels = ($config?.default_pinned_models).split(',').filter((id) => id);
|
|
||||||
pinnedModels = defaultPinnedModels.filter((id) => $models.find((model) => model.id === id));
|
|
||||||
|
|
||||||
settings.set({ ...$settings, pinnedModels });
|
|
||||||
await updateUserSettings(localStorage.token, { ui: $settings });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Auto-unpin hidden or deleted models
|
|
||||||
if (pinnedModels.length > 0) {
|
|
||||||
await cleanupStalePinnedModels(pinnedModels);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsubscribeSettings = settings.subscribe((value) => {
|
|
||||||
pinnedModels = value?.pinnedModels ?? [];
|
|
||||||
});
|
|
||||||
|
|
||||||
await tick();
|
await tick();
|
||||||
initPinnedModelsSortable();
|
initPinnedModelsSortable();
|
||||||
});
|
});
|
||||||
|
|
||||||
onDestroy(() => {
|
|
||||||
if (unsubscribeSettings) {
|
|
||||||
unsubscribeSettings();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="mt-0.5 pb-1.5" id="pinned-models-list">
|
<div class="mt-0.5 pb-1.5" id="pinned-models-list">
|
||||||
{#each pinnedModels as modelId (modelId)}
|
{#each $visiblePinnedModels as modelId (modelId)}
|
||||||
{@const model = $models.find((model) => model.id === modelId)}
|
<PinnedModelItem
|
||||||
{#if model}
|
model={$models.find((model) => model.id === modelId)}
|
||||||
<PinnedModelItem
|
{shiftKey}
|
||||||
{model}
|
onClick={() => {
|
||||||
{shiftKey}
|
selectedChatId = null;
|
||||||
onClick={() => {
|
chatId.set('');
|
||||||
selectedChatId = null;
|
if ($mobile) {
|
||||||
chatId.set('');
|
showSidebar.set(false);
|
||||||
if ($mobile) {
|
}
|
||||||
showSidebar.set(false);
|
}}
|
||||||
}
|
onUnpin={() => {
|
||||||
}}
|
settings.set({
|
||||||
onUnpin={($settings?.pinnedModels ?? []).includes(modelId)
|
...$settings,
|
||||||
? () => {
|
pinnedModels: $pinnedModels.filter((id) => id !== modelId)
|
||||||
const pinnedModels = $settings.pinnedModels.filter((id) => id !== modelId);
|
});
|
||||||
settings.set({ ...$settings, pinnedModels });
|
updateUserSettings(localStorage.token, { ui: $settings });
|
||||||
updateUserSettings(localStorage.token, { ui: $settings });
|
}}
|
||||||
}
|
/>
|
||||||
: null}
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
config,
|
config,
|
||||||
mobile,
|
mobile,
|
||||||
models as _models,
|
models as _models,
|
||||||
|
pinnedModels,
|
||||||
settings,
|
settings,
|
||||||
user,
|
user,
|
||||||
workspaceActions
|
workspaceActions
|
||||||
@@ -292,15 +293,12 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
const pinModelHandler = async (modelId) => {
|
const pinModelHandler = async (modelId) => {
|
||||||
let pinnedModels = $settings?.pinnedModels ?? [];
|
settings.set({
|
||||||
|
...$settings,
|
||||||
if (pinnedModels.includes(modelId)) {
|
pinnedModels: $pinnedModels.includes(modelId)
|
||||||
pinnedModels = pinnedModels.filter((id) => id !== modelId);
|
? $pinnedModels.filter((id) => id !== modelId)
|
||||||
} else {
|
: [...$pinnedModels, modelId]
|
||||||
pinnedModels = [...new Set([...pinnedModels, modelId])];
|
});
|
||||||
}
|
|
||||||
|
|
||||||
settings.set({ ...$settings, pinnedModels: pinnedModels });
|
|
||||||
await updateUserSettings(localStorage.token, { ui: $settings });
|
await updateUserSettings(localStorage.token, { ui: $settings });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
import Pin from '$lib/components/icons/Pin.svelte';
|
import Pin from '$lib/components/icons/Pin.svelte';
|
||||||
import PinSlash from '$lib/components/icons/PinSlash.svelte';
|
import PinSlash from '$lib/components/icons/PinSlash.svelte';
|
||||||
|
|
||||||
import { config, user as currentUser, settings } from '$lib/stores';
|
import { config, user as currentUser, pinnedModels, settings } from '$lib/stores';
|
||||||
import Link from '$lib/components/icons/Link.svelte';
|
import Link from '$lib/components/icons/Link.svelte';
|
||||||
|
|
||||||
const i18n = getContext('i18n');
|
const i18n = getContext('i18n');
|
||||||
@@ -131,14 +131,14 @@
|
|||||||
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[0.8125rem] hover:text-gray-900 dark:hover:text-gray-100"
|
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[0.8125rem] hover:text-gray-900 dark:hover:text-gray-100"
|
||||||
on:click={() => runAndClose(() => pinModelHandler(model?.id))}
|
on:click={() => runAndClose(() => pinModelHandler(model?.id))}
|
||||||
>
|
>
|
||||||
{#if ($settings?.pinnedModels ?? []).includes(model?.id)}
|
{#if $pinnedModels.includes(model?.id)}
|
||||||
<PinSlash />
|
<PinSlash />
|
||||||
{:else}
|
{:else}
|
||||||
<Pin />
|
<Pin />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
{#if ($settings?.pinnedModels ?? []).includes(model?.id)}
|
{#if $pinnedModels.includes(model?.id)}
|
||||||
{$i18n.t('Hide from Sidebar')}
|
{$i18n.t('Hide from Sidebar')}
|
||||||
{:else}
|
{:else}
|
||||||
{$i18n.t('Keep in Sidebar')}
|
{$i18n.t('Keep in Sidebar')}
|
||||||
|
|||||||
+17
-2
@@ -1,5 +1,5 @@
|
|||||||
import { APP_NAME } from '$lib/constants';
|
import { APP_NAME } from '$lib/constants';
|
||||||
import { type Writable, writable } from 'svelte/store';
|
import { type Writable, derived, writable } from 'svelte/store';
|
||||||
import type { ModelConfig } from '$lib/apis';
|
import type { ModelConfig } from '$lib/apis';
|
||||||
import type { Banner } from '$lib/types';
|
import type { Banner } from '$lib/types';
|
||||||
import type { Socket } from 'socket.io-client';
|
import type { Socket } from 'socket.io-client';
|
||||||
@@ -103,6 +103,20 @@ export const banners: Writable<Banner[]> = writable([]);
|
|||||||
|
|
||||||
export const settings: Writable<Settings> = writable({});
|
export const settings: Writable<Settings> = writable({});
|
||||||
|
|
||||||
|
// Users who never pinned a model follow the admin default, so changes to it keep reaching them
|
||||||
|
export const pinnedModels = derived([settings, config], ([$settings, $config]) =>
|
||||||
|
$settings?.pinnedModels === undefined
|
||||||
|
? ($config?.default_pinned_models ?? '').split(',').filter((id) => id)
|
||||||
|
: $settings.pinnedModels
|
||||||
|
);
|
||||||
|
|
||||||
|
// Pins for models the user cannot see are kept in their settings but left out of the sidebar
|
||||||
|
export const visiblePinnedModels = derived([pinnedModels, models], ([$pinnedModels, $models]) =>
|
||||||
|
$pinnedModels.filter((id) =>
|
||||||
|
$models.some((model) => model.id === id && !model.info?.meta?.hidden)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
export const audioQueue = writable<AudioQueue | null>(null);
|
export const audioQueue = writable<AudioQueue | null>(null);
|
||||||
export const chatRequestQueues: Writable<
|
export const chatRequestQueues: Writable<
|
||||||
Record<string, { id: string; prompt: string; files: any[] }[]>
|
Record<string, { id: string; prompt: string; files: any[] }[]>
|
||||||
@@ -200,7 +214,7 @@ type OllamaModelDetails = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type Settings = {
|
type Settings = {
|
||||||
pinnedModels?: never[];
|
pinnedModels?: string[];
|
||||||
toolServers?: never[];
|
toolServers?: never[];
|
||||||
detectArtifacts?: boolean;
|
detectArtifacts?: boolean;
|
||||||
showUpdateToast?: boolean;
|
showUpdateToast?: boolean;
|
||||||
@@ -310,6 +324,7 @@ type Config = {
|
|||||||
version: string;
|
version: string;
|
||||||
default_locale: string;
|
default_locale: string;
|
||||||
default_models: string;
|
default_models: string;
|
||||||
|
default_pinned_models?: string | null;
|
||||||
default_prompt_suggestions: PromptSuggestion[];
|
default_prompt_suggestions: PromptSuggestion[];
|
||||||
features: {
|
features: {
|
||||||
auth: boolean;
|
auth: boolean;
|
||||||
|
|||||||
Reference in new issue
Block a user