Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,31 @@ All notable changes to DLSSync are documented here.
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.6.6] - 2026-06-07

Quality-of-life on the X button, the apply modal, and Backups. The X minimizes to the system tray for new installs while your saved choice still wins. The Apply Progress wall is gone: close the modal mid-apply, navigate to Catalog or About, then pick it back up from the activity dock. And the date-grouped Backups view divides itself into month headers, so months of history read at a glance instead of a flat scroll.

### Added

- A token-driven `<CounterPill>` component shared by every sidebar counter so the badges line up the same way everywhere.
- Three intent-named sidebar groups (Library, Catalog & Drivers, History) plus the existing General. Same icons, same view names, clearer headers.
- A bundle-size contract test that fails the build if the main JS chunk exceeds 250 KB gzip or the CSS exceeds 75 KB gzip.
- A rotating chevron on the language switcher tied to `aria-expanded` so the menu state reads correctly to screen readers and to the eye.
- A month header divides the Backups date-grouped view at every month boundary, so multi-month histories read at a glance instead of a long flat list.
- An axe-core accessibility contract over the core components (CounterPill, BrandMark, Checkbox, Toast) that fails the build on any critical or serious WCAG violation.

### Changed

- Close-to-tray is on by default for new installs. An explicit `false` from a prior config is preserved — never silently re-enabled.
- The Apply Progress modal can be closed while applies are still running. Click the X, hit Escape, or click outside, and it minimizes to the activity dock. The apply keeps going, the rest of the app is yours. A one-time hint shows you where the dock is the first time.
- The apply pipeline's pure decisions (error classification, signature-error hinting, version-major parsing, the Streamline cross-major ban check, the failure-outcome shape) moved into their own module with a focused 14-test cargo suite. The 400-line apply hot path stays the orchestrator; the decisions get their own contract.
- Driver history and Catalog versions flyouts share a `<FlyoutShell>` primitive now. Backdrop, dialog frame, vendor-accent routing, and the Escape-close behavior live in one place instead of repeated in each consumer.

### Fixed

- Two concurrent driver checks used to collect WMI/DXGI system info twice in parallel under a stale read-lock pattern. A coordinator now serializes the collection so it runs exactly once, and the cached value is reused.
- Large DLL copies during apply no longer block the tokio runtime — every `std::fs::copy` is wrapped in `spawn_blocking` so download-progress emits, cancellation signals, and tray pings stay responsive even while the file copy runs.

## [1.6.5] - 2026-06-03

DLSSync can keep your DLLs current on its own now — a background daemon that scans on a schedule, sits in the tray, and warns you before you touch a game that can ban you for it.
Expand Down
26 changes: 13 additions & 13 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ resolver = "2"
members = ["src-tauri", "crates/*"]

[workspace.package]
version = "1.6.5"
version = "1.6.6"
edition = "2021"
license = "Apache-2.0"
repository = "https://github.com/xt0n1-t3ch/DLSSync"
Expand Down
12 changes: 12 additions & 0 deletions crates/driver-install/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ impl InstallPhase {
}
}

pub fn reboot_required(code: i32) -> bool {
code == EXIT_REBOOT_REQUIRED
}

