From 4969795a9533d1b6675e3f62ff5458f679c51a37 Mon Sep 17 00:00:00 2001 From: nemitha2005 Date: Tue, 28 Apr 2026 09:10:13 +0530 Subject: [PATCH] feat(downloads): enhance downloads popover with dynamic resizing and recent history --- .claude/settings.local.json | 10 +++++ src/main/index.ts | 22 +++++++---- src/preload/index.ts | 6 +++ src/renderer/downloads-popover.html | 4 +- src/renderer/downloads-popover.ts | 57 ++++++++++++++++++----------- src/renderer/window.d.ts | 1 + src/shared/ipc-contract.ts | 1 + 7 files changed, 70 insertions(+), 31 deletions(-) create mode 100644 .claude/settings.local.json diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..eb582e4 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,10 @@ +{ + "permissions": { + "allow": [ + "Bash(npm run *)", + "Bash(xargs wc *)", + "Bash(xargs ls *)", + "Bash(git checkout *)" + ] + } +} diff --git a/src/main/index.ts b/src/main/index.ts index c5638a8..a1d3544 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -100,7 +100,7 @@ interface ManagedDownload { const MENU_WIDTH = 220; const MENU_HEIGHT = 360; const DOWNLOADS_POPOVER_WIDTH = 320; -const DOWNLOADS_POPOVER_HEIGHT = 280; +const DOWNLOADS_POPOVER_HEIGHT = 480; let mainWindow: BrowserWindow | null = null; let floatWindow: BrowserWindow | null = null; @@ -483,9 +483,7 @@ function emitDownloadsChanged(): void { if (downloadsPopoverWindow && !downloadsPopoverWindow.isDestroyed()) { const downloadsPopoverPayload: DownloadsPopoverInitPayload = { theme: downloadsPopoverTheme, - downloads: downloadsSnapshot.filter(download => { - return download.state === 'progressing' || download.state === 'paused'; - }), + downloads: downloadsSnapshot.slice(0, 5), }; downloadsPopoverWindow.webContents.send( @@ -1359,9 +1357,7 @@ ipcMain.handle(IPC_CHANNELS.DOWNLOADS_POPOVER_SHOW, (_event, payload: unknown) = const initPayload: DownloadsPopoverInitPayload = { theme: downloadsPopoverTheme, - downloads: getDownloadsSnapshot().filter(download => { - return download.state === 'progressing' || download.state === 'paused'; - }), + downloads: getDownloadsSnapshot().slice(0, 5), }; const sendAndShow = (): void => { @@ -1385,6 +1381,18 @@ ipcMain.handle(IPC_CHANNELS.DOWNLOADS_POPOVER_OPEN_PAGE, () => { } }); +ipcMain.handle(IPC_CHANNELS.DOWNLOADS_POPOVER_RESIZE, (_event, height: unknown) => { + if (typeof height !== 'number' || height <= 0) return; + if (downloadsPopoverWindow && !downloadsPopoverWindow.isDestroyed()) { + const currentBounds = downloadsPopoverWindow.getBounds(); + const newHeight = Math.min(Math.round(height), DOWNLOADS_POPOVER_HEIGHT); + downloadsPopoverWindow.setBounds({ + ...currentBounds, + height: newHeight, + }); + } +}); + ipcMain.handle(IPC_CHANNELS.FLOAT_NAVIGATE, (_event, payload: unknown) => { const safeUrl = parseFloatNavigatePayload(payload); if (!safeUrl) { diff --git a/src/preload/index.ts b/src/preload/index.ts index c90da12..28534a5 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -303,6 +303,12 @@ contextBridge.exposeInMainWorld('orb', { .then(() => undefined); }, + resizeDownloadsPopover: (height: number) => { + return ipcRenderer + .invoke(IPC_CHANNELS.DOWNLOADS_POPOVER_RESIZE, height) + .then(() => undefined); + }, + onDownloadsPopoverInit: (callback: (payload: DownloadsPopoverInitPayload) => void) => { const handler = (_event: Electron.IpcRendererEvent, payload: unknown): void => { const parsedPayload = parseDownloadsPopoverInitPayload(payload); diff --git a/src/renderer/downloads-popover.html b/src/renderer/downloads-popover.html index 0947be8..15f138d 100644 --- a/src/renderer/downloads-popover.html +++ b/src/renderer/downloads-popover.html @@ -11,8 +11,8 @@
// downloads
-
-
no active downloads.
+
+
no recent downloads.
diff --git a/src/renderer/downloads-popover.ts b/src/renderer/downloads-popover.ts index c857e72..3ae3232 100644 --- a/src/renderer/downloads-popover.ts +++ b/src/renderer/downloads-popover.ts @@ -69,27 +69,23 @@ function renderDownloads(downloads: DownloadSnapshot[]): void {
${escapeHtml(formatBytes(download.receivedBytes))} / ${escapeHtml(totalBytesText)}
` : ''; - let actionsMarkup = ''; - if (isCompleted) { - actionsMarkup = ` -
- - -
`; - } else if (isActive) { - const pauseBtn = download.state === 'progressing' - ? `` - : ''; - const resumeBtn = download.state === 'paused' && download.canResume - ? `` - : ''; - actionsMarkup = ` -
- ${pauseBtn} - ${resumeBtn} - -
`; - } + const canPause = download.state === 'progressing'; + const canResume = download.state === 'paused' && download.canResume; + const canCancel = isActive; + const canRemove = !isActive; + + const actionBtn = (label: string, attr: string, recordId: string, disabled: boolean): string => + disabled ? '' : ``; + + const actionsMarkup = ` +
+ ${actionBtn('pause', 'data-popover-pause-id', id, !canPause)} + ${actionBtn('resume', 'data-popover-resume-id', id, !canResume)} + ${actionBtn('cancel', 'data-popover-cancel-id', id, !canCancel)} + ${actionBtn('open', 'data-popover-open-id', id, !isCompleted)} + ${actionBtn('show in folder', 'data-popover-show-id', id, !isCompleted)} + ${actionBtn('remove', 'data-popover-remove-id', id, !canRemove)} +
`; item.innerHTML = `
@@ -102,6 +98,20 @@ function renderDownloads(downloads: DownloadSnapshot[]): void { downloadsList.appendChild(item); }); + + requestAnimationFrame(() => { + const parentContainer = document.querySelector('.rounded-orb') as HTMLElement; + if (parentContainer) { + void window.orb.resizeDownloadsPopover(parentContainer.offsetHeight); + } + }); + + requestAnimationFrame(() => { + const parentContainer = document.querySelector('.rounded-orb') as HTMLElement; + if (parentContainer) { + void window.orb.resizeDownloadsPopover(parentContainer.offsetHeight); + } + }); } // Event delegation for action buttons @@ -122,7 +132,10 @@ downloadsList.addEventListener('click', event => { if (resumeId) { void window.orb.resumeDownload(resumeId); return; } const cancelId = target.closest('[data-popover-cancel-id]')?.getAttribute('data-popover-cancel-id'); - if (cancelId) { void window.orb.cancelDownload(cancelId); } + if (cancelId) { void window.orb.cancelDownload(cancelId); return; } + + const removeId = target.closest('[data-popover-remove-id]')?.getAttribute('data-popover-remove-id'); + if (removeId) { void window.orb.removeDownload(removeId); } }); window.orb.onDownloadsPopoverInit((payload: DownloadsPopoverInitPayload) => { diff --git a/src/renderer/window.d.ts b/src/renderer/window.d.ts index f2ae802..f891309 100644 --- a/src/renderer/window.d.ts +++ b/src/renderer/window.d.ts @@ -49,6 +49,7 @@ declare global { onDownloadsChanged: (callback: (downloads: DownloadSnapshot[]) => void) => () => void; showDownloadsPopover: (payload: DownloadsPopoverShowPayload) => Promise; openDownloadsPageFromPopover: () => Promise; + resizeDownloadsPopover: (height: number) => Promise; onDownloadsPopoverInit: ( callback: (payload: DownloadsPopoverInitPayload) => void, ) => () => void; diff --git a/src/shared/ipc-contract.ts b/src/shared/ipc-contract.ts index 404238d..839731d 100644 --- a/src/shared/ipc-contract.ts +++ b/src/shared/ipc-contract.ts @@ -125,6 +125,7 @@ export const IPC_CHANNELS = { DOWNLOADS_SHOW_IN_FOLDER: 'downloads-show-in-folder', DOWNLOADS_POPOVER_SHOW: 'downloads-popover-show', DOWNLOADS_POPOVER_INIT: 'downloads-popover-init', + DOWNLOADS_POPOVER_RESIZE: 'downloads-popover-resize', DOWNLOADS_POPOVER_OPEN_PAGE: 'downloads-popover-open-page', MENU_SHOW: 'menu-show', MENU_INIT: 'menu-init',