diff --git a/src/renderer/ui/ContextInfoButton.tsx b/src/renderer/ui/ContextInfoButton.tsx
index 8429738..7e0e5a8 100644
--- a/src/renderer/ui/ContextInfoButton.tsx
+++ b/src/renderer/ui/ContextInfoButton.tsx
@@ -1,4 +1,4 @@
-import { Children, Fragment, isValidElement, useId, type ReactElement, type ReactNode } from "react";
+import { Children, Fragment, isValidElement, useId, useRef, type ReactElement, type ReactNode } from "react";
import { Icon } from "./Icon";
export interface ContextInfoButtonProps {
@@ -37,19 +37,45 @@ export function ContextInfoButton({
onOpenChange
}: ContextInfoButtonProps): ReactElement | null {
const regionId = useId();
+ const pointerInside = useRef(false);
+ const focusInside = useRef(false);
if (!hasRenderableContent(content)) {
return null;
}
return (
-
+
{
+ if (!event.currentTarget.contains(event.relatedTarget as Node | null)) {
+ focusInside.current = false;
+ if (!pointerInside.current) {
+ onOpenChange(false);
+ }
+ }
+ }}
+ onFocus={() => {
+ focusInside.current = true;
+ onOpenChange(true);
+ }}
+ onPointerEnter={() => {
+ pointerInside.current = true;
+ onOpenChange(true);
+ }}
+ onPointerLeave={() => {
+ pointerInside.current = false;
+ if (!focusInside.current) {
+ onOpenChange(false);
+ }
+ }}
+ >
);
}
diff --git a/src/renderer/views/downloads/download-disclosure-transition.ts b/src/renderer/views/downloads/download-disclosure-transition.ts
index 715e89f..d8fc373 100644
--- a/src/renderer/views/downloads/download-disclosure-transition.ts
+++ b/src/renderer/views/downloads/download-disclosure-transition.ts
@@ -62,7 +62,7 @@ function itemGroup(
packageId,
items: items.map((row) => ({ ...row, height: DOWNLOAD_FILE_ROW_HEIGHT })),
height,
- disclosureOpacity: 1,
+ disclosureOpacity: phase === "entering" ? 0 : 1,
disclosurePhase: phase
};
}
@@ -197,8 +197,8 @@ export function activateDownloadDisclosureTransition(rows: readonly DownloadDisc
return rows.map((row) => {
if (row.type !== "item-group") return row;
return row.disclosurePhase === "entering"
- ? { ...row, height: row.items.length * DOWNLOAD_FILE_ROW_HEIGHT }
- : { ...row, height: 0 };
+ ? { ...row, height: row.items.length * DOWNLOAD_FILE_ROW_HEIGHT, disclosureOpacity: 1 }
+ : { ...row, height: 0, disclosureOpacity: 0 };
});
}
diff --git a/src/renderer/views/downloads/downloads.css b/src/renderer/views/downloads/downloads.css
index 5fbffbd..80ccbaf 100644
--- a/src/renderer/views/downloads/downloads.css
+++ b/src/renderer/views/downloads/downloads.css
@@ -39,18 +39,11 @@
padding: 14px 12px 6px;
}
-.downloads-mode-title {
- display: flex;
- min-height: 36px;
- align-items: center;
- justify-content: center;
- padding: 0 10px;
+.downloads-filter-group {
+ padding: 4px;
border: 1px solid var(--ui-border);
border-radius: 6px;
- color: #0a0f1a;
- background: #90cdf4;
- text-align: center;
- font-weight: 700;
+ background: var(--ui-input);
}
.downloads-sidebar nav button,
@@ -204,8 +197,10 @@
.downloads-toolbar {
display: flex;
align-items: center;
+ flex-wrap: wrap;
gap: 6px;
min-height: 52px;
+ overflow-x: hidden;
padding: 8px 10px;
border-bottom: 1px solid var(--ui-border);
background: var(--ui-surface);
@@ -237,6 +232,13 @@
background: var(--ui-border);
}
+.downloads-toolbar-tail {
+ display: flex;
+ flex: 1 0 auto;
+ justify-content: flex-end;
+ margin-left: auto;
+}
+
.downloads-schedule-slot {
display: grid;
grid-template-columns: 0fr;
@@ -941,7 +943,6 @@
}
@media (max-width: 1120px) {
- .downloads-toolbar,
.downloads-footer {
overflow-x: auto;
}
diff --git a/tests/downloads-view.test.tsx b/tests/downloads-view.test.tsx
index 89f7cfe..d669619 100644
--- a/tests/downloads-view.test.tsx
+++ b/tests/downloads-view.test.tsx
@@ -1,7 +1,7 @@
import { readFileSync } from "node:fs";
import fs from "node:fs";
import path from "node:path";
-import { isValidElement, type ReactElement, type ReactNode } from "react";
+import { Children, isValidElement, type ReactElement, type ReactNode } from "react";
import { renderToStaticMarkup } from "react-dom/server";
import { describe, expect, it, vi } from "vitest";
import type { DownloadItem, DownloadStatus, PackageEntry } from "../src/shared/types";
@@ -70,6 +70,7 @@ import {
normalizeDownloadServiceLabel
} from "../src/renderer/download-format";
import { getRollingMetricDirection } from "../src/renderer/ui/RollingMetricValue";
+import { SlidingSelection } from "../src/renderer/ui/SlidingSelection";
const now = new Date(2026, 7, 10, 12, 0, 0, 0).getTime();
@@ -371,7 +372,7 @@ describe("virtualisierte Paketanimation", () => {
expect(prepared.animated).toBe(true);
expect(prepared.rows.map((row) => [row.id, row.height, row.disclosurePhase, row.disclosureOpacity])).toEqual([
["package-a", 40, "stable", 1],
- ["package-a:items", 0, "entering", 1],
+ ["package-a:items", 0, "entering", 0],
["package-b", 40, "stable", 1],
["b-1", 38, "stable", 1]
]);
@@ -411,7 +412,7 @@ describe("virtualisierte Paketanimation", () => {
expect(preparedGroup?.type === "item-group" ? preparedGroup.items.map((row) => row.height) : []).toEqual([38, 38]);
expect(activateDownloadDisclosureTransition(prepared.rows).map((row) => [row.id, row.height, row.disclosureOpacity])).toEqual([
["package-a", 40, 1],
- ["package-a:items", 0, 1],
+ ["package-a:items", 0, 0],
["package-b", 40, 1],
["b-1", 38, 1]
]);
@@ -1041,14 +1042,52 @@ describe("downloads view", () => {
expect(multiple).not.toMatch(/