pub fn classify_exit(code: i32) -> InstallStage {
match code {
EXIT_OK | EXIT_REBOOT_REQUIRED => InstallStage::Completed,
Expand Down Expand Up @@ -160,6 +164,14 @@ mod tests {
}
}

#[test]
fn reboot_required_true_only_for_3010() {
assert!(reboot_required(3010));
assert!(!reboot_required(0));
assert!(!reboot_required(1602));
assert!(!reboot_required(1));
}

#[test]
fn exit_codes_map_to_terminal_stages() {
assert_eq!(classify_exit(0), InstallStage::Completed);
Expand Down
2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "dlssync-frontend",
"private": true,
"version": "1.6.5",
"version": "1.6.6",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down
38 changes: 32 additions & 6 deletions frontend/src/components/ApplyProgressModal.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,26 @@
onClose();
}

// v1.7: close the modal WITHOUT clearing in-flight applies. The ActivityDock
// keeps tracking the same `activeApplies` store and the user can click its
// expand chevron at any time to re-open the modal. We never lose progress.
async function pin(): Promise<void> {
onClose();
if (!anyRunning) return;
try {
if (!localStorage.getItem("dlssync.applyPinHintSeen")) {
localStorage.setItem("dlssync.applyPinHintSeen", "1");
showToast(
"info",
translate(get(locale), "component.applyModal.toast.movedToDock"),
);
}
} catch {
// localStorage may be blocked (private window, exotic config). Silent
// skip is safe — at worst the hint shows once next session.
}
}

async function handleRetryGroup(g: ApplyGroup): Promise<void> {
if (busy) return;
busy = true;
Expand Down Expand Up @@ -540,9 +560,12 @@
class="backdrop"
transition:fade={{ duration: 150 }}
role="presentation"
onclick={() => allDone && dismiss()}
onclick={() => (allDone ? void dismiss() : void pin())}
onkeydown={(e) => {
if (e.key === "Escape" && allDone) void dismiss();
if (e.key === "Escape") {
if (allDone) void dismiss();
else void pin();
}
}}
tabindex="-1"
></div>
Expand Down Expand Up @@ -586,10 +609,13 @@
</div>
<button
class="dialog-close"
onclick={() => allDone && dismiss()}
disabled={!allDone}
title={allDone ? $t("common.close") : $t("component.applyModal.closeBlockedTitle")}
aria-label={$t("common.close")}
onclick={() => (allDone ? void dismiss() : void pin())}
title={allDone
? $t("common.close")
: $t("component.applyModal.action.minimize")}
aria-label={allDone
? $t("common.close")
: $t("component.applyModal.action.minimize")}
>
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
</button>
Expand Down
46 changes: 16 additions & 30 deletions frontend/src/components/CatalogVersionsFlyout.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<script lang="ts">
import { onMount } from "svelte";
import { get } from "svelte/store";
import { fly } from "svelte/transition";
import { t, locale, translate } from "../lib/i18n/index";
import { listReleases, type Release } from "../lib/api";
import {
Expand All @@ -17,6 +16,7 @@
import { mergeFamilyReleases } from "../lib/catalogReleases";
import FeatureIcon from "./FeatureIcon.svelte";
import Checkbox from "./Checkbox.svelte";
import FlyoutShell from "./FlyoutShell.svelte";

let {
vendor,
Expand Down Expand Up @@ -203,14 +203,12 @@
}
}

function handleKey(e: KeyboardEvent): void {
if (e.key === "Escape") {
e.stopPropagation();
if (mode === "versions" && catalogKey === "advanced") {
backToModules();
} else {
onClose();
}
function onEscape(e: KeyboardEvent): void {
e.stopPropagation();
if (mode === "versions" && catalogKey === "advanced") {
backToModules();
} else {
onClose();
}
}

Expand All @@ -222,8 +220,14 @@
}
</script>

<div class="flyout-backdrop" role="presentation" onclick={onClose} onkeydown={handleKey} tabindex="-1"></div>
<div class="flyout glass-dialog" transition:fly={{ y: -8, duration: 160 }} style:--edge-color={accent} role="dialog" aria-label={headerTitle} tabindex="-1" onkeydown={handleKey}>
<FlyoutShell
{onClose}
{accent}
{onEscape}
ariaLabel={headerTitle}
backdropOpacity={0.45}
backdropBlur={3}
>
<header class="flyout-head">
{#if mode === "versions" && catalogKey === "advanced"}
<button class="flyout-back" onclick={backToModules} aria-label={$t("component.flyout.backToModules")}>
Expand Down Expand Up @@ -362,27 +366,9 @@
<span class="foot-count">{$t("component.flyout.versionCount", { shown: filtered.length, count: releases.length })}</span>
</footer>
{/if}
</div>
</FlyoutShell>

<style>
.flyout-backdrop {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.45);
backdrop-filter: blur(3px);
z-index: 220;
}
.flyout {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: min(720px, 94vw);
max-height: 84vh;
display: flex;
flex-direction: column;
z-index: 221;
}
.flyout-head {
padding: 18px 52px 14px 20px;
border-bottom: 1px solid var(--border);
Expand Down
Loading
Loading