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',