mirror of
https://github.com/ggml-org/llama.cpp.git
synced 2026-09-17 20:31:47 +02:00
ui : lift download delete/cancel confirmations into the options panel
Move the two confirmation dialogs out of the quant download chip into the download-options parent, which now owns a single pair keyed by the acted-on repo+tag. The chip stays presentational and only signals delete/cancel intent. Assisted-by: llama-ui:Qwen3.8-Flash-Next
This commit is contained in:
+44
-1
@@ -2,6 +2,7 @@
|
||||
import { classify, labelFor } from './download-options.utils';
|
||||
import ModelsDiscoverDetailsDownloadOptionsDownloadCommand from './ModelsDiscoverDetailsDownloadOptionsDownloadCommand.svelte';
|
||||
import ModelsDiscoverDetailsDownloadOptionsRow from './ModelsDiscoverDetailsDownloadOptionsRow.svelte';
|
||||
import DialogConfirmation from '$lib/components/app/dialogs/DialogConfirmation.svelte';
|
||||
import { SelectableFileKind } from '$lib/enums';
|
||||
import { HuggingFaceService, ModelsService } from '$lib/services';
|
||||
import { modelsStore } from '$lib/stores';
|
||||
@@ -22,6 +23,29 @@
|
||||
|
||||
let { bitDepthRows, getDownloadState, modelId }: Props = $props();
|
||||
|
||||
// Destructive chip actions (delete a downloaded model, cancel a download) are
|
||||
// confirmed here rather than inside each chip: one pair of dialogs owned by
|
||||
// the options panel, keyed by the repo+tag the user acted on.
|
||||
let pending: { action: 'cancel' | 'delete'; repoWithTag: string } | null = $state(null);
|
||||
|
||||
function requestCancel(repoWithTag: string) {
|
||||
pending = { action: 'cancel', repoWithTag };
|
||||
}
|
||||
|
||||
function requestDelete(repoWithTag: string) {
|
||||
pending = { action: 'delete', repoWithTag };
|
||||
}
|
||||
|
||||
function closePending() {
|
||||
pending = null;
|
||||
}
|
||||
|
||||
function confirmPending() {
|
||||
if (pending) void modelsStore.status.cancelDownload(pending.repoWithTag);
|
||||
|
||||
pending = null;
|
||||
}
|
||||
|
||||
function stateFor(repoWithTag: string, filePath: string, isSidecar: boolean): DownloadEntryState {
|
||||
if (getDownloadState) return getDownloadState(repoWithTag, filePath, isSidecar);
|
||||
|
||||
@@ -117,7 +141,12 @@
|
||||
lifecycle state; nothing here selects anything. -->
|
||||
<div class="flex w-full flex-col divide-y divide-border/50 px-4 pb-1 dark:divide-border/35">
|
||||
{#each rows as row (row.bitDepth)}
|
||||
<ModelsDiscoverDetailsDownloadOptionsRow bitDepth={row.bitDepth} files={row.files} />
|
||||
<ModelsDiscoverDetailsDownloadOptionsRow
|
||||
bitDepth={row.bitDepth}
|
||||
files={row.files}
|
||||
onRequestCancel={requestCancel}
|
||||
onRequestDelete={requestDelete}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
@@ -127,3 +156,17 @@
|
||||
</div>
|
||||
</section>
|
||||
{/if}
|
||||
|
||||
{#if pending}
|
||||
<DialogConfirmation
|
||||
confirmText={pending.action === 'delete' ? 'Delete' : 'Cancel download'}
|
||||
description={pending.action === 'delete'
|
||||
? `This permanently removes ${modelsStore.toDisplayName(pending.repoWithTag)} from disk. You can download it again later.`
|
||||
: `This stops the download of ${modelsStore.toDisplayName(pending.repoWithTag)} and removes the partial files. Pause it instead to keep the progress.`}
|
||||
onCancel={closePending}
|
||||
onConfirm={confirmPending}
|
||||
open
|
||||
title={pending.action === 'delete' ? 'Delete model' : 'Cancel download'}
|
||||
variant="destructive"
|
||||
/>
|
||||
{/if}
|
||||
|
||||
+10
-37
@@ -2,7 +2,6 @@
|
||||
import DownloadProgressBar from '../../DownloadProgressBar.svelte';
|
||||
import { labelFor } from './download-options.utils';
|
||||
import { Check, Download, Loader2, Pause, Play, RotateCw, X } from '@lucide/svelte';
|
||||
import DialogConfirmation from '$lib/components/app/dialogs/DialogConfirmation.svelte';
|
||||
import * as Tooltip from '$lib/components/ui/tooltip';
|
||||
import { HuggingFaceService } from '$lib/services';
|
||||
import { modelsStore } from '$lib/stores';
|
||||
@@ -13,13 +12,16 @@
|
||||
file: HfModelSibling;
|
||||
/** Download state of the file, from the parent's status feed. */
|
||||
entry: DownloadEntryState;
|
||||
/**
|
||||
* Ask the parent to confirm deleting a downloaded model. The chip owns no
|
||||
* dialog; the parent renders the single confirmation and acts on confirm.
|
||||
*/
|
||||
onRequestDelete?: (repoWithTag: string) => void;
|
||||
/** Ask the parent to confirm cancelling an in-flight download. */
|
||||
onRequestCancel?: (repoWithTag: string) => void;
|
||||
}
|
||||
|
||||
let { entry, file }: Props = $props();
|
||||
|
||||
// delete / cancel confirmation state
|
||||
let confirmDeleteOpen = $state(false);
|
||||
let confirmCancelOpen = $state(false);
|
||||
let { entry, file, onRequestCancel, onRequestDelete }: Props = $props();
|
||||
|
||||
/** Queue the download; a failed attempt leaves partial files, drop them first. */
|
||||
async function startDownload() {
|
||||
@@ -62,7 +64,7 @@
|
||||
aria-label={tooltipText}
|
||||
class="group relative inline-flex h-auto cursor-pointer items-center gap-1 rounded-md! border px-2 py-1 text-left font-mono text-xs shadow-xs transition-[background-color,border-color,transform] duration-200 ease-[cubic-bezier(0.23,1,0.32,1)] active:scale-[0.97]
|
||||
border-green-600/25 bg-green-500/5 hover:border-destructive/50 hover:bg-destructive/10 dark:border-green-500/30 dark:bg-green-500/10 dark:hover:border-destructive/50 dark:hover:bg-destructive/15"
|
||||
onclick={() => (confirmDeleteOpen = true)}
|
||||
onclick={() => onRequestDelete?.(entry.repoWithTag)}
|
||||
type="button"
|
||||
>
|
||||
{#if meta?.sidecar}
|
||||
@@ -98,20 +100,6 @@
|
||||
<p>{tooltipText}</p>
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Root>
|
||||
|
||||
<DialogConfirmation
|
||||
confirmText="Delete"
|
||||
description={`This permanently removes ${modelsStore.toDisplayName(entry.repoWithTag)} from disk. You can download it again later.`}
|
||||
onCancel={() => (confirmDeleteOpen = false)}
|
||||
onConfirm={() => {
|
||||
confirmDeleteOpen = false;
|
||||
|
||||
void modelsStore.status.cancelDownload(entry.repoWithTag);
|
||||
}}
|
||||
open={confirmDeleteOpen}
|
||||
title="Delete model"
|
||||
variant="destructive"
|
||||
/>
|
||||
{:else if entry.isDownloading || entry.isPaused}
|
||||
<!-- in-flight / paused chips: the chip body pauses / resumes on click, the X
|
||||
inside the chip cancels (stops and discards the partial files). The X slot
|
||||
@@ -188,7 +176,7 @@
|
||||
<button
|
||||
aria-label="Cancel downloading"
|
||||
class="relative inline-flex h-3.5 w-3.5 shrink-0 cursor-pointer items-center justify-center text-muted-foreground/70"
|
||||
onclick={() => (confirmCancelOpen = true)}
|
||||
onclick={() => onRequestCancel?.(entry.repoWithTag)}
|
||||
type="button"
|
||||
>
|
||||
<X
|
||||
@@ -261,18 +249,3 @@
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Root>
|
||||
{/if}
|
||||
|
||||
<DialogConfirmation
|
||||
cancelText="Keep downloading"
|
||||
confirmText="Cancel download"
|
||||
description={`This stops the download of ${modelsStore.toDisplayName(entry.repoWithTag)} and removes the partial files. Pause it instead to keep the progress.`}
|
||||
onCancel={() => (confirmCancelOpen = false)}
|
||||
onConfirm={() => {
|
||||
confirmCancelOpen = false;
|
||||
|
||||
void modelsStore.status.cancelDownload(entry.repoWithTag);
|
||||
}}
|
||||
open={confirmCancelOpen}
|
||||
title="Cancel download"
|
||||
variant="destructive"
|
||||
/>
|
||||
|
||||
+11
-2
@@ -15,9 +15,13 @@
|
||||
bitDepth: number;
|
||||
/** Every GGUF of this bit depth, with download state attached. */
|
||||
files: (SelectableFile & { state: DownloadEntryState })[];
|
||||
/** Forwarded to each chip: ask the parent to confirm a cancel. */
|
||||
onRequestCancel?: (repoWithTag: string) => void;
|
||||
/** Forwarded to each chip: ask the parent to confirm a delete. */
|
||||
onRequestDelete?: (repoWithTag: string) => void;
|
||||
}
|
||||
|
||||
let { bitDepth, files }: Props = $props();
|
||||
let { bitDepth, files, onRequestCancel, onRequestDelete }: Props = $props();
|
||||
|
||||
let mainFile = $derived(files.find((f) => f.kind === SelectableFileKind.MAIN) ?? null);
|
||||
let draftFile = $derived(files.find((f) => f.kind === SelectableFileKind.DRAFT) ?? null);
|
||||
@@ -45,7 +49,12 @@
|
||||
|
||||
<div class="flex flex-wrap justify-end gap-1.5">
|
||||
{#each files as file (file.path)}
|
||||
<ModelsDiscoverDetailsDownloadOptionsQuantDownloadButton entry={file.state} {file} />
|
||||
<ModelsDiscoverDetailsDownloadOptionsQuantDownloadButton
|
||||
entry={file.state}
|
||||
{file}
|
||||
{onRequestCancel}
|
||||
{onRequestDelete}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user