release: publish Multi-Debrid Downloader v2.0.14

Add live English and German localization, queue availability and metadata resolution, responsive package controls, polished navigation and drag interactions, clearer history and account states, and a rebuilt public README. Harden Windows packaging with verified icons and version metadata, archive inspection, and expanded release tests.
This commit is contained in:
Sucukdeluxe
2026-08-10 19:42:49 +02:00
parent 069babfd54
commit a5758aa905
61 changed files with 9117 additions and 6754 deletions
+63 -30
View File
@@ -1,38 +1,71 @@
import type { ReactElement, ReactNode } from "react";
import { useEffect, useLayoutEffect, useRef, type ReactElement, type ReactNode } from "react";
import appIconUrl from "../../../assets/app_icon.png";
import { Icon } from "../ui/Icon";
import { buildMainNavigation, type MainView } from "./shell-model";
import { buildMainNavigation, type MainView } from "./shell-model";
export interface AppHeaderProps {
activeView: MainView;
onViewChange: (view: MainView) => void;
actions: ReactNode;
activeView: MainView;
onViewChange: (view: MainView) => void;
actions: ReactNode;
}
const useHeaderLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
export function AppHeader({ activeView, onViewChange, actions }: AppHeaderProps): ReactElement {
const navigation = buildMainNavigation(activeView);
const navigationRef = useRef<HTMLElement>(null);
return (
<header className="md-shell-header" data-ui-region="header">
<div className="md-shell-brand">Multi-Debrid-Downloader</div>
<nav aria-label="Hauptnavigation" className="md-shell-navigation" role="tablist">
{navigation.map((item) => (
<button
aria-current={item.active ? "page" : undefined}
aria-selected={item.active}
useHeaderLayoutEffect(() => {
const element = navigationRef.current;
if (!element) return;
let frame = 0;
const sync = (): void => {
cancelAnimationFrame(frame);
frame = requestAnimationFrame(() => {
const activeItem = element.querySelector<HTMLElement>(`.md-shell-navigation-item[data-main-view="${activeView}"]`);
if (!activeItem) return;
const navigationRect = element.getBoundingClientRect();
const itemRect = activeItem.getBoundingClientRect();
element.style.setProperty("--md-navigation-active-x", `${itemRect.left - navigationRect.left}px`);
element.style.setProperty("--md-navigation-active-y", `${itemRect.top - navigationRect.top}px`);
element.style.setProperty("--md-navigation-active-width", `${itemRect.width}px`);
element.style.setProperty("--md-navigation-active-height", `${itemRect.height}px`);
element.classList.add("has-active-indicator");
});
};
const observer = new ResizeObserver(sync);
observer.observe(element);
element.querySelectorAll<HTMLElement>(".md-shell-navigation-item").forEach((item) => observer.observe(item));
sync();
return () => {
cancelAnimationFrame(frame);
observer.disconnect();
};
}, [activeView]);
return (
<header className="md-shell-header" data-ui-region="header">
<div className="md-shell-brand"><img alt="" className="md-shell-brand-mark" src={appIconUrl} /><span>Multi-Debrid Downloader</span></div>
<nav aria-label="Hauptnavigation" className="md-shell-navigation" ref={navigationRef} role="tablist">
{navigation.map((item) => (
<button
aria-current={item.active ? "page" : undefined}
aria-selected={item.active}
className={`md-shell-navigation-item${item.active ? " is-active" : ""}`}
data-visual-active-view={item.active ? item.id : undefined}
key={item.id}
onClick={() => onViewChange(item.id)}
role="tab"
title={item.label}
type="button"
>
<Icon name={item.icon} size={18} />
<span>{item.label}</span>
</button>
))}
</nav>
{actions ? <div aria-label="Globale Aktionen" className="md-shell-header-actions" role="group">{actions}</div> : null}
</header>
);
}
data-main-view={item.id}
data-visual-active-view={item.active ? item.id : undefined}
key={item.id}
onClick={() => onViewChange(item.id)}
role="tab"
title={item.label}
type="button"
>
<Icon name={item.icon} size={18} />
<span>{item.label}</span>
</button>
))}
</nav>
{actions ? <div aria-label="Globale Aktionen" className="md-shell-header-actions" role="group">{actions}</div> : null}
</header>
);
}
+1 -1
View File
@@ -113,7 +113,7 @@ export function UpdateExperience({
{releaseNotes.trim() ? (
<details className="md-update-release-notes">
<summary>Changelog anzeigen</summary>
<pre>{releaseNotes}</pre>
<pre data-i18n-ignore="true">{releaseNotes}</pre>
</details>
) : null}
</>
File diff suppressed because it is too large Load Diff