diff --git a/tools/ui/src/lib/components/app/models/discover/ModelsDiscoverDetails/ModelsDiscoverDetailsDownloadOptions/download-options.utils.ts b/tools/ui/src/lib/components/app/models/discover/ModelsDiscoverDetails/ModelsDiscoverDetailsDownloadOptions/download-options.utils.ts
new file mode 100644
index 0000000000..4dd8accb42
--- /dev/null
+++ b/tools/ui/src/lib/components/app/models/discover/ModelsDiscoverDetails/ModelsDiscoverDetailsDownloadOptions/download-options.utils.ts
@@ -0,0 +1,28 @@
+import { DRAFT_FILE_LABEL, MODEL_ID, PATH_SEPARATOR } from '$lib/constants';
+import { ModelSelectableFileKind } from '$lib/enums';
+import { HuggingFaceService } from '$lib/services';
+import { isAuxSidecar, isDraftSidecar } from '$lib/utils';
+
+/** Kind of a file path: the main weights, a draft sidecar, or an aux sidecar (mmproj). */
+export function classify(path: string): ModelSelectableFileKind {
+ const sidecar = HuggingFaceService.extractQuantMeta(path)?.sidecar;
+
+ if (!sidecar) return ModelSelectableFileKind.MAIN;
+
+ return isAuxSidecar(sidecar) ? ModelSelectableFileKind.AUX : ModelSelectableFileKind.DRAFT;
+}
+
+/** Display label of a file: its quant, else the file name without the extension. */
+export function labelFor(path: string): string {
+ const meta = HuggingFaceService.extractQuantMeta(path);
+
+ if (meta?.quant) return meta.quant;
+
+ // Quantless sidecar files: the chip badge already carries the sidecar type,
+ // so the label only marks draft files; aux sidecars badge alone.
+ if (meta?.sidecar) return isDraftSidecar(meta.sidecar) ? DRAFT_FILE_LABEL : '';
+
+ const basename = path.split(PATH_SEPARATOR).pop() ?? path;
+
+ return basename.replace(MODEL_ID.WEIGHT_EXTENSION_REGEX, '');
+}
diff --git a/tools/ui/src/lib/components/app/models/discover/ModelsDiscoverDetails/ModelsDiscoverDetailsDownloadOptions/index.ts b/tools/ui/src/lib/components/app/models/discover/ModelsDiscoverDetails/ModelsDiscoverDetailsDownloadOptions/index.ts
new file mode 100644
index 0000000000..b5c0ebef67
--- /dev/null
+++ b/tools/ui/src/lib/components/app/models/discover/ModelsDiscoverDetails/ModelsDiscoverDetailsDownloadOptions/index.ts
@@ -0,0 +1,40 @@
+/**
+ *
+ * MODELS DISCOVER - DETAILS - DOWNLOAD OPTIONS
+ *
+ * The download area of the detail pane: GGUF files grouped by bit depth, each an
+ * independent download action chip, plus the standalone terminal command preview.
+ *
+ */
+
+/**
+ * **ModelsDiscoverDetailsDownloadOptions** - GGUF download options
+ *
+ * Groups GGUF files by bit depth and renders one independent download
+ * action chip per file, plus the standalone terminal command preview.
+ */
+export { default as ModelsDiscoverDetailsDownloadOptions } from './ModelsDiscoverDetailsDownloadOptions.svelte';
+
+/**
+ * **ModelsDiscoverDetailsDownloadOptionsRow** - One bit-depth group of quants
+ *
+ * A single bit-depth row inside ModelsDiscoverDetailsDownloadOptions: the
+ * depth label with its memory hint and the quant chips of that depth.
+ */
+export { default as ModelsDiscoverDetailsDownloadOptionsRow } from './ModelsDiscoverDetailsDownloadOptionsRow.svelte';
+
+/**
+ * **ModelsDiscoverDetailsDownloadOptionsQuantDownloadButton** - One quant chip
+ *
+ * A single GGUF file as an independent action chip: download / retry when
+ * idle, pause / resume / cancel while in flight, delete when downloaded.
+ */
+export { default as ModelsDiscoverDetailsDownloadOptionsQuantDownloadButton } from './ModelsDiscoverDetailsDownloadOptionsQuantDownloadButton.svelte';
+
+/**
+ * **ModelsDiscoverDetailsDownloadOptionsDownloadCommand** - Terminal command
+ *
+ * The `llama serve -hf ...` command box with inline quant selects and a copy
+ * button; owns its picks, nothing two-way binds them to the quant chips.
+ */
+export { default as ModelsDiscoverDetailsDownloadOptionsDownloadCommand } from './ModelsDiscoverDetailsDownloadOptionsDownloadCommand.svelte';
diff --git a/tools/ui/src/lib/components/app/models/discover/ModelsDiscoverDetails/ModelsDiscoverDetailsHeader.svelte b/tools/ui/src/lib/components/app/models/discover/ModelsDiscoverDetails/ModelsDiscoverDetailsHeader.svelte
new file mode 100644
index 0000000000..0536ac975a
--- /dev/null
+++ b/tools/ui/src/lib/components/app/models/discover/ModelsDiscoverDetails/ModelsDiscoverDetailsHeader.svelte
@@ -0,0 +1,93 @@
+
+
+
+
diff --git a/tools/ui/src/lib/components/app/models/discover/ModelsDiscoverList/index.ts b/tools/ui/src/lib/components/app/models/discover/ModelsDiscoverList/index.ts
new file mode 100644
index 0000000000..d888343a15
--- /dev/null
+++ b/tools/ui/src/lib/components/app/models/discover/ModelsDiscoverList/index.ts
@@ -0,0 +1,38 @@
+/**
+ *
+ * MODELS DISCOVER - LIST
+ *
+ * The discover sidebar column: a debounced search field above a navigable list of
+ * HuggingFace GGUF models, with skeleton rows while loading.
+ *
+ */
+
+/**
+ * **ModelsDiscoverList** - Sidebar model list
+ *
+ * Renders the discover model list as a navigable column. Each row links to the
+ * model's detail and highlights the active one.
+ */
+export { default as ModelsDiscoverList } from './ModelsDiscoverList.svelte';
+
+/**
+ * **ModelsDiscoverListSearch** - Sidebar search input
+ *
+ * Debounced search field for the model list.
+ */
+export { default as ModelsDiscoverListSearch } from './ModelsDiscoverListSearch.svelte';
+
+/**
+ * **ModelsDiscoverListItem** - Single sidebar row
+ *
+ * One model entry in the discover sidebar list, selectable via `onSelect`.
+ */
+export { default as ModelsDiscoverListItem } from './ModelsDiscoverListItem.svelte';
+
+/**
+ * **ModelsDiscoverListItemSkeleton** - Skeleton sidebar row
+ *
+ * Pulsing placeholder matching a ModelsDiscoverListItem row, shown while the
+ * list is loading.
+ */
+export { default as ModelsDiscoverListItemSkeleton } from './ModelsDiscoverListItemSkeleton.svelte';
diff --git a/tools/ui/src/lib/components/app/models/discover/index.ts b/tools/ui/src/lib/components/app/models/discover/index.ts
new file mode 100644
index 0000000000..5926b75153
--- /dev/null
+++ b/tools/ui/src/lib/components/app/models/discover/index.ts
@@ -0,0 +1,39 @@
+/**
+ *
+ * MODELS DISCOVER
+ *
+ * Components for the Models Discover view: a sidebar search + list of
+ * HuggingFace GGUF models and a detail view for the selected model, used as the
+ * body of the discovery dialog. The list and detail trees live in their own
+ * subfolders; this barrel re-exports them alongside the shared leaves.
+ *
+ */
+
+/**
+ * **ModelsDiscover** - Models discover explorer
+ *
+ * The complete discovery layout: a sidebar search + model list on the left and a
+ * detail view for the selected model on the right. Used as the body of the
+ * discovery dialog.
+ */
+export { default as ModelsDiscover } from './ModelsDiscover.svelte';
+
+/**
+ * **ModelsDiscoverAvatar** - Org avatar for a model row
+ *
+ * Shows the org's avatar image, falling back to a monogram on a stable hue
+ * derived from the org name when the image fails to load. Shared by the list,
+ * the detail header and the model selector rows.
+ */
+export { default as ModelsDiscoverAvatar } from './ModelsDiscoverAvatar.svelte';
+
+/**
+ * **ModelsDiscoverDownloadProgressBar** - Thin download progress bar
+ *
+ * Normalizes bytes to a 0..100% bar; can pin to the bottom edge as an overlay.
+ * Shared by the quant chips and the model selector's download rows.
+ */
+export { default as ModelsDiscoverDownloadProgressBar } from './ModelsDiscoverDownloadProgressBar.svelte';
+
+export * from './ModelsDiscoverList';
+export * from './ModelsDiscoverDetails';
diff --git a/tools/ui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigation.svelte b/tools/ui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigation.svelte
index 8bf12279e3..375461cc57 100644
--- a/tools/ui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigation.svelte
+++ b/tools/ui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigation.svelte
@@ -5,6 +5,7 @@
import {
ActionIcon,
DialogConversationRename,
+ DialogModelsDiscover,
DialogSettingsChat,
Logo,
SidebarNavigationActions,
@@ -93,6 +94,7 @@
let renameDialogOpen = $state(false);
let settingsDialogOpen = $state(false);
+ let modelsDiscoverOpen = $state(false);
let renameTargetConversationId = $state(null);
let renameDraft = $state('');
let renameOriginalTitle = $state('');
@@ -389,6 +391,7 @@
bind:searchQuery
class="px-2"
isExpandedMode={innerWidth > 768 ? uiStore.isSidebarExpanded : true}
+ onDiscoverModelsClick={() => (modelsDiscoverOpen = true)}
onNewChat={() => {
if (deviceStore.isMobile) {
scheduleMobileCollapse();
@@ -452,6 +455,8 @@
+
+