ui : restructure discover components to the target names

- ModelsDiscoverItem + ModelsDiscoverInfo fold into
  ModelsDiscoverListItem (avatar + model id + badges + context/size)
- ModelsDiscoverDetails* renamed to ModelsDiscoverModelDetails*
- TerminalCommands renamed to ModelsDiscoverModelDetailsCommands
- ModelsDiscoverDetailsName folded into the details header
- ModelsDiscoverListSearch extracted from the list search input
- stories updated for the new names

Assisted-by: pi
This commit is contained in:
Aleksander Grygier
2026-09-01 00:22:58 +02:00
parent c43cec9c26
commit c00a7ccd83
15 changed files with 182 additions and 235 deletions
@@ -1,6 +1,9 @@
<script lang="ts">
import { SearchInput } from '$lib/components/app';
import { ModelsDiscoverDetails, ModelsDiscoverList } from '$lib/components/app/models/discover';
import {
ModelsDiscoverList,
ModelsDiscoverModelDetails
} from '$lib/components/app/models/discover';
import { HuggingFaceService } from '$lib/services';
import { modelsHubStore } from '$lib/stores';
import type { HfModelDetailInfo, HfModelSibling } from '$lib/types';
@@ -122,13 +125,13 @@
<main class="overflow-y-auto">
{#if selectedId}
<ModelsDiscoverDetails
details={details}
<ModelsDiscoverModelDetails
{details}
error={detailError}
files={files}
{files}
loading={detailLoading}
modelId={selectedId}
readme={readme}
{readme}
/>
{/if}
</main>
@@ -1,79 +0,0 @@
<script lang="ts">
import { ExternalLink, Image, Lightbulb, Wrench } from '@lucide/svelte';
import * as Tooltip from '$lib/components/ui/tooltip';
import { HuggingFaceService } from '$lib/services';
interface Props {
/** Full HuggingFace model id (quant org + name), e.g. `ggml-org/Qwen3.8-27B-GGUF`. */
modelId: string;
/** Base model ids, shown as small text under the quant name. */
baseModels: string[];
hasVision: boolean;
hasTools: boolean;
hasReasoning: boolean;
}
let { baseModels, hasReasoning, hasTools, hasVision, modelId }: Props = $props();
</script>
<div class="min-w-0">
<div class="flex items-center gap-2">
<h1 class="truncate text-lg font-semibold">{modelId}</h1>
{#if hasVision || hasTools || hasReasoning}
<div class="flex shrink-0 items-center gap-2.5 text-muted-foreground">
{#if hasVision}
<Tooltip.Root>
<Tooltip.Trigger>
<Image class="h-4 w-4" />
</Tooltip.Trigger>
<Tooltip.Content>
<p>Vision</p>
</Tooltip.Content>
</Tooltip.Root>
{/if}
{#if hasTools}
<Tooltip.Root>
<Tooltip.Trigger>
<Wrench class="h-4 w-4" />
</Tooltip.Trigger>
<Tooltip.Content>
<p>Tool use</p>
</Tooltip.Content>
</Tooltip.Root>
{/if}
{#if hasReasoning}
<Tooltip.Root>
<Tooltip.Trigger>
<Lightbulb class="h-4 w-4" />
</Tooltip.Trigger>
<Tooltip.Content>
<p>Reasoning</p>
</Tooltip.Content>
</Tooltip.Root>
{/if}
</div>
{/if}
</div>
{#if baseModels.length}
<div class="flex items-center gap-1">
<span class="truncate text-xs text-muted-foreground">{baseModels.join(', ')}</span>
<a
aria-label="View base model on HuggingFace"
class="shrink-0 text-muted-foreground transition-colors hover:text-foreground"
href={HuggingFaceService.getModelUrl(baseModels[0])}
rel="noopener noreferrer"
target="_blank"
>
<ExternalLink class="h-3 w-3" />
</a>
</div>
{/if}
</div>
@@ -1,43 +0,0 @@
<script lang="ts">
import ModelsDiscoverAvatar from './ModelsDiscoverAvatar.svelte';
import ModelsDiscoverInfo from './ModelsDiscoverInfo.svelte';
import { HuggingFaceService } from '$lib/services';
import type { HfModelInfo } from '$lib/types/huggingface';
interface Props {
model: HfModelInfo;
active?: boolean;
/** Show the original (base) model's org avatar instead of the repo's org. */
showBaseModelAvatar?: boolean;
onSelect?: (modelId: string) => void;
}
let { active = false, model, onSelect, showBaseModelAvatar = false }: Props = $props();
let org = $derived(model.id.split('/')[0] ?? model.id);
// Org whose avatar is shown: the base model's org when showBaseModelAvatar
// (e.g. the Qwen logo for ggml-org/Qwen3.8-27B-GGUF), else the repo's org.
let avatarOrg = $derived.by(() => {
if (!showBaseModelAvatar) return org;
const base = HuggingFaceService.getBaseModels(model)[0];
return base?.split('/')[0] || org;
});
</script>
<li>
<button
aria-current={active ? 'page' : undefined}
class="flex w-full cursor-pointer items-start gap-2.5 rounded-lg p-2.5 text-left transition-colors {active
? 'bg-primary/10 hover:bg-primary/15'
: 'hover:bg-muted/60'}"
onclick={() => onSelect?.(model.id)}
type="button"
>
<ModelsDiscoverAvatar org={avatarOrg} quantOrg={showBaseModelAvatar ? org : undefined} />
<ModelsDiscoverInfo {model} />
</button>
</li>
@@ -1,5 +1,5 @@
<script lang="ts">
import ModelsDiscoverItem from './ModelsDiscoverItem.svelte';
import ModelsDiscoverListItem from './ModelsDiscoverListItem.svelte';
import type { HfModelInfo } from '$lib/types/huggingface';
interface Props {
@@ -15,6 +15,11 @@
<ul class="space-y-0.5 p-2">
{#each models as model (model.id)}
<ModelsDiscoverItem active={model.id === activeId} {model} {onSelect} {showBaseModelAvatar} />
<ModelsDiscoverListItem
active={model.id === activeId}
{model}
{onSelect}
{showBaseModelAvatar}
/>
{/each}
</ul>
@@ -1,5 +1,6 @@
<script lang="ts">
import ModelId from '../ModelId.svelte';
import ModelsDiscoverAvatar from './ModelsDiscoverAvatar.svelte';
import { isAuxSidecar, type ModelSidecar } from '$lib/constants';
import { HuggingFaceService, ModelsService } from '$lib/services';
import { modelsHubStore } from '$lib/stores';
@@ -10,9 +11,25 @@
interface Props {
model: HfModelInfo;
active?: boolean;
/** Show the original (base) model's org avatar instead of the repo's org. */
showBaseModelAvatar?: boolean;
onSelect?: (modelId: string) => void;
}
let { model }: Props = $props();
let { active = false, model, onSelect, showBaseModelAvatar = false }: Props = $props();
let org = $derived(model.id.split('/')[0] ?? model.id);
// Org whose avatar is shown: the base model's org when showBaseModelAvatar
// (e.g. the Qwen logo for ggml-org/Qwen3.8-27B-GGUF), else the repo's org.
let avatarOrg = $derived.by(() => {
if (!showBaseModelAvatar) return org;
const base = HuggingFaceService.getBaseModels(model)[0];
return base?.split('/')[0] || org;
});
let contextLength = $derived(model.gguf?.context_length);
@@ -130,19 +147,32 @@
});
</script>
<span class="min-w-0 flex-1">
<ModelId
class="min-w-0"
{contextLength}
{draftSidecars}
hideOrgName
iconsOnNewLine
{modalities}
modelId={model.id}
params={paramsFallback}
{sizeRange}
{supportsThinking}
{supportsToolUse}
wrap
/>
</span>
<li>
<button
aria-current={active ? 'page' : undefined}
class="flex w-full cursor-pointer items-start gap-2.5 rounded-lg p-2.5 text-left transition-colors {active
? 'bg-primary/10 hover:bg-primary/15'
: 'hover:bg-muted/60'}"
onclick={() => onSelect?.(model.id)}
type="button"
>
<ModelsDiscoverAvatar org={avatarOrg} quantOrg={showBaseModelAvatar ? org : undefined} />
<span class="min-w-0 flex-1">
<ModelId
class="min-w-0"
{contextLength}
{draftSidecars}
hideOrgName
iconsOnNewLine
{modalities}
modelId={model.id}
params={paramsFallback}
{sizeRange}
{supportsThinking}
{supportsToolUse}
wrap
/>
</span>
</button>
</li>
@@ -1,8 +1,8 @@
<script lang="ts">
import ModelsDiscoverDetailsDownloadOptions from './ModelsDiscoverDetailsDownloadOptions.svelte';
import ModelsDiscoverDetailsHeader from './ModelsDiscoverDetailsHeader.svelte';
import ModelsDiscoverDetailsReadme from './ModelsDiscoverDetailsReadme.svelte';
import TerminalCommands from './TerminalCommands.svelte';
import ModelsDiscoverModelDetailsCommands from './ModelsDiscoverModelDetailsCommands.svelte';
import ModelsDiscoverDetailsDownloadOptions from './ModelsDiscoverModelDetailsDownloadOptions.svelte';
import ModelsDiscoverDetailsHeader from './ModelsDiscoverModelDetailsHeader.svelte';
import ModelsDiscoverDetailsReadme from './ModelsDiscoverModelDetailsReadme.svelte';
import { isAuxSidecar, type ModelSidecar } from '$lib/constants';
import { HuggingFaceService } from '$lib/services';
import type { HfModelDetailInfo, HfModelSibling } from '$lib/types/huggingface';
@@ -108,7 +108,7 @@
<ModelsDiscoverDetailsDownloadOptions {bitDepthRows} {modelId} />
<TerminalCommands {modelId} sidecars={draftSidecars} />
<ModelsDiscoverModelDetailsCommands {modelId} sidecars={draftSidecars} />
<ModelsDiscoverDetailsReadme {readme} />
</div>
@@ -1,7 +1,7 @@
<script lang="ts">
import { Check, Copy, Server, SquareTerminal } from '@lucide/svelte';
import { isAuxSidecar, type ModelSidecar } from '$lib/constants';
import { ModelAuxSidecar, ModelDraftSidecar } from '$lib/enums';
import { ModelAuxSidecar, ModelDraftSidecar } from '$lib/enums';
import { copyToClipboard } from '$lib/utils';
interface Props {
@@ -6,7 +6,7 @@
import { isAuxSidecar, type ModelSidecar } from '$lib/constants';
import { HuggingFaceService, ModelsService } from '$lib/services';
import { modelsStore } from '$lib/stores';
import type { ModelDownloadProgress } from '$lib/types';
import type { ModelDownloadProgress } from '$lib/types';
import type { HfModelSibling } from '$lib/types/huggingface';
import { estimateModelMemoryBytes } from '$lib/utils';
@@ -33,7 +33,11 @@ import type { ModelDownloadProgress } from '$lib/types';
/** GGUF files grouped by bit depth. */
bitDepthRows: BitDepthRow[];
/** Download state lookup; defaults to the models store status feed. */
getDownloadState?: (repoWithTag: string, filePath: string, isSidecar: boolean) => DownloadEntryState;
getDownloadState?: (
repoWithTag: string,
filePath: string,
isSidecar: boolean
) => DownloadEntryState;
}
let { bitDepthRows, getDownloadState, modelId }: Props = $props();
@@ -93,13 +97,17 @@ import type { ModelDownloadProgress } from '$lib/types';
{@const meta = HuggingFaceService.extractQuantMeta(file.path)}
{@const basename = file.path.split('/').pop() ?? file.path}
{@const label = meta?.quant ?? basename.replace(/\.gguf$/i, '')}
{@const hfRepoWithTag = ModelsService.buildDownloadTag(modelId, meta?.quant ?? null, meta?.sidecar ?? null)}
{@const hfRepoWithTag = ModelsService.buildDownloadTag(
modelId,
meta?.quant ?? null,
meta?.sidecar ?? null
)}
{@const state = stateFor(hfRepoWithTag, file.path, Boolean(meta?.sidecar))}
{@const isDownloading = state.isDownloading}
{@const progress = state.progress}
{@const isDownloaded = state.isDownloaded}
{@const isFailed = state.isFailed}
{@const memoryGb = Math.ceil(estimateModelMemoryBytes(file.size ?? 0) / (1024 ** 3))}
{@const memoryGb = Math.ceil(estimateModelMemoryBytes(file.size ?? 0) / 1024 ** 3)}
{@const tooltipText = isDownloading
? `Downloading ${file.path}`
: isDownloaded
@@ -1,8 +1,16 @@
<script lang="ts">
import ModelsDiscoverAvatar from './ModelsDiscoverAvatar.svelte';
import ModelsDiscoverChatTemplateDialog from './ModelsDiscoverChatTemplateDialog.svelte';
import ModelsDiscoverDetailsName from './ModelsDiscoverDetailsName.svelte';
import { Download, ExternalLink, Heart, MessageSquareCode } from '@lucide/svelte';
import {
Download,
ExternalLink,
Heart,
Image,
Lightbulb,
MessageSquareCode,
Wrench
} from '@lucide/svelte';
import * as Tooltip from '$lib/components/ui/tooltip';
import { ICON_CLASS_SM } from '$lib/constants';
import { HuggingFaceService } from '$lib/services';
import { modelsHubStore } from '$lib/stores';
@@ -43,13 +51,67 @@
<div class="flex min-w-0 items-center gap-2">
<ModelsDiscoverAvatar org={avatarOrg} {quantOrg} />
<ModelsDiscoverDetailsName
{baseModels}
{hasReasoning}
{hasTools}
{hasVision}
modelId={details.id ?? modelId}
/>
<div class="min-w-0">
<div class="flex items-center gap-2">
<h1 class="truncate text-lg font-semibold">{details.id ?? modelId}</h1>
{#if hasVision || hasTools || hasReasoning}
<div class="flex shrink-0 items-center gap-2.5 text-muted-foreground">
{#if hasVision}
<Tooltip.Root>
<Tooltip.Trigger>
<Image class="h-4 w-4" />
</Tooltip.Trigger>
<Tooltip.Content>
<p>Vision</p>
</Tooltip.Content>
</Tooltip.Root>
{/if}
{#if hasTools}
<Tooltip.Root>
<Tooltip.Trigger>
<Wrench class="h-4 w-4" />
</Tooltip.Trigger>
<Tooltip.Content>
<p>Tool use</p>
</Tooltip.Content>
</Tooltip.Root>
{/if}
{#if hasReasoning}
<Tooltip.Root>
<Tooltip.Trigger>
<Lightbulb class="h-4 w-4" />
</Tooltip.Trigger>
<Tooltip.Content>
<p>Reasoning</p>
</Tooltip.Content>
</Tooltip.Root>
{/if}
</div>
{/if}
</div>
{#if baseModels.length}
<div class="flex items-center gap-1">
<span class="truncate text-xs text-muted-foreground">{baseModels.join(', ')}</span>
<a
aria-label="View base model on HuggingFace"
class="shrink-0 text-muted-foreground transition-colors hover:text-foreground"
href={HuggingFaceService.getModelUrl(baseModels[0])}
rel="noopener noreferrer"
target="_blank"
>
<ExternalLink class="h-3 w-3" />
</a>
</div>
{/if}
</div>
</div>
<a
@@ -29,7 +29,7 @@ export { default as ModelsDiscoverList } from './ModelsDiscoverList.svelte';
*
* One model entry in the sidebar list. Links to `/models-hub/[org]/[model]`.
*/
export { default as ModelsDiscoverItem } from './ModelsDiscoverItem.svelte';
export { default as ModelsDiscoverListItem } from './ModelsDiscoverListItem.svelte';
/**
* **ModelsDiscoverAvatar** - Org avatar for a model row
@@ -39,21 +39,13 @@ export { default as ModelsDiscoverItem } from './ModelsDiscoverItem.svelte';
*/
export { default as ModelsDiscoverAvatar } from './ModelsDiscoverAvatar.svelte';
/**
* **ModelsDiscoverInfo** - Model name + metadata for a row
*
* Renders the model name via ModelId and the remaining metadata (org, last
* modified, params, downloads, likes, vision) below it.
*/
export { default as ModelsDiscoverInfo } from './ModelsDiscoverInfo.svelte';
/**
* **ModelsDiscoverDetails** - 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 ModelsDiscoverDetails } from './ModelsDiscoverDetails.svelte';
export { default as ModelsDiscoverModelDetails } from './ModelsDiscoverModelDetails.svelte';
/**
* **ModelsDiscoverDetailsHeader** - Detail view header
@@ -61,16 +53,7 @@ export { default as ModelsDiscoverDetails } from './ModelsDiscoverDetails.svelte
* Shows the model avatar (base org + quant org corner badge), name, base model
* info, stats, metadata chips and capability badges.
*/
export { default as ModelsDiscoverDetailsHeader } from './ModelsDiscoverDetailsHeader.svelte';
/**
* **ModelsDiscoverDetailsName** - Model name block
*
* Shows the quant model name with capability icons (vision, tool use,
* reasoning) beside it, and the base model name with a smaller external-link
* icon on the line below.
*/
export { default as ModelsDiscoverDetailsName } from './ModelsDiscoverDetailsName.svelte';
export { default as ModelsDiscoverModelDetailsHeader } from './ModelsDiscoverModelDetailsHeader.svelte';
/**
* **ModelsDiscoverDetailsDownloadOptions** - GGUF download options
@@ -78,7 +61,7 @@ export { default as ModelsDiscoverDetailsName } from './ModelsDiscoverDetailsNam
* Groups GGUF files by bit depth and renders per-file download buttons with
* progress, owned by the download confirmation dialog.
*/
export { default as ModelsDiscoverDetailsDownloadOptions } from './ModelsDiscoverDetailsDownloadOptions.svelte';
export { default as ModelsDiscoverModelDetailsDownloadOptions } from './ModelsDiscoverModelDetailsDownloadOptions.svelte';
/**
* **ModelsDiscoverChatTemplateDialog** - Chat template viewer
@@ -92,14 +75,14 @@ export { default as ModelsDiscoverChatTemplateDialog } from './ModelsDiscoverCha
*
* Renders the model card README as markdown.
*/
export { default as ModelsDiscoverDetailsReadme } from './ModelsDiscoverDetailsReadme.svelte';
export { default as ModelsDiscoverModelDetailsReadme } from './ModelsDiscoverModelDetailsReadme.svelte';
/**
* **TerminalCommands** - Terminal command block
*
* Shows the `llama serve` / `llama cli` commands for a model with copy buttons.
*/
export { default as TerminalCommands } from './TerminalCommands.svelte';
export { default as ModelsDiscoverModelDetailsCommands } from './ModelsDiscoverModelDetailsCommands.svelte';
/**
* **DialogModelDownload** - Download confirmation / progress dialog
@@ -6,7 +6,7 @@
* a model, so `firstModel` drives the initial selection. By default the list
* shows a curated set of official ggml-org GGUF models in a fixed display order;
* search replaces the list with matching models across all of HuggingFace.
* Detail data is loaded by ModelsDiscoverDetails, not here.
* Detail data is loaded by ModelsDiscoverModelDetails, not here.
*/
import { HuggingFaceService } from '$lib/services';
@@ -23,11 +23,8 @@
// manager keeps this in a private SvelteSet keyed by `<repo>:<tag>`.
const FAILED_TAG = 'ggml-org/gemma-4-12b-it-GGUF:Q4_K_M';
(
modelsStore.status as unknown as { failedDownloads: Set<string> }
).failedDownloads = new SvelteSet([FAILED_TAG]);
(modelsStore.status as unknown as { failedDownloads: Set<string> }).failedDownloads =
new SvelteSet([FAILED_TAG]);
</script>
<Story name="Progress bar">
@@ -2,9 +2,8 @@
import { mockGemma, mockListModels, mockQwen } from './fixtures/models-discover';
import { defineMeta } from '@storybook/addon-svelte-csf';
import ModelsDiscoverAvatar from '$lib/components/app/models/discover/ModelsDiscoverAvatar.svelte';
import ModelsDiscoverInfo from '$lib/components/app/models/discover/ModelsDiscoverInfo.svelte';
import ModelsDiscoverItem from '$lib/components/app/models/discover/ModelsDiscoverItem.svelte';
import ModelsDiscoverList from '$lib/components/app/models/discover/ModelsDiscoverList.svelte';
import ModelsDiscoverListItem from '$lib/components/app/models/discover/ModelsDiscoverListItem.svelte';
const { Story } = defineMeta({
tags: ['autodocs'],
@@ -30,17 +29,17 @@
<Story name="Info">
<div class="flex w-80 flex-col gap-2 p-4">
<ModelsDiscoverInfo model={mockGemma} />
<ModelsDiscoverListItem model={mockGemma} />
<ModelsDiscoverInfo model={mockQwen} />
<ModelsDiscoverListItem model={mockQwen} />
</div>
</Story>
<Story name="Item">
<ul class="w-96 p-2">
<ModelsDiscoverItem active model={mockGemma} />
<ModelsDiscoverListItem active model={mockGemma} />
<ModelsDiscoverItem model={mockQwen} />
<ModelsDiscoverListItem model={mockQwen} />
</ul>
</Story>
@@ -2,12 +2,11 @@
import { mockDetails, mockSiblings } from './fixtures/models-discover';
import { defineMeta } from '@storybook/addon-svelte-csf';
import ModelsDiscoverChatTemplateDialog from '$lib/components/app/models/discover/ModelsDiscoverChatTemplateDialog.svelte';
import ModelsDiscoverDetails from '$lib/components/app/models/discover/ModelsDiscoverDetails.svelte';
import ModelsDiscoverDetailsDownloadOptions from '$lib/components/app/models/discover/ModelsDiscoverDetailsDownloadOptions.svelte';
import ModelsDiscoverDetailsHeader from '$lib/components/app/models/discover/ModelsDiscoverDetailsHeader.svelte';
import ModelsDiscoverDetailsName from '$lib/components/app/models/discover/ModelsDiscoverDetailsName.svelte';
import ModelsDiscoverDetailsReadme from '$lib/components/app/models/discover/ModelsDiscoverDetailsReadme.svelte';
import TerminalCommands from '$lib/components/app/models/discover/TerminalCommands.svelte';
import ModelsDiscoverModelDetails from '$lib/components/app/models/discover/ModelsDiscoverModelDetails.svelte';
import ModelsDiscoverModelDetailsCommands from '$lib/components/app/models/discover/ModelsDiscoverModelDetailsCommands.svelte';
import ModelsDiscoverModelDetailsDownloadOptions from '$lib/components/app/models/discover/ModelsDiscoverModelDetailsDownloadOptions.svelte';
import ModelsDiscoverModelDetailsHeader from '$lib/components/app/models/discover/ModelsDiscoverModelDetailsHeader.svelte';
import ModelsDiscoverModelDetailsReadme from '$lib/components/app/models/discover/ModelsDiscoverModelDetailsReadme.svelte';
import { ModelDraftSidecar } from '$lib/enums';
import type { HfModelSibling } from '$lib/types';
@@ -34,7 +33,7 @@
<Story name="Header">
<div class="w-160 p-4">
<ModelsDiscoverDetailsHeader
<ModelsDiscoverModelDetailsHeader
baseModels={mockDetails.cardData?.base_model ? [String(mockDetails.cardData.base_model)] : []}
details={mockDetails}
hasReasoning
@@ -48,29 +47,9 @@
</div>
</Story>
<Story name="Name">
<div class="space-y-4 p-4">
<ModelsDiscoverDetailsName
baseModels={['google/gemma-4-12b-it']}
hasReasoning
hasTools
hasVision
modelId="ggml-org/gemma-4-12b-it"
/>
<ModelsDiscoverDetailsName
baseModels={[]}
hasReasoning={false}
hasTools={false}
hasVision={false}
modelId="ggml-org/Qwen3.8-27B"
/>
</div>
</Story>
<Story name="Download options">
<div class="w-200 p-4">
<ModelsDiscoverDetailsDownloadOptions
<ModelsDiscoverModelDetailsDownloadOptions
bitDepthRows={[
{ bitDepth: 4, files: files.filter((f) => f.path.includes('Q4_K_M')) },
{ bitDepth: 8, files: files.filter((f) => f.path.includes('Q8_0')) },
@@ -83,7 +62,7 @@
<Story name="Download options (unknown device)">
<div class="w-200 p-4">
<ModelsDiscoverDetailsDownloadOptions
<ModelsDiscoverModelDetailsDownloadOptions
bitDepthRows={[
{ bitDepth: 4, files: files.filter((f) => f.path.includes('Q4_K_M')) },
{ bitDepth: 8, files: files.filter((f) => f.path.includes('Q8_0')) }
@@ -95,19 +74,22 @@
<Story name="Terminal commands (no sidecars)">
<div class="w-200 p-4">
<TerminalCommands modelId="ggml-org/Qwen3.8-27B-GGUF" />
<ModelsDiscoverModelDetailsCommands modelId="ggml-org/Qwen3.8-27B-GGUF" />
</div>
</Story>
<Story name="Terminal commands (MTP sidecar)">
<div class="w-200 p-4">
<TerminalCommands modelId="ggml-org/gemma-4-12b-it-GGUF" sidecars={[ModelDraftSidecar.MTP]} />
<ModelsDiscoverModelDetailsCommands
modelId="ggml-org/gemma-4-12b-it-GGUF"
sidecars={[ModelDraftSidecar.MTP]}
/>
</div>
</Story>
<Story name="Readme">
<div class="w-160 p-4">
<ModelsDiscoverDetailsReadme {readme} />
<ModelsDiscoverModelDetailsReadme {readme} />
</div>
</Story>
@@ -119,7 +101,7 @@
<Story name="Details (loading)">
<div class="h-96 w-200 border">
<ModelsDiscoverDetails
<ModelsDiscoverModelDetails
details={null}
files={[]}
loading
@@ -131,7 +113,7 @@
<Story name="Details (error)">
<div class="h-96 w-200 border">
<ModelsDiscoverDetails
<ModelsDiscoverModelDetails
details={null}
error="Model not found"
files={[]}
@@ -143,7 +125,7 @@
<Story name="Details (loaded)">
<div class="h-96 w-200 overflow-y-auto border">
<ModelsDiscoverDetails
<ModelsDiscoverModelDetails
details={mockDetails}
{files}
modelId={mockDetails.id ?? 'ggml-org/gemma-4-12b-it-GGUF'}