Releases: TheHolyOneZ/ZGameLib
ZGameLib (1.2.1)
[1.2.1] — 2026-04-04
Added
- Ubisoft Connect platform — added alongside Steam, Epic Games, and GOG as a built-in default platform
- Ubisoft auto-scan — reads
HKLM\SOFTWARE\WOW6432Node\Ubisoft\Launcher\Installs\{id}for install directories andHKLM\...\Uninstall\Uplay Install {id}for display names; exposed asscan_ubisoft_gamescommand and included inscan_all_games - Ubisoft launch —
launch_ubisoft_gamecommand opensubisoft://launch/{gameId}viaopen::that(); playtime tracking follows the same directory-based process monitoring used by Steam and Epic ubisoft_game_idcolumn — new nullable TEXT column in thegamestable; live migration viaALTER TABLEapplies to existing installs- Ubisoft platform badge —
UbisoftIconSVG added toIcons.tsxandPlatformBadge.tsx - Ubisoft sidebar filter — Ubisoft Connect entry with live game count in the Platforms section of the sidebar
- Ubisoft spin wheel filter — "Ubisoft" quick-filter chip added to the Game Spin page; platform dot colored
#60a5fa - Ubisoft stats support —
ubisoftfield added toLibraryGrowthEntry; library growth chart, platform breakdown, and Year in Review donut all track Ubisoft games separately - Ubisoft filter builder — Ubisoft Connect available as a platform value in the advanced filter builder
- Ubisoft game detail — Game ID shown in the info section of the detail panel when
ubisoft_game_idis set - Collapsible library sections — Play Next and Recently Played rows can be collapsed by clicking their header; a chevron rotates to indicate state; collapsed state persists in
localStorageacross sessions
Changed
VALID_PLATFORMSinsettings.rsextended to include"ubisoft"PlatformTypeScript union extended to"steam" | "epic" | "gog" | "custom" | "ubisoft"useLaunchGamehook routes Ubisoft games tolaunch_ubisoft_gamebefore falling back to direct exe launch
ZGameLib (1.2.0)
[1.2.0] — 2026-03-21
Added
- Game-already-running detection — launching while another session is active shows a confirm dialog with "Stop & Launch" to switch games instantly
- Live "Playing" indicator — play button shows a pulsing green "Playing" state while a game session is active
- Stop & Launch — terminates the running game process (
TerminateProcess) before launching the new one; all launch entry-points consolidated into a shareduseLaunchGamehook
Fixed
- ZipSlip path traversal —
install_bepinexandinstall_melonloadernow validate each ZIP entry path component, rejecting any..or rooted segments;canonicalizedouble-check against base directory - Scan button broken —
GameGridnow callsuseScan()at component level; the "Scan Steam / Epic / GOG" empty-state card correctly triggers the scan hook instead of a dead dynamic import - Dual game state divergence —
game-session-endedlistener inAppBehaviornow callsqc.invalidateQueriesrather than manually replacing store state; TanStack Query is the single source of truth - Soft-delete UNIQUE constraint —
steam_app_idandepic_app_nameUNIQUE column constraints replaced with partial unique indexes (WHERE deleted_at IS NULL); live migration rebuilds existing installs automatically - IGDB token waste —
fetch_igdb_metadatanow caches the OAuth access token inIgdbTokenStatewith a 60-second expiry buffer; subsequent lookups reuse the cached token instead of requesting a new one per call - Unrestricted filesystem write —
save_filecommand validates the resolved canonical target path against an allowlist of safe directories (APPDATA, LOCALAPPDATA, Documents, Desktop, Downloads, OneDrive); writes outside these directories are rejected - Missing transactions —
reorder_gamesandbatch_update_gamesnow wrap their multi-statement loops in a single SQLite transaction for atomicity and a ~10× throughput improvement - Game tracking fallback — tracking state no longer resets instantly when a Steam/Epic game has no install directory or exe path; fallback tracker keeps the session active until manually stopped
v1.0.0 — The Milestone Release
v1.0.0 is focused on Polish & Discovery — making ZGameLib feel welcoming from
the very first launch. Three major features land alongside a set of smaller
quality-of-life additions.
Download and more info at https://zsync.eu/zgamelib/
─────────────────────────────────────────────────────────────────────────────
🎬 Interactive Onboarding Tour
The signature 1.0 feature. On first launch, pick a tour mode and ZGameLib
walks you through the entire UI automatically.
- Three modes: Quick Start (~2 min, 10 steps), Standard (~5 min, 23 steps),
Deep Dive (~10 min, 37 steps) - Animated SVG spotlight overlay with glow ring and pulse effects; auto-scrolls
off-screen targets before measuring - Live demonstrations — context menu actually opens on a game card, scan
dropdown expands and highlights its options, game detail tabs switch, settings
page scrolls to each section - Full toolbar coverage: dedicated steps for Bulk IGDB, Remove Duplicates, Scan
Log, HLTB, and IGDB buttons in the detail panel - Auto-scan runs when the tour starts so your library populates while you watch
- Chapter system in Deep Dive (37 steps across 9 labeled chapters)
- Cinematic finale: fly-through purple heart animation + "Made By TheHolyOneZ"
credit with particle effects - Keyboard navigation: Enter/→ advance, ← back, Escape skip
- Re-triggerable anytime from Settings → About
- Onboarding reset on upgrade: users coming from 0.x get the tour automatically
on first 1.0.0 launch
📅 Year in Review (/wrapped)
Annual gaming recap accessible from the sidebar or the W shortcut.
- Year selector defaulting to current year; only shows years with session data
- 9 animated stat cards: total hours, most played, top rated, new additions,
completed, longest session, busiest month (12-bar chart), platform split
(SVG donut), and games explored - Empty state shown when no session data exists for the selected year
💡 Smart Play Recommendations
"Play Next" strip on the library page surfacing backlog games you're most
likely to enjoy.
- Frontend-only algorithm: taste profile = tags + genre of all games rated ≥ 8;
score = matching tags ×2 + genre match ×3; tiebreak by time in library - Each card shows "X matching tags" so you know why it was suggested
- Per-card dismiss (session only, no persistent blacklist)
- Only activates when the library has ≥ 3 games rated ≥ 8 and ≥ 3 eligible
suggestions; otherwise hidden entirely
─────────────────────────────────────────────────────────────────────────────
Also in 1.0.0
What's New Modal
- Shown automatically on first launch after an update
- "What's New" button in Settings → About to re-open it anytime
Contextual Empty State
- When the library is empty and onboarding is complete: three action cards
(Scan for Games, Add Manually, Browse Steam) - First-run users still see the original illustration + Take the Tour CTA
New Keyboard Shortcuts
- S — focus the Scan Games dropdown
- W — navigate to Year in Review
- H — toggle visibility of hidden/duplicate games
- 1–9, 0 — quick-rate the open game detail (0 = 10 stars)
- Ctrl+Z — undo last game deletion
- All new shortcuts added to the ? overlay
Changed
- Default grid columns changed from 4 to 6
─────────────────────────────────────────────────────────────────────────────
Full Changelog (1.0.0)
Interactive Onboarding Tour (F-050)
- Tour Mode Selector: full-screen mode picker on first launch with three
options; skip option marks onboarding complete without running tour - Cinematic spotlight overlay: SVG mask-based cutout, accent glow ring, double
pulse rings, radial gradient highlight; auto-scrolls off-screen targets - Live interactive demonstrations: scan dropdown expands, context menu appears
via synthetic right-click, Add Game modal opens, game detail opens with IGDB
and HLTB buttons highlighted, settings page scrolls to each section - Full toolbar coverage: dedicated steps for Bulk IGDB Metadata, Remove
Duplicates, and Scan Log buttons - Auto-scan on first launch in every tour mode
- Chapter system (Deep Dive): 37 steps across 9 labeled chapters with gradient
indicator strip - Tour card: glass-morphism floating card with gradient accent header, step
counter badge, animated position transitions, smooth content crossfade - Cinematic finale: fly-through purple SVG heart, "Made By TheHolyOneZ" credit,
particle effects, phased reveal - Keyboard navigation: Enter/→ advance, ← back, Escape skip
- afterRender hook system: context menu opens after spotlight is stable to
prevent scroll-triggered dismissal - Persistent state: onboarding_completed and onboarding_tour_mode saved to
settings; backend event and frontend check ensure reliable first-launch trigger - Onboarding reset on upgrade: users from 0.x get the tour on first 1.0.0 launch
- Restart tour: "Take the Tour" button in Settings → About
- data-tour attributes on all targeted elements
Year in Review / Wrapped (F-051)
- New /wrapped page accessible from sidebar (between Spin and Settings) and W shortcut
- Year selector: dropdown defaulting to current year; only years with sessions shown
- Stat cards (staggered Framer Motion entry):
Hero: total hours across total sessions
Most Played: cover, name, platform badge, total hours
Top Rated: highest-rated game played or added in the year
New Additions: games added to the library this year
Completed: games marked completed this year
Longest Session: formatted as hours + minutes
Busiest Month: month name with 12-bar chart
Platform Split: SVG donut chart
Games Explored: distinct games with at least one session - Empty state when no session data exists for the selected year
- Rust backend command get_year_in_review(year) with multi-join SQLite queries
Smart Play Recommendations (F-052)
- Recommendations strip: collapsible horizontal scroll row below Pinned, above
grid; up to 5 personalized suggestions - Taste profiling: frontend-only; scores status=none/backlog games with
playtime_mins < 30; matching tags ×2 + genre match ×3; tiebreak by time in library - Hint label: each card shows "X matching tags"
- Per-card dismiss: hides for session, resets on reload
- Threshold guard: only renders when ≥ 3 games rated ≥ 8 AND ≥ 3 eligible suggestions
What's New Modal (I-047)
- Auto-shown on first launch after an update
- Rust compares last_seen_version to CARGO_PKG_VERSION; emits show-whats-new event
- "View full changelog" link opens GitHub releases
- Manual trigger via Settings → About
Empty State Enhancement (I-048)
- Contextual empty state: three action cards when library empty + onboarding complete
- First-run users see original illustration-based empty state
Keyboard Shortcut Additions (I-049)
- S: focus Scan Games dropdown
- W: navigate to /wrapped
- H: toggle hidden/duplicate game visibility
- 1–9, 0: quick-rate open game detail (only when panel is open, never in fields)
- Ctrl+Z: undo last game deletion
- All added to ? overlay
Changed
- Default grid columns: 4 → 6 (Rust default and frontend fallback)
- Wrapped page added to sidebar between Spin and Settings
- Settings → About: added "Take the Tour" and "What's New" buttons
ZGameLib (0.9.0 - Stable)
[0.9.0] — 2026-03-20
Added
Uninstalled Steam Games (F-017)
- Pull Uninstalled Steam Games — import all owned but uninstalled Steam games into the library via Steam Web API (
IPlayerService/GetOwnedGames) - "Not Installed" badge shown on game cards (top-left, next to platform badge) and in list-view rows for any uninstalled game
- Install via ZGameLib — clicking launch/play on a "Not Installed" Steam game opens Steam's install dialog (
steam://install/{appId}) instead of running the game - Scan Games dropdown in the top bar — replaces the single Scan button with a split button/dropdown: "Scan Installed Games" (existing behavior) and "Pull Uninstalled Steam Games"
- Sidebar filter — "Not Installed" section appears in the sidebar when any uninstalled games are present, with a count badge; clicking it filters the library to show only uninstalled games
- Auto-pull on startup toggle in Settings → Behavior — "Pull uninstalled Steam games on startup"; requires Steam API Key and SteamID64 to be configured
- Scanner auto-detects installs — when a Steam scan runs and finds a game previously marked as "Not Installed", it automatically marks it as installed and populates the
exe_pathandinstall_dir - Database migration — new
not_installedcolumn (INTEGER, default 0) added togamestable via schema migration - Settings key —
include_uninstalled_steam(defaultfalse) persisted in app settings
Library Pagination (F-042)
- Pagination toggle in Settings → Appearance — off by default; when enabled the library splits into pages instead of one continuous scroll
- Page size selector — choose 12, 24, 36, 48, 60, or 100 games per page; selector only visible when pagination is on
- Pagination bar — prev/next arrows with numbered page buttons; smart ellipsis condensing for large page counts (e.g.
1 … 4 5 6 … 20); current page highlighted with accent gradient; appears below the grid in all view modes (grid, list, manual sort order) - Page resets to 1 automatically when search, filters, page size, or toggle change
- New settings keys:
pagination_enabled(defaultfalse),pagination_page_size(default24, clamped 6–200)
Steam Playtime Sync (F-013)
- Steam Playtime Sync in Settings → Integrations — enter your Steam API Key and SteamID64 to sync playtime from Steam; only increases local values, never decreases
- Sync button shows updated/skipped count in a toast
Idle Detection (F-014)
- Exclude idle time from playtime toggle in Settings → Behavior (default: on) — deducts time when the game isn't in focus for 5+ consecutive minutes; brief alt-tabs are ignored
Advanced Filter Builder (F-016)
- Advanced filter section in the sidebar — under a collapsible "Advanced" toggle below the Cover Art section
- Filter rules with field/operator/value — supports platform, status, rating, playtime, tags, date_added, is_favorite, has_cover
- AND / OR logic toggle between rules
- Add Rule and Clear All buttons; each rule has a remove button
Game Tracking Overhaul (I-046)
- Directory-based process tracking — all launchers (Steam, Epic, direct exe) now watch the game's
install_dirfor any running process instead of tracking a single spawned PID; handles multi-process games that previously recorded 0 playtime - Launcher stub support — games that start via a stub launcher no longer lose their session when the stub exits; the tracker maintains a grace window between process deaths so the real game process is picked up seamlessly
- install_dir fallback —
launch_gamenow fetchesinstall_dirfrom the database; if unset, derives it from the exe's parent directory; falls back to single-PID tracking only when no directory is resolvable - Directory-based idle detection —
exclude_idle_timenow checks whether the foreground window belongs to any process in the game directory, not just the originally spawned PID - ToolHelp process enumeration — replaced all
tasklistsubprocess calls withCreateToolhelp32Snapshot+QueryFullProcessImageNameW; process scans are now ~10× faster and create no console flicker - Steam / Epic install_dir tracking — Steam and Epic launch paths use the game's stored
install_dirfor directory tracking with a 300-second startup window; falls back to exe-name scanning only when install_dir is unavailable - Parallel process tracking prevention — direct launches now register in
ActivePidsalongside Steam/Epic, preventing duplicate session threads if the user clicks play twice before the tracking thread starts
Auto Grid Columns (I-042)
- "Auto" option for grid columns in Settings → Appearance — uses
repeat(auto-fill, minmax(180px, 1fr))to fill the available space automatically
Persistent Error Log (I-045)
- Copy Logs button in Settings → About — copies the last 200 error log lines to the clipboard; errors are written to
%APPDATA%\zgamelib\logs\app.logwith rotation at 1 MB (keeps last 3 files)
Changed
- Version bumped to 0.9.0
ZGameLib (0.8.0)
[0.8.0] — 2026-03-19
Added
Collections (F-020)
- Collections page — create, rename, delete, and describe collections; grid/list view toggle; search bar to filter by name
- Collection description — click-to-edit description/notes field per collection; stored in
collections.description(backwards-compatibleALTER TABLE) - Collection detail view — click a collection to see its games with search and grid/list toggle
- Right-click context menu on collections — rename / delete directly from the collections page
- Game context menu → Collections submenu — right-click any game anywhere → hover Collections → submenu shows all collections with checkmarks; click to add or remove; a game can belong to multiple collections simultaneously
- Remove from Collection — when viewing a collection's game list, right-click a game → orange "Remove from Collection" option
- Batch operations → Add to Collection — select multiple games →
+ Collectionbutton in theBatchActionBar→ pick a collection to add all selected games at once - Full export v3 — export now includes
collectionsandcollection_gamesarrays; import is fully backwards-compatible (#[serde(default)]); old v1/v2 exports still import correctly
IGDB Metadata (F-006)
- Settings → Integrations — IGDB Client ID and Client Secret fields with a clear 6-step setup guide: create a Twitch Developer app at
dev.twitch.tv/console, set OAuth Redirect URL tohttp://localhost, set Category to Other, copy Client ID and Client Secret - Fetch IGDB button in Game Detail — fetches genre, developer, publisher, and release year for the open game; populates description if empty
- IGDB metadata card in Game Detail Info tab — shown when any IGDB field is present; modern 2×2 flex-wrap grid with custom SVG icons (gamepad · code · building · calendar) for each field; adapts to any panel width
- (i) info button in metadata card header — hover or click reveals: "Data sourced from IGDB by game name. If another title shares a similar name, the wrong match may have been returned"
- Clear IGDB data button (trash icon) in metadata card header — removes all IGDB fields and sets an
igdb_skippedflag on the game igdb_skippedflag (ALTER TABLE games ADD COLUMN igdb_skipped INTEGER) — persisted in SQLite; once set, the bulk scan silently skips the game and the individual fetch button shows a confirmation dialog before proceeding- Bulk IGDB scan button (sparkle icon) in library Topbar — fetches IGDB data for every game that has none and is not flagged; spins while running with a live
X/Ycounter badge; reloads the game list on completion; if no credentials are configured shows an error toast pointing to Settings → Integrations
Notes — Markdown Preview (I-026)
- Notes in Game Detail now have a per-note preview/edit toggle (eye and pencil SVG icons)
- Preview renders full Markdown using
react-markdown+rehype-sanitizewith prose styling
Tags — Undo Deletion (I-012)
- Removing a tag in Game Detail now starts a 5-second countdown instead of deleting immediately — tag shows strikethrough at 45% opacity with a
↩undo indicator - Click the pending tag to cancel the deletion; after 5 seconds the removal is committed
Statistics — Library Growth Chart (F-036)
- Library Growth section on the Stats page — stacked bar chart showing how many games were added per calendar month, bars colored by platform using the existing
PLATFORM_COLORS_HEXpalette, legend at the bottom; data comes from the newget_library_growthbackend command
Custom Theme Creator (F-041)
- Create Theme button in Settings → General → Theme — opens an inline editor to build a fully custom theme from scratch
- Accent color picker — native color input plus 14 preset swatches; HSL shade generation auto-derives all 8 accent levels (200–900) with a live shade strip preview
- Background and sidebar color pickers — 10 dark preset swatches; sidebar auto-derived from background with manual override
- Live preview — the entire app updates in real time as colors are adjusted; a miniature sidebar+content mockup is also shown in the editor
- Save, edit, delete — custom themes appear alongside built-in themes with hover controls (pencil to edit, trash to delete); stored in the
custom_themessettings key as JSON - Theme utility module (
src/lib/theme.ts) — shared HSL math, shade generation, and CSS variable injection used by both Settings and Layout
Unsaved Settings Guard (I-029)
- Dirty detection — Settings page tracks whether any field has changed since the last save using a JSON snapshot comparison
- Navigation interception — Sidebar and Command Palette check the dirty flag before navigating away from Settings; if unsaved changes exist, navigation is blocked and a modal appears
- Unsaved Changes modal — glass-morphism dialog with accent-colored info icon, spring animation, and two options: Discard (reverts to saved state and navigates) or Save & Leave (persists changes then navigates)
Fixed
- Collections submenu click closes context menu — root cause:
CollectionsSubmenuwas rendered viacreatePortaltodocument.body, which placed it outsidemenuRef's DOM tree; the outside-clickmousedownhandler detected submenu clicks as "outside" and closed the menu before the button registered. Fix: restorecreatePortal(needed sofixedpositioning works correctly outside thebackdrop-filterparent), adddata-ctx-submenu="true"attribute to the portaled div, and update the handler to checktarget.closest("[data-ctx-submenu]")before closing - Collections submenu not appearing on hover — root cause:
CollectionsSubmenuwas afixedchild of the main menu div which usesbackdrop-filter(viaglass-strong);backdrop-filtercreates a new CSS containing block forfixedchildren, so the submenu's viewport coordinates were applied relative to the parent, placing it off-screen. Fix: restoredcreatePortalso the submenu renders outside the backdrop-filter ancestor - IGDB metadata card swallowed / not resizing — removed
overflow-hiddenfrom the card wrapper (was clipping the tooltip and preventing content growth); replacedgrid grid-cols-2withflex flex-wrapwith percentage widths so cells reflow gracefully at any panel width; addedbreak-wordsto values
Changed
- Version bumped to 0.8.0
- Database additions (all backwards-compatible
ALTER TABLE):games.genre TEXTgames.developer TEXTgames.publisher TEXTgames.release_year INTEGERgames.igdb_skipped INTEGER NOT NULL DEFAULT 0- New table
collections (id, name, created_at, description) - New table
collection_games (collection_id, game_id)with cascade delete - New setting
custom_themes— JSON array of user-created themes (id, name, accent, bg, sidebar)
ZGameLib
Fixed
- Process tracking fallback for Steam/Epic — when the launched game process is never found within the polling window, the session is now properly closed with 0 minutes and the UI restores correctly; previously the background thread would silently exit leaving the app in a tracking state
- Cover cache race condition — multiple components mounting simultaneously for the same game no longer trigger parallel fetches; a per-key in-flight deduplication guard prevents redundant requests; cache capacity raised from 200 to 500 entries
- Input length validation —
nameis capped at 255 characters,descriptionat 10,000,tagslimited to 100 items each ≤ 50 characters; the backend rejects out-of-range values with a descriptive error rather than storing them silently - No keyboard navigation on game cards — cards now have
tabIndex={0},role="button", and respond to Enter / Space, making the library navigable without a mouse - Focus not trapped in modals — Tab key could previously escape the Game Detail panel and Cover Search modal; a keyboard trap now constrains focus to the active overlay while it is open
- PowerShell injection in icon extraction — icon extraction no longer interpolates file paths into the PowerShell command string; paths are now passed via
ZGAMELIB_EXE_PATHandZGAMELIB_DEST_PATHenvironment variables, eliminating a potential command injection vector - Empty catch blocks in GameListRow — launch and open-folder errors were silently swallowed; both now display an error toast with the failure message
- Cover cache memory leak — the in-memory cover URL cache now uses LRU eviction (max 200 entries); previously it grew unboundedly for large libraries
- Non-atomic cover downloads — cover images are now written to a
.tmpfile first and renamed atomically, preventing corrupt partial writes on crash or error - Folder scan walk limit — the custom folder scanner now hard-caps at 10,000 file system entries to prevent runaway walks on deeply nested or large drives
- Exe icon not refreshing after game update — the icon cache key now includes the file's modification time (
mtime); replacing a game's exe now shows the new icon on next launch - Game detail shows stale data after switching games — the detail panel now invalidates and refetches game data when
selectedGameIdchanges, preventing stale reads between rapid game switches - Auto-scan not triggering on startup —
scanwas missing from theuseEffectdependency array inLayout.tsx; auto-scan now fires reliably on app start when enabled - Cover placeholder constant duplicated —
COVER_PLACEHOLDERwas defined independently inRecentlyPlayedandPinnedRow; both now import from the shared@/lib/utilsmodule - Missing aria labels on scan-related topbar buttons — scan log toggle and remove-duplicates button now have
aria-labelattributes for screen readers
Added
- Drag-and-drop reordering — a new "Custom Order" sort option enables Framer Motion
Reorderdrag-and-drop for the game grid; dragging a card updatessort_orderin the database for all affected games in a single batch transaction; order persists across sessions - Time-to-beat estimates (HLTB) — a clock icon button in the Game Detail panel fetches HowLongToBeat data for the game (main story and completionist hours); results are cached in the database (
hltb_main_mins,hltb_extra_mins) and displayed in the stats grid - Custom fields — users can define arbitrary key/value metadata per game (text values); field editor in the Info tab with add/edit/delete; stored as a JSON map in the database under
custom_fields - Global keyboard shortcuts —
?toggles a keyboard shortcut help overlay;Nopens the Add Game modal;Ftoggles favorite on the currently open game;Escapecloses the detail panel or any overlay; globalkeydownlistener inLayout.tsx - Portable mode — if a file named
portable.flagexists next tozgamelib.exeat startup, the database and settings are stored in the same directory as the exe instead of%APPDATA%\zgamelib; useful for USB drives or self-contained installs - Duplicate removal confirmation — clicking "Remove Duplicates" in the topbar now shows a confirm dialog listing how many games will be hidden before acting; previously it applied the change instantly with no warning
- Cover lightbox — clicking the game cover image in the detail panel now opens a full-size lightbox overlay instead of jumping straight to the cover search modal; a separate "Change Cover" button on the hover overlay handles cover replacement
- Quick rate from game card — a row of 10 rating buttons appears at the bottom of a game card on hover, allowing ratings to be set without opening the detail panel; the active rating is highlighted
- Empty library illustration — the first-run / empty-library state now shows an animated gamepad SVG illustration with orbiting sparkle dots and an accent glow, replacing the previous blank panel
- Trash bin / soft delete — deleting a game moves it to trash (
deleted_attimestamp) instead of hard-deleting it; restore or permanently delete from a new Trash section in Settings → Data; "Empty Trash" purges all at once — backwards-compatible viaALTER TABLE - Pinned games row — right-click any game → Pin to show it in a dedicated "Pinned" strip at the top of the Library; pin state persists to the database
- Session history — each game launch records a session row (
started_at,ended_at,duration_mins) in a newsessionstable; view the last 50 sessions per game in a new History tab in the Game Detail panel - Bulk auto-fetch missing covers — new "Fetch Missing Covers" button in Settings → Data; fetches covers for all games that have no cover art (Steam games via CDN, others via name search); reports updated/failed counts
- Platform badge component — dedicated
PlatformBadgecomponent with platform icons (Steam / Epic / GOG / Custom) used consistently across GameCard, GameListRow, GameDetail, and Spin pages - Collapsible sidebar — sidebar can be collapsed to a 62 px icon-only strip; collapses with a spring animation; collapse toggle moved to the header (always visible); state persists across sessions via
localStorage - Sort direction toggle — ascending/descending sort button added to the PageSearch bar
- Theme hover preview — hovering a theme button in Settings instantly previews it; the previous theme restores on mouse-out if not confirmed
- Delete option in right-click context menu — "Delete" entry with red styling and a visual divider separator added to the
GameContextMenu - Weekly playtime goal (
GoalBar) — collapsible goal widget at the top of the Library page; set a target in hours, animated progress bar shows current week's playtime; "Goal reached!" state with green color; persists vialocalStorage - Search scope toggle — small
A/A+toggle embedded inside the search input; switches between searching game name only vs. name + description - Cover art filter — "Has Cover" and "Missing Cover" filter buttons in the sidebar under a dedicated Cover Art section (consistent with Platform/Status styling); shows counts; replaces the old cluttered buttons in the search bar
- Export library as CSV — new button in Settings → Data exports the full library as a
.csvfile with proper quoting; fields: id, name, platform, status, rating, playtime_mins, date_added, is_favorite, tags - Export Filtered — new button in Settings → Data exports only the currently visible/filtered games as JSON; button label shows the active count (e.g. "Export Filtered (12)")
- "Saved ✓" flash indicator — editing a game's name, description, or rating now flashes a brief "Saved ✓" indicator in the detail panel's tab bar using
AnimatePresence - Scroll-to-top button — a floating ↑ button appears after scrolling 400 px on the Library, Favorites, and Recently Played pages; smooth animated entrance/exit
- Tab counts in game detail — Screenshots and History tab labels now show live counts (e.g. "Screenshots (6)", "History (3)")
- Description expand/collapse — long game descriptions are truncated at 200 characters with a "Show more / Show less" toggle in the game detail Info tab
- Cover search empty state — the cover search modal now shows "No covers found for 'X'" with a helpful hint ("Try a shorter name or remove subtitles") instead of a blank panel before searching
Changed
- Version bumped to 0.6.0
delete_gamecommand is now a soft delete; hard delete ispermanent_delete_game- Sidebar collapse toggle moved from the bottom to the header row (always accessible regardless of window height)
- About section in Settings spans full grid width (was half-width like other cards)
- Cover art filter and search scope controls moved out of the search bar into proper locations (sidebar and inside search input respectively) for a cleaner topbar
ZGameLib (0.7.0)
[0.7.0] — 2026-03-18
Fixed
- Tiny settings labels — all
text-[10px]instances in Settings replaced withtext-xsfor readable, consistent label sizing throughout the settings page - Invalid platform/status saved silently —
update_gamenow validatesplatform(must besteam,epic,gog, orcustom) andstatus(must benone,backlog,playing,completed,dropped, oron_hold) and returns a descriptive error instead of persisting garbage values - Import library accepts bad data —
import_librarynow validates each game before inserting: skips entries with empty or >255 char names, ratings outside 0–10, and unrecognised platform strings - Launcher threads run forever — background process-polling threads for Steam/Epic/GOG/custom now have a hard 86 400-second (24-hour) cap; previously a game that never launched would leave a thread running indefinitely
- Batch status dropdown invisible on dark background — the floating
BatchActionBarstatus select now forces a dark background (bg-[#1a1825]) with matching dark option elements so the text is readable on all platforms - Batch rating input shows number spinners — the rating field in the batch action bar no longer renders browser-native up/down spinners; uses CSS to suppress them on all engines
- Batch rating not clamped — entering a value outside 1–10 in the batch rating field is now rejected on every keystroke; the input clamps to
[1, 10]immediately - Export missing sessions and notes —
export_librarypreviously only exported games; the JSON export now uses a v2FullExportenvelope ({ version, games, sessions, notes }) so all play history and notes are preserved;import_librarydetects v1 (plain array) vs v2 automatically and restores sessions and notes, skipping any whosegame_idno longer exists - Playtime not recorded for short sessions — three root causes fixed: GOG/custom process polling interval reduced from 30 s to 5 s; Steam/Epic
game_starttimer now begins only after the target PID is found (not at launch time, which inflated playtime by up to 3 minutes); sessions ≥ 30 seconds are now saved (previous threshold was > 0 minutes, silently dropping any session under 1 minute) - Game card action buttons invisible on light covers — overlay buttons changed from
bg-black/40tobg-black/70with an explicitborder border-white/10 shadow-lg, ensuring they are always legible regardless of cover brightness - Fire icon appears squished —
FireIconcompletely redrawn as a proper three-layer SVG flame; size on game cards raised from 12 px to 18 px, and on list rows from 10 px to 15 px - Spin wheel inner hub dominates the empty wheel — removed the large dark center circle entirely; pie slices now extend from the exact center point, giving the wheel a clean look even with a single entry
- Spin wheel empty state spinner icon — removed the decorative rotating icon from the empty state overlay; the empty state now shows text only
- Spin winner card pushes "Spin the Wheel" button upward — the right panel is now split into a fixed top section (wheel + button,
shrink-0) and a separate scrollable bottom section (winner card + history); the button never moves regardless of how much content appears below it - Directory traversal in cover file copy —
set_game_covernow rejects symlinks (viasymlink_metadata), validates the file extension against an allowlist (jpg,jpeg,png,webp), and checks magic bytes before copying; previously any file path including sensitive system files could be copied into the app data directory - N+1 query pattern in scanner — Steam, Epic, and GOG scan loops previously fired one
SELECTper discovered game to check for duplicates; each scanner now bulk-fetches all existing IDs into aHashMap/HashSetbefore the loop and does in-memory lookups, reducing scan overhead from O(n) queries to a single query per platform - Playtime chart renders as a solid block — the "Last 12 Weeks" chart used
preserveAspectRatio="none"on its SVG, which stretched all bars to fill the full container width and merged them into an indistinguishable block; replaced with a div-based flexbox chart where bars grow from the bottom, labels show every other week, and a Framer MotionscaleYentrance animation staggers each bar in
Added
- Command palette (
Ctrl+K) — centered overlay with a fuzzy search that matches any game by name; results show cover thumbnail and platform badge; keyboard navigation (↑ ↓ Enter Escape); also surfaces six quick actions (Add Game, Library, Favorites, Stats, Spin, Settings);Ctrl+Kagain or Escape closes it - Batch multi-select — hold-click the checkbox that appears on any game card (top-left, visible on hover) or list row to build a selection; a floating
BatchActionBarslides up from the bottom showing the selection count, a status dropdown, a rating input, an "add tag" field, and a Delete button; all changes apply to every selected game in one transaction; × clears the selection - Weekly playtime chart — new "Playtime — Last 12 Weeks" SVG bar chart on the Stats page; each bar represents one ISO week; bars animate in on mount; hovering a bar shows the exact playtime and week label in a tooltip; data comes from the
sessionstable - Lowest Rated section — Stats page shows a ranked list of up to 5 games rated ≤ 4 with their scores highlighted in red
- Most Neglected section — Stats page shows up to 5 games with zero recorded playtime, sorted by how long they have been in the library, with a "Added X days ago" label
- Screenshot lightbox navigation — left / right chevron buttons let you page through all screenshots without closing the lightbox; an "X / Y" counter in the top-right corner tracks position;
ArrowLeft/ArrowRightkeyboard support added; navigation wraps around at both ends - Playtime reminder on startup — if Playtime Reminders is enabled in settings, the app checks on launch for the game you have not played in the longest time (minimum 30-day threshold); emits a toast-style notification with the game name and number of days since last session
- Window position and size memory — the app saves the window's last position (
window_x,window_y) and size (window_width,window_height) to the database on close and restores them exactly on next launch; only restores if coordinates are on-screen (≥ 0) - Playtime Reminders toggle — new toggle in Settings → Behavior; persisted as
playtime_remindersin the settings table; defaults to enabled - "Game Started" play button confirmation — after clicking Play in the game detail panel, the button transitions to a green "Game Started" state with a checkmark for 3 seconds using
AnimatePresence, giving clear visual feedback that the launch command was sent
Changed
- Sidebar active link now has a clear left-edge accent bar (
border-l-2 border-accent-500) plus a subtle background fill and bold white text; previously only the text color changed on the active route - Fire icon on high-rated games now pulses with a Framer Motion breathing animation (scale 1 → 1.2 → 1, 2-second loop) on both game cards and list rows
- Game card hover overlay buttons now stagger in (opacity 0→1, y 10→0) with a 50 ms per-button delay instead of appearing all at once
- Rating buttons on game cards moved above the game title (previously rendered below the title, partially obscured by the status badge)
- Spin wheel clock tick marks removed from the static overlay SVG; the outer ring and pointer triangle are kept; the wheel looks cleaner with no decorative minute-hand lines
- Version bumped to 0.7.0
ZGameLib(0.5.0)
Update README.md