diff --git a/tools/ui/src/app.d.ts b/tools/ui/src/app.d.ts index 5309dce8f4..48fd334139 100644 --- a/tools/ui/src/app.d.ts +++ b/tools/ui/src/app.d.ts @@ -20,11 +20,14 @@ import type { ApiModelDataEntry, ApiModelListResponse, ApiModelLoadStage, + ApiModelsDownloadProgressData, ApiModelsSseData, ApiModelsSseEvent, ApiModelsSseProgress, ApiProcessingState, ApiRouterModelMeta, + ApiRouterModelsDownloadRequest, + ApiRouterModelsDownloadResponse, ApiRouterModelsListResponse, ApiRouterModelsLoadRequest, ApiRouterModelsLoadResponse, @@ -52,6 +55,7 @@ import type { DatabaseMessageExtraVideoFile, ExportedConversation, ExportedConversations, + ModelDownloadProgress, ModelLoadProgress, // Model types ModelModalities, @@ -89,14 +93,17 @@ declare global { ApiModelsSseProgress, ApiModelsSseData, ApiModelsSseEvent, + ApiModelsDownloadProgressData, ApiModelListResponse, ApiProcessingState, ApiRouterModelMeta, + ApiRouterModelsDownloadRequest, + ApiRouterModelsDownloadResponse, + ApiRouterModelsListResponse, ApiRouterModelsLoadRequest, ApiRouterModelsLoadResponse, ApiRouterModelsStatusRequest, ApiRouterModelsStatusResponse, - ApiRouterModelsListResponse, ApiRouterModelsUnloadRequest, ApiRouterModelsUnloadResponse, // Chat types @@ -127,6 +134,7 @@ declare global { ModelModalities, ModelOption, ModelLoadProgress, + ModelDownloadProgress, // Settings types SettingsChatServiceOptions, SettingsConfigValue, diff --git a/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsListItem/ChatAttachmentsListItemMcpResource.svelte b/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsListItem/ChatAttachmentsListItemMcpResource.svelte index 80ef25bbcb..e72d1442e2 100644 --- a/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsListItem/ChatAttachmentsListItemMcpResource.svelte +++ b/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsListItem/ChatAttachmentsListItemMcpResource.svelte @@ -1,5 +1,5 @@ + + + + + Discover Models + + +
+ + +
+ {#if selectedId} + + {/if} +
+
+
+
diff --git a/tools/ui/src/lib/components/app/dialogs/index.ts b/tools/ui/src/lib/components/app/dialogs/index.ts index 1ffa7a2b9e..b23c8c8bdc 100644 --- a/tools/ui/src/lib/components/app/dialogs/index.ts +++ b/tools/ui/src/lib/components/app/dialogs/index.ts @@ -26,6 +26,16 @@ export { default as DialogMcpServerAddNew } from './DialogMcpServerAddNew.svelte */ export { default as DialogMcpServers } from './DialogMcpServers.svelte'; +/** + * **DialogModelsHub** - Models Hub discovery dialog + * + * Two-pane HuggingFace GGUF browser in a modal dialog: a sidebar model list + * (ModelsHubList) and a detail view (ModelsHubModelDetails). Always opens the + * first model. Used for discovery and downloading; the `/models-hub` route is + * reserved for model management. + */ +export { default as DialogModelsHub } from './DialogModelsHub.svelte'; + /** * **DialogSettingsChat** - Chat settings shown in a modal dialog * diff --git a/tools/ui/src/lib/components/app/index.ts b/tools/ui/src/lib/components/app/index.ts index 4914c743a5..74d1a4d156 100644 --- a/tools/ui/src/lib/components/app/index.ts +++ b/tools/ui/src/lib/components/app/index.ts @@ -8,5 +8,6 @@ export * from './mcp'; export * from './misc'; export * from './settings'; export * from './models'; +export * from './models-hub'; export * from './navigation'; export * from './server'; diff --git a/tools/ui/src/lib/components/app/mcp/McpResourcePreview.svelte b/tools/ui/src/lib/components/app/mcp/McpResourcePreview.svelte index eb84b2ab69..acad3f0662 100644 --- a/tools/ui/src/lib/components/app/mcp/McpResourcePreview.svelte +++ b/tools/ui/src/lib/components/app/mcp/McpResourcePreview.svelte @@ -1,5 +1,5 @@ + + + + + + + {#if phase === 'pending'} + Download this model? + {:else} + Downloading {tagDisplay} + {/if} + + + {#if phase === 'pending'} + llama-server will download this file (and related sidecar weights such as multimodal + projectors or draft models) from Hugging Face into your local model cache. + {:else} + Download runs in the background; this dialog tracks live progress. + {/if} + + + {#if previousFailure && phase === 'pending'} +
+ + + A previous attempt for this tag failed and left partial files on disk. The server will + reject a fresh download until those files are removed. The Retry button below deletes + the partial files automatically. + +
+ {/if} +
+ + {#if canDelete} +
+ +
+ {/if} + +
+
+ Request + POST /models · {`{ model: "${hfRepoWithTag}" }`} +
+
+ File + {filePath} +
+
+ + {tagDisplay} + + {#if formattedSize} + {formattedSize} + {/if} + {#if variant} + + {variant} + + {/if} +
+ + {#if phase === 'downloading' || phase === 'finished'} +
+
+ + {#if phase === 'finished'} + Complete + {:else if progress && progress.totalBytes > 0} + Downloading + {:else} + Preparing download + {/if} + + {progressPercent}% +
+ +
+ {/if} +
+ + {#if lastError} +

{lastError}

+ {/if} + {#if lastCancelError} +

{lastCancelError}

+ {/if} + + + {#if phase === 'downloading'} + + {#if cancelling} + + Cancelling... + {:else} + Cancel download + {/if} + + {:else} + + {#if phase === 'finished'}Close{:else}Cancel{/if} + + {/if} + {#if phase === 'pending'} + + + {previousFailure ? 'Retry download' : 'Download'} + + {:else if phase === 'starting'} + + + Starting... + + {/if} + +
+
+ + (showDeleteConfirm = false)} +/> diff --git a/tools/ui/src/lib/components/app/models-hub/DownloadProgressBar.svelte b/tools/ui/src/lib/components/app/models-hub/DownloadProgressBar.svelte new file mode 100644 index 0000000000..5b4a9eeac9 --- /dev/null +++ b/tools/ui/src/lib/components/app/models-hub/DownloadProgressBar.svelte @@ -0,0 +1,35 @@ + + +{#if overlay} +
+
+
+{:else} +
+
+
+{/if} diff --git a/tools/ui/src/lib/components/app/models-hub/ModelsHubList.svelte b/tools/ui/src/lib/components/app/models-hub/ModelsHubList.svelte new file mode 100644 index 0000000000..4ab5b591e5 --- /dev/null +++ b/tools/ui/src/lib/components/app/models-hub/ModelsHubList.svelte @@ -0,0 +1,20 @@ + + + diff --git a/tools/ui/src/lib/components/app/models-hub/ModelsHubListItem.svelte b/tools/ui/src/lib/components/app/models-hub/ModelsHubListItem.svelte new file mode 100644 index 0000000000..efdde4f02e --- /dev/null +++ b/tools/ui/src/lib/components/app/models-hub/ModelsHubListItem.svelte @@ -0,0 +1,104 @@ + + +
  • + +
  • diff --git a/tools/ui/src/lib/components/app/models-hub/ModelsHubModelDetails.svelte b/tools/ui/src/lib/components/app/models-hub/ModelsHubModelDetails.svelte new file mode 100644 index 0000000000..8a9a0cc19d --- /dev/null +++ b/tools/ui/src/lib/components/app/models-hub/ModelsHubModelDetails.svelte @@ -0,0 +1,374 @@ + + +{#if loading} +
    +

    Loading model...

    +
    +{:else if error} +
    +

    {error}

    +
    +{:else if details} +
    + +
    +
    +
    +

    {details.id}

    + +
    + + + View on HF + +
    + +
    + {#if typeof details.downloads === 'number'} + + + {HuggingFaceService.formatDownloads(details.downloads)} + + {/if} + {#if typeof details.likes === 'number'} + + + {HuggingFaceService.formatLikes(details.likes)} + + {/if} + {#if details.lastModified} + Updated {HuggingFaceService.formatRelativeTime(details.lastModified)} + {/if} +
    + + {#if details.cardData?.description} +

    {details.cardData.description}

    + {/if} + + +
    + {#if gguf?.total} + + {HuggingFaceService.formatFileSize(gguf.total).replace(' B', '')}B params + + {/if} + {#if gguf?.architecture} + + {gguf.architecture.replace(/_/g, ' ')} + + {/if} + {#if gguf?.context_length} + + {gguf.context_length.toLocaleString()} ctx + + {/if} + {#if licenseTag} + + {licenseTag} + + {/if} + {#if details.gated === true} + + gated + + {/if} +
    + + + {#if hasVision || hasTools || hasReasoning} +
    + {#if hasVision} + + + Vision + + {/if} + {#if hasTools} + + + Tool use + + {/if} + {#if hasReasoning} + + + Reasoning + + {/if} +
    + {/if} +
    + + + {#if bitDepthRows.length} +
    +

    + + Download options +

    +
    + {#each bitDepthRows as row (row.bitDepth)} +
    +
    + {#if row.bitDepth === 99} + Other + {:else} + {row.bitDepth}-bit + {/if} +
    +
    + {#each row.files as file (file.path)} + {@const meta = HuggingFaceService.extractQuantMeta(file.path)} + {@const basename = file.path.split('/').pop() ?? file.path} + {@const label = meta?.quant ?? basename.replace(/\.gguf$/i, '')} + {@const tagInput = meta?.quant + ? { quant: meta.quant, variant: meta.variant ?? null } + : null} + {@const hfRepoWithTag = ModelsService.buildDownloadTag(modelId, tagInput)} + {@const progress = modelsStore.status.getDownloadProgress(hfRepoWithTag)} + {@const isDownloading = modelsStore.status.isDownloadInProgress(hfRepoWithTag)} + {@const isDownloaded = meta?.variant + ? modelsStore.status.isDraftDownloaded(modelId, file.path) + : modelsStore.status.isModelDownloaded(hfRepoWithTag)} + {@const isFailed = modelsStore.status.hasFailedDownload(hfRepoWithTag)} + + {/each} +
    +
    + {/each} +
    +
    + {/if} + + + {#if baseModels.length} +
    +

    + Base model +

    + +
    + {/if} + + + {#if readme} +
    +

    README

    +
    + +
    +
    + {/if} +
    +{/if} + +{#if pendingDownload} + pendingDownload !== null, + (v) => { + if (!v) pendingDownload = null; + } + } + repoId={modelId} + filePath={pendingDownload.filePath} + quant={pendingDownload.quant} + variant={pendingDownload.variant} + formattedSize={pendingDownload.sizeBytes != null + ? HuggingFaceService.formatFileSize(pendingDownload.sizeBytes) + : undefined} + onConfirm={() => (pendingDownload = null)} + onCancel={() => (pendingDownload = null)} + /> +{/if} diff --git a/tools/ui/src/lib/components/app/models-hub/index.ts b/tools/ui/src/lib/components/app/models-hub/index.ts new file mode 100644 index 0000000000..5a72ee99ef --- /dev/null +++ b/tools/ui/src/lib/components/app/models-hub/index.ts @@ -0,0 +1,46 @@ +/** + * + * MODELS HUB + * + * Components for the Models Hub route (`/models-hub`): a sidebar list of + * HuggingFace GGUF models and a detail view for the selected model. + * + */ + +/** + * **ModelsHubList** - Sidebar model list + * + * Renders the hub's model list as a navigable column. Each row links to the + * model's detail route and highlights the active one. + */ +export { default as ModelsHubList } from './ModelsHubList.svelte'; + +/** + * **ModelsHubListItem** - Single sidebar row + * + * One model entry in the sidebar list. Links to `/models-hub/[org]/[model]`. + */ +export { default as ModelsHubListItem } from './ModelsHubListItem.svelte'; + +/** + * **ModelsHubModelDetails** - Model detail view + * + * Detail pane for the selected model. Loads its own data (details + GGUF file + * list) from HuggingFaceService based on the `modelId` route param. + */ +export { default as ModelsHubModelDetails } from './ModelsHubModelDetails.svelte'; + +/** + * **DialogModelDownload** - Download confirmation / progress dialog + * + * Confirms a single GGUF download, tracks live progress over the SSE feed and + * offers cancel / delete-&-retry flows. + */ +export { default as DialogModelDownload } from './DialogModelDownload.svelte'; + +/** + * **DownloadProgressBar** - Thin download progress bar + * + * Normalizes bytes to a 0..100% bar; can pin to the bottom edge as an overlay. + */ +export { default as DownloadProgressBar } from './DownloadProgressBar.svelte'; diff --git a/tools/ui/src/lib/components/app/models/ModelId.svelte b/tools/ui/src/lib/components/app/models/ModelId.svelte index ea700580a0..a84083d612 100644 --- a/tools/ui/src/lib/components/app/models/ModelId.svelte +++ b/tools/ui/src/lib/components/app/models/ModelId.svelte @@ -39,6 +39,8 @@ 'inline-flex w-fit shrink-0 items-center justify-center whitespace-nowrap rounded-md border border-border/50 px-1 py-0 text-[10px] font-mono bg-foreground/15 dark:bg-foreground/10 text-foreground [a&]:hover:bg-foreground/25'; const tagBadgeClass = 'inline-flex w-fit shrink-0 items-center justify-center whitespace-nowrap rounded-md border border-border/50 px-1 py-0 text-[10px] font-mono text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground'; + const variantBadgeClass = + 'inline-flex w-fit shrink-0 items-center justify-center whitespace-nowrap rounded-md bg-primary px-1.5 py-0 text-[10px] font-mono font-semibold uppercase tracking-wide text-primary-foreground'; let parsed = $derived(ModelsService.parseModelId(modelId)); let resolvedShowRaw = $derived( @@ -68,6 +70,12 @@ + {#if parsed.variant} + + {parsed.variant} + + {/if} + {#if parsed.params} {parsed.params}{parsed.activatedParams ? `-${parsed.activatedParams}` : ''} diff --git a/tools/ui/src/lib/components/app/models/ModelsSelectorDropdown.svelte b/tools/ui/src/lib/components/app/models/ModelsSelector/ModelsSelectorDropdown.svelte similarity index 93% rename from tools/ui/src/lib/components/app/models/ModelsSelectorDropdown.svelte rename to tools/ui/src/lib/components/app/models/ModelsSelector/ModelsSelectorDropdown.svelte index 30dedcbae9..9320e69a10 100644 --- a/tools/ui/src/lib/components/app/models/ModelsSelectorDropdown.svelte +++ b/tools/ui/src/lib/components/app/models/ModelsSelector/ModelsSelectorDropdown.svelte @@ -1,10 +1,11 @@ + +{#snippet modelRow(option: ModelOption, indent: boolean, hideQuantization: boolean)} + {@const entry = getRouterEntry(option.model)} + {@const status = entry?.status} + {@const statusValue = status?.value} + {@const isLoaded = statusValue === ServerModelStatus.LOADED || statusValue === ServerModelStatus.SLEEPING} + {@const isLoading = statusValue === ServerModelStatus.LOADING} + {@const isDownloading = statusValue === ServerModelStatus.DOWNLOADING} + {@const isFailed = statusValue === ServerModelStatus.FAILED || status?.failed === true} + {@const canRemove = entry?.can_remove === true} + {@const isFav = modelsStore.isFavorite(option.model)} + {@const hfLink = hfUrl(option.model)} + {@const downloadProgress = modelsStore.status.getDownloadProgress(option.model)} + + + + + + + + + {#if isLoading} + + Loading + {:else if isDownloading} + + Downloading + {:else if isLoaded} + + {statusValue === ServerModelStatus.SLEEPING ? 'Sleeping' : 'Loaded'} + {:else if isFailed} + + Failed + {:else if statusValue === ServerModelStatus.DOWNLOADED} + + Downloaded + {:else} + + Unloaded + {/if} + + + {#if isDownloading && downloadProgress} +
    +
    +
    + {/if} + + + +
    + {#if isRouter} + {#if isLoading} + modelsStore.status.cancelLoad(option.model)} + /> + {:else if isDownloading} + modelsStore.status.cancelDownload(option.model)} + /> + {:else if isLoaded} + modelsStore.status.unload(option.model)} + /> + {:else} + modelsStore.status.load(option.model)} + /> + {/if} + + {#if canRemove && !isDownloading} + modelsStore.status.cancelDownload(option.model)} + /> + {/if} + {/if} + + modelsStore.toggleFavorite(option.model)} + /> + + copyToClipboard(option.model)} + /> + + {#if hfLink} + + + + {/if} +
    + + +{/snippet} + +
    +
    +
    +

    Model Manager

    + +

    + Models installed on this machine and available through /v1/models. +

    +
    + + +
    + + {#if modelsStore.loading} +
    +

    Loading models...

    +
    + {:else if modelsStore.error} +
    +

    {modelsStore.error}

    +
    + {:else if modelsStore.models.length === 0} +
    +

    No models installed.

    + + +
    + {:else} +
    + + + + + + + + + + + {#each tree as parent (parent.parentId)} + + + + + {#each parent.quantOrgs as org (org.repoId)} + + + + + {#each org.quants as quant (quant.quant ?? 'default')} + {@render modelRow(quant.main, false, false)} + + {#each quant.drafts as draft (draft.option.id)} + {@render modelRow(draft.option, true, true)} + {/each} + {/each} + {/each} + {/each} + +
    ModelStatusActions
    + {parent.parentId} +
    + {org.repoId} +
    +
    + {/if} +
    + + diff --git a/tools/ui/src/routes/temp/models-hub/Breadcrumb.svelte b/tools/ui/src/routes/temp/models-hub/Breadcrumb.svelte new file mode 100644 index 0000000000..b67c7a8bf0 --- /dev/null +++ b/tools/ui/src/routes/temp/models-hub/Breadcrumb.svelte @@ -0,0 +1,50 @@ + + + diff --git a/tools/ui/src/routes/temp/models-hub/DialogModelDownload.svelte b/tools/ui/src/routes/temp/models-hub/DialogModelDownload.svelte new file mode 100644 index 0000000000..fed5f88b1a --- /dev/null +++ b/tools/ui/src/routes/temp/models-hub/DialogModelDownload.svelte @@ -0,0 +1,316 @@ + + + + + + + + {#if phase === 'pending'} + Download this model? + {:else} + Downloading {tagDisplay} + {/if} + + + {#if phase === 'pending'} + llama-server will download this file (and related sidecar weights such as multimodal + projectors or draft models) from Hugging Face into your local model cache. + {:else} + Download runs in the background; this dialog tracks live progress. + {/if} + + + {#if previousFailure && phase === 'pending'} +
    + + + A previous attempt for this tag failed and left partial files on disk. The server will + reject a fresh download until those files are removed. The Retry button below deletes + the partial files automatically. + +
    + {/if} +
    + + {#if canDelete} +
    + +
    + {/if} + +
    +
    + Request + POST /models · {`{ model: "${hfRepoWithTag}" }`} +
    +
    + File + {filePath} +
    +
    + + {tagDisplay} + + {#if formattedSize} + {formattedSize} + {/if} + {#if variant} + + {variant} + + {/if} +
    + + {#if phase === 'downloading' || phase === 'finished'} +
    +
    + + {#if phase === 'finished'} + Complete + {:else if progress && progress.totalBytes > 0} + Downloading + {:else} + Preparing download + {/if} + + {progressPercent}% +
    + +
    + {/if} +
    + + {#if lastError} +

    {lastError}

    + {/if} + {#if lastCancelError} +

    {lastCancelError}

    + {/if} + + + {#if phase === 'downloading'} + + {#if cancelling} + + Cancelling... + {:else} + Cancel download + {/if} + + {:else} + + {#if phase === 'finished'}Close{:else}Cancel{/if} + + {/if} + {#if phase === 'pending'} + + + {previousFailure ? 'Retry download' : 'Download'} + + {:else if phase === 'starting'} + + + Starting... + + {/if} + +
    +
    + + (showDeleteConfirm = false)} +/> diff --git a/tools/ui/src/routes/temp/models-hub/DownloadProgressBar.svelte b/tools/ui/src/routes/temp/models-hub/DownloadProgressBar.svelte new file mode 100644 index 0000000000..5b4a9eeac9 --- /dev/null +++ b/tools/ui/src/routes/temp/models-hub/DownloadProgressBar.svelte @@ -0,0 +1,35 @@ + + +{#if overlay} +
    +
    +
    +{:else} +
    +
    +
    +{/if} diff --git a/tools/ui/src/routes/temp/models-hub/IconFromName.svelte b/tools/ui/src/routes/temp/models-hub/IconFromName.svelte new file mode 100644 index 0000000000..2428f12d33 --- /dev/null +++ b/tools/ui/src/routes/temp/models-hub/IconFromName.svelte @@ -0,0 +1,60 @@ + + + + + diff --git a/tools/ui/src/routes/temp/models-hub/ModelDetail.svelte b/tools/ui/src/routes/temp/models-hub/ModelDetail.svelte new file mode 100644 index 0000000000..fbef215461 --- /dev/null +++ b/tools/ui/src/routes/temp/models-hub/ModelDetail.svelte @@ -0,0 +1,446 @@ + + +
    + +
    +
    + +
    + +

    {modelId}

    + + + + + + + +
    + +
    + + + {#if error} +
    +

    {error}

    +
    + {/if} + + {#if loading} +
    +

    Loading model details...

    +
    + {/if} + + {#if !loading && modelInfo} + +
    + {#if author} + + {author} + + {/if} + {#if modelInfo.pipeline_tag} + + {HuggingFaceService.pipelineTagLabel(modelInfo.pipeline_tag)} + + {/if} + {#if modelInfo.library_name} + + {modelInfo.library_name} + + {/if} + {#if modelInfo.gated === true} + + gated + + {/if} + {#if licenseTag} + + {licenseTag} + + {/if} +
    + + +
    +
    + {#if description} +
    +

    + About +

    +

    {description}

    +
    + {/if} + + {#if gguf} +
    +

    + GGUF Specs +

    +
    + {#if gguf.architecture} +
    +
    Architecture
    +
    + {gguf.architecture.replace(/_/g, ' ')} +
    +
    + {/if} + {#if gguf.total} +
    +
    Total params
    +
    + {HuggingFaceService.formatFileSize(gguf.total).replace(' B', '')}B +
    +
    + {/if} + {#if gguf.context_length} +
    +
    Context length
    +
    + {gguf.context_length.toLocaleString()} +
    +
    + {/if} +
    +
    + {/if} +
    + + +
    + {/if} +
    +
    + +{#if pendingDownload} + pendingDownload !== null, + (v) => { + if (!v) pendingDownload = null; + } + } + repoId={modelId} + filePath={pendingDownload.filePath} + quant={pendingDownload.quant} + variant={pendingDownload.variant} + formattedSize={pendingDownload.sizeBytes !== null + ? HuggingFaceService.formatFileSize(pendingDownload.sizeBytes) + : ''} + onConfirm={() => (pendingDownload = null)} + onCancel={() => (pendingDownload = null)} + /> +{/if} diff --git a/tools/ui/src/routes/temp/models-hub/ModelsHub.svelte b/tools/ui/src/routes/temp/models-hub/ModelsHub.svelte new file mode 100644 index 0000000000..cc902730c7 --- /dev/null +++ b/tools/ui/src/routes/temp/models-hub/ModelsHub.svelte @@ -0,0 +1,341 @@ + + +
    +
    + +
    +
    +

    Models

    + {#if !loading} + {filteredModels.length} + {/if} +
    + + {#if !isSearching} +
    + + +
    + {/if} + + + + {#if !isSearching} + + {/if} + + {#if !isSearching && availableFilters.length > 0} +
    + + {#each availableFilters as f (f.tag)} + {@const isActive = activeFilter === f.tag} + + {/each} +
    + {/if} +
    + + +
    + {#if error} +
    +

    {error}

    +
    + {/if} + {#if searchError} +
    +

    {searchError}

    +
    + {/if} + {#if loading || searchLoading} +
    +

    + {isSearching ? 'Searching...' : 'Loading models...'} +

    +
    + {:else if filteredModels.length === 0} +
    +

    + {#if isSearching} + No models found matching "{searchQuery}". + {:else} + No models found. + {/if} +

    +
    + {:else} +
    + {#each filteredModels as model (model.id)} + {@const isActive = model.id === selectedModelId} + + {/each} +
    + {/if} +
    +
    + + +
    + {#if selectedModelId} + + {:else} +
    +
    +
    + +
    +

    + Select a model from the list to see its details and download options. +

    +
    +
    + {/if} +
    +
    diff --git a/tools/ui/src/routes/temp/models-hub/[...modelId]/+page.svelte b/tools/ui/src/routes/temp/models-hub/[...modelId]/+page.svelte new file mode 100644 index 0000000000..078ca3603a --- /dev/null +++ b/tools/ui/src/routes/temp/models-hub/[...modelId]/+page.svelte @@ -0,0 +1,12 @@ + + + + {modelId ? `${modelId} · llama.cpp` : 'Models · llama.cpp'} + + +