diff --git a/tools/ui/src/lib/components/app/dialogs/DialogModelsDownloadManager.svelte b/tools/ui/src/lib/components/app/dialogs/DialogModelsDownloadManager.svelte new file mode 100644 index 0000000000..986f50d69e --- /dev/null +++ b/tools/ui/src/lib/components/app/dialogs/DialogModelsDownloadManager.svelte @@ -0,0 +1,28 @@ + + + + + + Download manager + + + + + diff --git a/tools/ui/src/lib/components/app/dialogs/index.ts b/tools/ui/src/lib/components/app/dialogs/index.ts index e3963ba499..895a13199e 100644 --- a/tools/ui/src/lib/components/app/dialogs/index.ts +++ b/tools/ui/src/lib/components/app/dialogs/index.ts @@ -536,3 +536,13 @@ export { default as DialogMermaidPreview } from './DialogMermaidPreview.svelte'; * @see ModelsDiscover in $lib/components/app/models/discover */ export { default as DialogModelsDiscover } from './DialogModelsDiscover.svelte'; + +/** + * **DialogModelsDownloadManager** - download manager dialog. + * + * Lists all tracked model downloads with per-file progress, cancel and + * delete actions. + * + * @see ModelsDownloadManager in $lib/components/app/models/download-manager + */ +export { default as DialogModelsDownloadManager } from './DialogModelsDownloadManager.svelte'; diff --git a/tools/ui/src/lib/components/app/models/discover/ModelsDiscover.svelte b/tools/ui/src/lib/components/app/models/discover/ModelsDiscover.svelte index b75b02d585..16db298d77 100644 --- a/tools/ui/src/lib/components/app/models/discover/ModelsDiscover.svelte +++ b/tools/ui/src/lib/components/app/models/discover/ModelsDiscover.svelte @@ -1,7 +1,7 @@ + +
+ handleInput(v)} {placeholder} /> +
diff --git a/tools/ui/src/lib/components/app/models/discover/ModelsDiscoverModelDetails.svelte b/tools/ui/src/lib/components/app/models/discover/ModelsDiscoverModelDetails.svelte index ea23111d28..6e3d8370d4 100644 --- a/tools/ui/src/lib/components/app/models/discover/ModelsDiscoverModelDetails.svelte +++ b/tools/ui/src/lib/components/app/models/discover/ModelsDiscoverModelDetails.svelte @@ -1,6 +1,6 @@
- {#each boxes as box (box.title)} +
+ Quant + + + + {#if sidecars.length} + Draft + + + {/if} +
+ + {#each boxes as box, i (box.title)}
{box.title}
-
- {#each box.commands as cmd, i (cmd)} -
- {cmd} +
+ {box.command} - -
- {/each} +
{/each} diff --git a/tools/ui/src/lib/components/app/models/discover/ModelsDiscoverModelDetailsHeader.svelte b/tools/ui/src/lib/components/app/models/discover/ModelsDiscoverModelDetailsHeader.svelte index 62c00e5b22..da21d873bd 100644 --- a/tools/ui/src/lib/components/app/models/discover/ModelsDiscoverModelDetailsHeader.svelte +++ b/tools/ui/src/lib/components/app/models/discover/ModelsDiscoverModelDetailsHeader.svelte @@ -1,21 +1,11 @@
@@ -148,70 +131,5 @@ {/if}
- {#if description} -

{description}

- {/if} - - -
- {#if gguf?.total} - - Model size - - {formatParameters(gguf.total)} params - - {/if} - - {#if gguf?.context_length} - - Context - - {gguf.context_length.toLocaleString()} - - {/if} - - {#if gguf?.architecture} - - Architecture - - {gguf.architecture} - - {/if} - - {#if gguf?.chat_template} - - {/if} - - {#if licenseTag} - - License - - {licenseTag} - - - - {/if} - - {#if details.gated === true} - - gated - - {/if} -
+ - -{#if gguf?.chat_template} - -{/if} diff --git a/tools/ui/src/lib/components/app/models/discover/ModelsDiscoverModelDetailsMetadata.svelte b/tools/ui/src/lib/components/app/models/discover/ModelsDiscoverModelDetailsMetadata.svelte new file mode 100644 index 0000000000..78eeba6517 --- /dev/null +++ b/tools/ui/src/lib/components/app/models/discover/ModelsDiscoverModelDetailsMetadata.svelte @@ -0,0 +1,89 @@ + + +{#if description} +

{description}

+{/if} + + +
+ {#if gguf?.total} + + Model size + + {formatParameters(gguf.total)} params + + {/if} + + {#if gguf?.context_length} + + Context + + {gguf.context_length.toLocaleString()} + + {/if} + + {#if gguf?.architecture} + + Architecture + + {gguf.architecture} + + {/if} + + {#if gguf?.chat_template} + + {/if} + + {#if licenseTag} + + License + + {licenseTag} + + {/if} + + {#if details.gated === true} + + gated + + {/if} +
+ +{#if gguf?.chat_template} + +{/if} diff --git a/tools/ui/src/lib/components/app/models/discover/index.ts b/tools/ui/src/lib/components/app/models/discover/index.ts index dd2376ed81..a37c5b0eda 100644 --- a/tools/ui/src/lib/components/app/models/discover/index.ts +++ b/tools/ui/src/lib/components/app/models/discover/index.ts @@ -24,6 +24,13 @@ export { default as ModelsDiscover } from './ModelsDiscover.svelte'; */ 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'; + /** * **ModelsDiscoverItem** - Single sidebar row * diff --git a/tools/ui/src/lib/components/app/models/download-manager/ModelsDownloadManager.svelte b/tools/ui/src/lib/components/app/models/download-manager/ModelsDownloadManager.svelte new file mode 100644 index 0000000000..84762f60c9 --- /dev/null +++ b/tools/ui/src/lib/components/app/models/download-manager/ModelsDownloadManager.svelte @@ -0,0 +1,51 @@ + + +{#if open} +
+ {#each modelsStore.status.downloadEntries() as entry (entry.repoWithTag)} +
+
+ {entry.repoWithTag} + + +
+ + {#each Object.entries(entry.progress.files) as [file, fileProgress] (file)} +
+
+ {file} + + + {fileProgress.total > 0 + ? Math.round((fileProgress.done / fileProgress.total) * 100) + : 0}% + +
+ + +
+ {/each} +
+ {/each} +
+{/if} diff --git a/tools/ui/src/lib/components/app/models/download-manager/ModelsDownloadManagerDownloadStatusToast.svelte b/tools/ui/src/lib/components/app/models/download-manager/ModelsDownloadManagerDownloadStatusToast.svelte new file mode 100644 index 0000000000..9f36824748 --- /dev/null +++ b/tools/ui/src/lib/components/app/models/download-manager/ModelsDownloadManagerDownloadStatusToast.svelte @@ -0,0 +1,65 @@ + + +
+
+ {repoId} + + +
+ +
+ {#each files as [file, fileProgress] (file)} +
+
+ {file} + + + {percent(fileProgress.done, fileProgress.total)}% + +
+ + +
+ {/each} +
+ + {#if onOpenManager} + + {/if} +
diff --git a/tools/ui/src/lib/components/app/models/index.ts b/tools/ui/src/lib/components/app/models/index.ts index 3ac6ecb678..279e10fc3d 100644 --- a/tools/ui/src/lib/components/app/models/index.ts +++ b/tools/ui/src/lib/components/app/models/index.ts @@ -109,3 +109,18 @@ export { default as ModelBadge } from './ModelBadge.svelte'; * Respects the user's `showRawModelNames` setting. */ export { default as ModelId } from './ModelId.svelte'; + +/** + * **ModelsDownloadManager** - tracked downloads list + * + * Lists every in-flight download with per-file progress and a delete action. + */ +export { default as ModelsDownloadManager } from './download-manager/ModelsDownloadManager.svelte'; + +/** + * **ModelsDownloadManagerDownloadStatusToast** - per-entry download toast + * + * One toast per download with a progress bar per file (main + sidecars) and + * a CTA to open the download manager. + */ +export { default as ModelsDownloadManagerDownloadStatusToast } from './download-manager/ModelsDownloadManagerDownloadStatusToast.svelte'; diff --git a/tools/ui/src/lib/stores/models/status.svelte.ts b/tools/ui/src/lib/stores/models/status.svelte.ts index 15a0c3b87e..fe78f761d5 100644 --- a/tools/ui/src/lib/stores/models/status.svelte.ts +++ b/tools/ui/src/lib/stores/models/status.svelte.ts @@ -124,6 +124,16 @@ export class ModelStatusManager { constructor(private host: ModelStatusHost) {} + /** + * All tracked downloads (in flight), as a list for the download manager. + */ + downloadEntries(): { progress: ModelDownloadProgress; repoWithTag: string }[] { + return Array.from(this.downloadProgress, ([repoWithTag, progress]) => ({ + progress, + repoWithTag + })); + } + /** * Trigger a model download from HuggingFace via POST /models * (ggml-org/llama.cpp#23976). The download runs in the background on the @@ -332,7 +342,11 @@ export class ModelStatusManager { total += file?.total ?? 0; } - this.downloadProgress.set(event.model, { downloadedBytes: downloaded, totalBytes: total }); + this.downloadProgress.set(event.model, { + downloadedBytes: downloaded, + files: progress, + totalBytes: total + }); } /** diff --git a/tools/ui/src/lib/types/index.ts b/tools/ui/src/lib/types/index.ts index 208d10fd30..cd9b94047e 100644 --- a/tools/ui/src/lib/types/index.ts +++ b/tools/ui/src/lib/types/index.ts @@ -109,6 +109,7 @@ export type { ModelCapabilities, ModelModalities, ModelOption, + ModelDownloadFileProgress, ModelDownloadProgress, ModelLoadProgress, ModalityCapabilities diff --git a/tools/ui/src/lib/types/models.d.ts b/tools/ui/src/lib/types/models.d.ts index 962285f4b0..485c5b0aee 100644 --- a/tools/ui/src/lib/types/models.d.ts +++ b/tools/ui/src/lib/types/models.d.ts @@ -40,19 +40,20 @@ export interface ModelLoadProgress { * Per-byte download progress for one in-flight model download, driven by the * /models/sse feed. Lives only while a download runs. */ -export interface ModelDownloadProgress { - downloadedBytes: number; - totalBytes: number; +export interface ModelDownloadFileProgress { + /** Bytes downloaded for this file so far. */ + done: number; + /** Total bytes of the file. */ + total: number; } - -/** - * Per-byte download progress for one in-flight model download, driven by the - * /models/sse feed. Lives only while a download runs. - */ export interface ModelDownloadProgress { + /** Summed progress across all files of the download plan. */ downloadedBytes: number; + /** Summed plan size across all files. */ totalBytes: number; + /** Per-file progress keyed by file URL, as reported by the feed. */ + files: Record; } // LLAMA-APP-REUSE: parsed model id shape diff --git a/tools/ui/tests/stories/ModelsDiscoverModelDetails.stories.svelte b/tools/ui/tests/stories/ModelsDiscoverModelDetails.stories.svelte index 1ef5f8cbdd..c228349c47 100644 --- a/tools/ui/tests/stories/ModelsDiscoverModelDetails.stories.svelte +++ b/tools/ui/tests/stories/ModelsDiscoverModelDetails.stories.svelte @@ -74,7 +74,10 @@
- +
@@ -82,6 +85,7 @@