diff --git a/core/src/core.ts b/core/src/core.ts index 4f221f172..a44a11e95 100644 --- a/core/src/core.ts +++ b/core/src/core.ts @@ -47,6 +47,14 @@ const downloadFile: (url: string, fileName: string) => Promise = ( const deleteFile: (path: string) => Promise = (path) => window.coreAPI?.deleteFile(path) ?? window.electronAPI?.deleteFile(path); +/** + * Aborts the download of a specific file. + * @param {string} fileName - The name of the file whose download is to be aborted. + * @returns {Promise} A promise that resolves when the download has been aborted. + */ +const abortDownload: (fileName: string) => Promise = (fileName) => + window.coreAPI?.abortDownload(fileName); + /** * Retrieves the path to the app data directory using the `coreAPI` object. * If the `coreAPI` object is not available, the function returns `undefined`. @@ -79,6 +87,7 @@ export const core = { invokePluginFunc, executeOnMain, downloadFile, + abortDownload, deleteFile, appDataPath, getUserSpace, @@ -91,6 +100,7 @@ export { invokePluginFunc, executeOnMain, downloadFile, + abortDownload, deleteFile, appDataPath, getUserSpace, diff --git a/core/src/index.ts b/core/src/index.ts index 9e3954c4b..f7afccdb0 100644 --- a/core/src/index.ts +++ b/core/src/index.ts @@ -8,7 +8,13 @@ export { core, deleteFile, invokePluginFunc } from "./core"; * Core module exports. * @module */ -export { downloadFile, executeOnMain, appDataPath, getUserSpace } from "./core"; +export { + downloadFile, + executeOnMain, + appDataPath, + getUserSpace, + abortDownload, +} from "./core"; /** * Events module exports. diff --git a/core/src/plugins/model.ts b/core/src/plugins/model.ts index 03947d648..6ef158ec4 100644 --- a/core/src/plugins/model.ts +++ b/core/src/plugins/model.ts @@ -16,6 +16,14 @@ export abstract class ModelPlugin extends JanPlugin { */ abstract downloadModel(model: Model): Promise; + /** + * Cancels the download of a specific model. + * @param {string} name - The name of the model to cancel the download for. + * @param {string} modelId - The ID of the model to cancel the download for. + * @returns {Promise} A promise that resolves when the download has been cancelled. + */ + abstract cancelModelDownload(name: string, modelId: string): Promise; + /** * Deletes a model. * @param filePath - The file path of the model to delete. diff --git a/plugins/model-plugin/src/index.ts b/plugins/model-plugin/src/index.ts index 5fb487017..fd3e7331e 100644 --- a/plugins/model-plugin/src/index.ts +++ b/plugins/model-plugin/src/index.ts @@ -1,4 +1,4 @@ -import { PluginType, fs, downloadFile } from '@janhq/core' +import { PluginType, fs, downloadFile, abortDownload } from '@janhq/core' import { ModelPlugin } from '@janhq/core/lib/plugins' import { Model, ModelCatalog } from '@janhq/core/lib/types' import { parseToModel } from './helpers/modelParser' @@ -50,6 +50,15 @@ export default class JanModelPlugin implements ModelPlugin { downloadFile(model.downloadLink, path) } + /** + * Cancels the download of a specific machine learning model. + * @param {string} modelId - The ID of the model whose download is to be cancelled. + * @returns {Promise} A promise that resolves when the download has been cancelled. + */ + async cancelModelDownload(name: string, modelId: string): Promise { + return abortDownload(join(JanModelPlugin._homeDir, name, modelId)) + } + /** * Deletes a machine learning model. * @param filePath - The path to the model file to delete. diff --git a/web/containers/Layout/BottomBar/DownloadingState/index.tsx b/web/containers/Layout/BottomBar/DownloadingState/index.tsx index c825baa96..00056f74c 100644 --- a/web/containers/Layout/BottomBar/DownloadingState/index.tsx +++ b/web/containers/Layout/BottomBar/DownloadingState/index.tsx @@ -1,5 +1,7 @@ import { Fragment } from 'react' +import { PluginType } from '@janhq/core' +import { ModelPlugin } from '@janhq/core/lib/plugins' import { Progress, Modal, @@ -10,12 +12,18 @@ import { ModalTrigger, } from '@janhq/uikit' +import { useAtomValue } from 'jotai' + import { useDownloadState } from '@/hooks/useDownloadState' import { formatDownloadPercentage } from '@/utils/converter' +import { downloadingModelsAtom } from '@/helpers/atoms/Model.atom' +import { pluginManager } from '@/plugin' + export default function DownloadingState() { const { downloadStates } = useDownloadState() + const models = useAtomValue(downloadingModelsAtom) const totalCurrentProgress = downloadStates .map((a) => a.size.transferred + a.size.transferred) @@ -67,11 +75,17 @@ export default function DownloadingState() { diff --git a/web/containers/ModalCancelDownload/index.tsx b/web/containers/ModalCancelDownload/index.tsx index 33476fda1..2b5538e9b 100644 --- a/web/containers/ModalCancelDownload/index.tsx +++ b/web/containers/ModalCancelDownload/index.tsx @@ -1,5 +1,7 @@ import { useMemo } from 'react' +import { PluginType } from '@janhq/core' +import { ModelPlugin } from '@janhq/core/lib/plugins' import { ModelVersion } from '@janhq/core/lib/types' import { @@ -19,6 +21,9 @@ import { useDownloadState } from '@/hooks/useDownloadState' import { formatDownloadPercentage } from '@/utils/converter' +import { downloadingModelsAtom } from '@/helpers/atoms/Model.atom' +import { pluginManager } from '@/plugin' + type Props = { suitableModel: ModelVersion isFromList?: boolean @@ -34,6 +39,7 @@ export default function ModalCancelDownload({ // eslint-disable-next-line react-hooks/exhaustive-deps [suitableModel.name] ) + const models = useAtomValue(downloadingModelsAtom) const downloadState = useAtomValue(downloadAtom) return ( @@ -66,10 +72,15 @@ export default function ModalCancelDownload({