diff --git a/tools/ui/src/lib/components/app/models/discover/ModelsDiscoverDetails/ModelsDiscoverDetailsDownloadOptions/ModelsDiscoverDetailsDownloadOptions.svelte b/tools/ui/src/lib/components/app/models/discover/ModelsDiscoverDetails/ModelsDiscoverDetailsDownloadOptions/ModelsDiscoverDetailsDownloadOptions.svelte
index 3f99329e34..40783ba065 100644
--- a/tools/ui/src/lib/components/app/models/discover/ModelsDiscoverDetails/ModelsDiscoverDetailsDownloadOptions/ModelsDiscoverDetailsDownloadOptions.svelte
+++ b/tools/ui/src/lib/components/app/models/discover/ModelsDiscoverDetails/ModelsDiscoverDetailsDownloadOptions/ModelsDiscoverDetailsDownloadOptions.svelte
@@ -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. -->
{#each rows as row (row.bitDepth)}
-
+
{/each}
@@ -127,3 +156,17 @@
{/if}
+
+{#if pending}
+
+{/if}
diff --git a/tools/ui/src/lib/components/app/models/discover/ModelsDiscoverDetails/ModelsDiscoverDetailsDownloadOptions/ModelsDiscoverDetailsDownloadOptionsQuantDownloadButton.svelte b/tools/ui/src/lib/components/app/models/discover/ModelsDiscoverDetails/ModelsDiscoverDetailsDownloadOptions/ModelsDiscoverDetailsDownloadOptionsQuantDownloadButton.svelte
index 1a47cd439c..1b376489f3 100644
--- a/tools/ui/src/lib/components/app/models/discover/ModelsDiscoverDetails/ModelsDiscoverDetailsDownloadOptions/ModelsDiscoverDetailsDownloadOptionsQuantDownloadButton.svelte
+++ b/tools/ui/src/lib/components/app/models/discover/ModelsDiscoverDetails/ModelsDiscoverDetailsDownloadOptions/ModelsDiscoverDetailsDownloadOptionsQuantDownloadButton.svelte
@@ -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 @@
{tooltipText}
-
- (confirmDeleteOpen = false)}
- onConfirm={() => {
- confirmDeleteOpen = false;
-
- void modelsStore.status.cancelDownload(entry.repoWithTag);
- }}
- open={confirmDeleteOpen}
- title="Delete model"
- variant="destructive"
- />
{:else if entry.isDownloading || entry.isPaused}