All notable changes to Logia will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- Books API integration: Added two new providers for book media enrichment, following the existing dual-provider pattern (primary with key + secondary without key):
- Google Books (key required —
api_key_google_books): Primary provider for modern books. Rich metadata including title, authors, publisher, publish date, description, page count, categories, and multiple cover image resolutions. Free API key via Google Cloud Console (1000 req/day default quota). Rate-limited at 5 req/s. - Open Library (no key): Secondary/fallback provider from the Internet Archive. 20M+ titles with excellent coverage of older books (1800s–1970s). Covers fetched via
covers.openlibrary.org. Rate-limited at 1 req/s (per their low-volume usage policy; the existingLogia/{version}User-Agent header satisfies their identification requirement). - Both providers map authors to credits (role: "Author"), page count to the
durationfield, and categories to genres.
- Google Books (key required —
- Board games API integration (BoardGameGeek): Added BoardGameGeek XMLAPI2 as the first provider for the new
board_gamemedia type. BGG is the canonical board game database (100k+ titles) with designers, artists, publishers, mechanics, categories, player counts, and playing time.- Note on authentication: As of July 2nd, 2025, BGG requires application registration and a Bearer token (previously anonymous access was allowed). The token is configured via
api_key_bggin Settings → API Keys and obtained from https://boardgamegeek.com/applications. Sent asAuthorization: Bearer {token}header on every request. - XML parsing: BGG returns XML (not JSON). Added
quick-xml = "0.36"as a Rust dependency for streaming XML parsing of search results, thing details, and batch thumbnail lookups. - Search thumbnails: BGG's
/searchendpoint does not return thumbnails. A secondary batched/thing?id={id1},{id2},...call fetches thumbnails for the top 5 search results (1 additional grouped request). - Maps designers/artists/publishers to credits, categories+mechanics to genres, and playing time (minutes) to the
durationfield. Rate-limited at 1 req/s (BGG's rate limits are opaque and strict).
- Note on authentication: As of July 2nd, 2025, BGG requires application registration and a Bearer token (previously anonymous access was allowed). The token is configured via
- Cross-platform builds (Linux & macOS): Logia is now built and released for Linux and macOS in addition to Windows. The
ReleaseGitHub Actions workflow (.github/workflows/release.yml) now runs three parallel jobs on everyv*tag push:- Linux (
ubuntu-22.04): produces.deb,.AppImage, and.rpmpackages. The AppImage is the recommended format for Linux users — it is the only Linux bundle that supports the in-app auto-updater (.deb/.rpminstalls must be updated manually by downloading the new version). - macOS (
macos-latest): produces a single universal.dmgthat runs natively on both Intel and Apple Silicon (M1/M2/M3) Macs via--target universal-apple-darwin. The app is not code-signed with an Apple Developer certificate, so macOS Gatekeeper will show an "unidentified developer" warning on first launch — users can bypass it via right-click → Open (orxattr -dr com.apple.quarantine /Applications/Logia.app). The auto-updater works on macOS from this version onwards. - All three jobs upload their artifacts to the same GitHub Release, and
latest.json(the updater manifest) now includes entries forwindows-x86_64,linux-x86_64, anddarwin-universal(ordarwin-aarch64/darwin-x86_64).
- Linux (
- Cross-platform DB instance lock: the single-instance database lock (which prevents two Logia processes from using the same profile DB simultaneously) was previously implemented with Windows-only
kernel32LockFile/UnlockFilecalls andstd::os::windows::io::AsRawHandle, preventing compilation on Unix. Refactoredsrc-tauri/src/lib.rsto use conditional compilation:#[cfg(target_os = "windows")]keeps the existingLockFile/UnlockFilepath, while a new#[cfg(unix)]path usesflock(2)(LOCK_EX | LOCK_NBfor acquisition,LOCK_UNfor release) viastd::os::unix::io::AsRawFd. Behavior is identical on Windows; on Linux/macOS the lock is advisory (standard for Unix). - GitHub Pages landing page: added a static landing page at
docs/index.html(served via GitHub Pages athttps://cosmiir.github.io/Logia/) to present the app to new visitors. The page is a single self-contained HTML file (no external CSS/JS, no build step) styled after the Nebula theme (gradient background,#a855f7accent, glass-morphism cards, cosmic glow backdrop). Includes a sticky top nav with anchor links, a hero section with the logo and CTA buttons (Download latest release →releases/latest, View on GitHub), the full feature list (8 glass cards), a responsive 2-column screenshot gallery, installation instructions (direct download + from source), tech stack table, license and Ko-fi support sections. Optimized for SEO:<html lang="en">, descriptive<title>/<meta name="description">, Open Graph and Twitter Card tags,<link rel="canonical">,robots: index,follow,theme-color, and JSON-LDSoftwareApplicationstructured data (withscreenshotarray). All screenshots were converted from PNG to WebP at quality 95 (preserving fine text) intodocs/assets/, reducing total page weight from ~5 MB to ~1 MB. The logo (LOGIA.png) and favicon are also copied intodocs/assets/so the page is fully self-contained and indexable without depending on themainbranch's raw file URLs. English-only for now (multi-language via separate/fr/index.html+hreflangis planned for later once the English page generates traffic).
- IGDB Video Games Integration: Replaced TheGamesDB with IGDB (Internet Game Database via Twitch Developer API) for video game media search and enrichment.
- Supports Twitch OAuth2
client_credentialsauthentication with automatic in-memory token acquisition and caching. - Queries high-res cover artwork (
t_cover_big), search thumbnails (t_thumb), 720p screenshots and artworks (t_720p). - Extracts exact game developer name via
involved_companies(developer == true), genres, and estimated play duration (time_to_beat.normallyin hours). - Updated Settings → API Keys with dual inputs for IGDB (Client ID) (
api_key_igdb_client_id) and IGDB (Client Secret) (api_key_igdb_client_secret), linking directly to Twitch Developer Console (https://dev.twitch.tv/console).
- Supports Twitch OAuth2
- TheGamesDB Provider: Removed TheGamesDB integration in favor of IGDB for improved video game metadata, cover art quality, search speed, and developer resolution.
- Production Build API Image Previews (CSP): Added
https:andhttp:protocols to theimg-srcContent Security Policy directive insrc-tauri/tauri.conf.json. Previously, external API image URLs (from TMDB, RAWG, IGDB, etc.) pre-filled during media creation/editing were blocked by the production webview's CSP prior to saving, displaying missing file icons in gallery preview slots until saved to local disk. - CDN Referrer Policy: Added
<meta name="referrer" content="no-referrer" />toindex.htmlto ensure external CDN image hosts accept webview image requests without blocking custom Tauri scheme origins.
- Optional API enrichment: Logia is now offline-by-default with optional per-collection API enrichment. Users can map collections to external API providers to automatically pre-fill media details (title, creator, release date, status, synopsis, credits, and up to 8 gallery images) when creating or editing media entries. The feature is entirely optional — the app works fully offline without any API keys or provider configuration.
- 9 API providers across 6 media types:
- Films: TMDB (key required) + OMDb (key required)
- Series: TMDB (key required) + TVMaze (no key)
- Anime: Jikan/MyAnimeList (no key) + AniList (no key)
- Manga: Jikan/MyAnimeList (no key) + AniList (no key)
- Video Games: RAWG (key required) + TheGamesDB (key required)
- Music: MusicBrainz (no key) + iTunes (no key)
- Rust backend (
src-tauri/src/api/): All API calls are handled server-side usingreqwestwithrustls— no CSP modifications needed, API keys never exposed to the frontend, and images can be downloaded directly to local storage. Includes a per-provider rate limiter (token bucket) with 429 retry + exponential backoff (1s, 2s, 4s), respecting each provider's individual rate limits (e.g. Jikan 3 req/s, MusicBrainz 1 req/s). MusicBrainz User-Agent is dynamically set toLogia/{CARGO_PKG_VERSION}per their API policy. Provider modules parse responses flexibly withserde_json::Valuefor graceful handling of missing fields. - 4 new Tauri commands:
get_api_providers(list providers with key-availability status),search_api_media(parallel multi-provider search),get_api_media_detail(fetch full detail for a single result),download_api_image_to_media(download an image from a URL and save it to a media's gallery, reusing the existing WebP/EXIF/resize pipeline via the extractedsave_image_bytes_to_mediahelper). - CollectionEdit: New "API Enrichment" section with a multi-select of providers grouped by media type (Films, Series, Anime, Manga, Video Games, Music). Providers requiring an API key are grayed out with a "Key required" badge until the key is configured in Settings. Providers with a configured key show a "Key set" badge.
- Settings → API Keys: New tab for configuring API keys (TMDB, OMDb, RAWG, TheGamesDB) with save buttons and per-key status feedback. Includes the mandatory TMDB attribution notice required by their terms of service. An offline notice reminds users that enrichment is optional.
- ApiSearchModal: Debounced search modal (500ms) triggered by a search icon next to the title field in MediaCreate/MediaEdit (only shown when the selected collection has providers enabled). Results display thumbnails (base64-encoded from Rust), title, year, creator, and a source badge per provider. Selecting a result fetches the full detail and pre-fills the form fields; up to 8 images are downloaded and added to the gallery automatically after the media is saved. Users retain full control to edit all pre-filled fields.
- DB migration: Added
api_providersTEXT column (JSON array of provider IDs) to thecollectionstable. Migration is automatic on app launch. - i18n: Full English and French translations for all new UI strings (
settings.api.*,collectionEdit.apiEnrichment.*,apiSearch.*,mediaCreate.searchViaApi).
- 9 API providers across 6 media types:
- README: Updated from "100% offline, no external API" to "offline by default, with optional API enrichment" to reflect the new feature while reassuring users that the app remains fully functional without any internet connection.
- Image pipeline refactor: Extracted
save_image_bytes_to_mediafromupload_media_imageincommands/media.rsto share the core image processing logic (EXIF rotation, resize to max 1920px, WebP encode at quality 80) between the existing base64 upload path and the new API image download path. The function takes a&Connectionand&Pathdirectly instead ofState<'_, AppState>, making it reusable from non-command contexts.
- Onboarding (CreateProfile) content clipping at high Windows display scaling: at 150% Windows "Mise à l'échelle" (and similar zoom levels), the onboarding flow clipped its content — the "Suivant" button was half-cut on step 3 (Profile), and the bottom of step 4 (Personalization, from the "Display density" card downward) was invisible and unreachable. Root cause:
CreateProfilewrapped its content in a plain<div className="flex-1 flex flex-col p-8">with nooverflow-y-auto, whileAppShellenforcesoverflow-hiddenat the root — so any content exceeding the viewport height was silently clipped with no scrollbar. All other pages (Settings, Dashboard, Library, etc.) use theMainContentlayout component, which providesoverflow-y-auto,custom-scrollbar, andscrollbar-gutter: stable. RefactoredCreateProfileto useMainContentlike the rest of the app, bringing consistent scroll behavior, styled scrollbar, and stable layout (no width jump when the scrollbar appears/disappears). The decorative glow backdrop was switched fromabsolute inset-0tofixed inset-0so it stays centered in the viewport during scroll instead of drifting with the content.
- Silent (in-app) updates: the updater now installs new versions silently on Windows, with no installer window shown to the user (behavior similar to Discord). Previously, the updater used the default
passiveinstall mode, which displayed a separate NSIS installer window with a progress bar during installation. Switchedplugins.updater.windows.installModetoquietintauri.conf.jsonso the NSIS installer runs invisibly (/S /UPDATE): the user only sees the existing in-app progress bar inUpdateModalduring download, then the app exits, the installer overwrites the binary in the background, and the app relaunches automatically on the new version. No UAC prompt is shown. Also made the NSIS bundle install mode explicit (bundle.windows.nsis.installMode = "currentUser", install in%LOCALAPPDATA%) as a guard against regressions — thequietinstall mode cannot elevate admin privileges on its own, so acurrentUserinstall is required. SeeRELEASE.mdfor details.
- Auto-update system: Logia now checks for updates automatically on launch via
tauri-plugin-updater, using GitHub Releases as the update CDN (no dedicated server required). The updater fetches a signedlatest.jsonmanifest fromhttps://github.com/Cosmiir/Logia/releases/latest/download/latest.json(served by the GitHub CDN, no rate limit, no authentication), verifies the bundle signature against a public key embedded in the app, and offers to download and install the new version in-place before relaunching. The check runs asynchronously 800ms after app mount so it never blocks startup. When an update is available, a modal dialog offers four choices: Update now (downloads, installs, and relaunches automatically with a progress bar), Download manually (opens the GitHub release page), Ignore (dismisses for this session, reproposed on next launch), and Don't remind me for this version (persists the skipped version number tolocalStorage, suppressing the modal until a newer version is released). A manual "Check for updates" button is also available in Settings → About, which forces a check (ignoring any skipped version) and displays the result in the same modal. Addedtauri-plugin-updaterandtauri-plugin-process(Rust + npm) dependencies,useUpdateCheckhook,UpdateModalcomponent, i18n keys (update.*,settings.about.updates) in EN/FR, and a newReleaseGitHub Actions workflow (.github/workflows/release.yml) triggered onv*tags that builds the Windows NSIS installer + updater bundle, signs it withTAURI_SIGNING_PRIVATE_KEY, and uploadslatest.json+ installer + signature to the GitHub release viatauri-apps/tauri-action. SeeRELEASE.mdfor the release procedure and one-time signing key setup. Note: this is the first signed release — users on v1.0.5 or earlier must install v1.0.6 manually; all subsequent updates will be delivered automatically.
-
MediaCreate attachment sort order on import: when importing new attachments and reordering them via drag-and-drop before saving, the sort order was lost on first save. Root cause: new attachments were uploaded using
attachmentFilePathsRef(import order) instead ofform.attachments(form order after drag-and-drop), and thereorderAttachmentscall only covered existing (already in DB) attachments — newly uploaded ones kept their sequential DB insert positions. Fixed by deriving upload paths fromform.attachmentsin form order and including newly uploaded attachment IDs in the finalreorderAttachmentscall. -
MediaDetail attachments read-only: the attachments section in
MediaDetailallowed renaming and reordering files (drag-and-drop handle, rename button, rename input). These actions should only be available inMediaCreate/MediaEdit. Removed theSortableAttachmentcomponent (drag handle + rename + confirm) and replaced it with a simplerAttachmentItemthat only exposes the existing Read (CBZ/ZIP) and Download actions. Removed the associatedDndContext/SortableContextwrappers, thelocalAttachments/renamingId/renameValuestate, and thehandleStartRename/handleConfirmRename/handleCancelRename/handleAttachmentDragEndhandlers. Cleaned up now-unused imports (@dnd-kit/*,GripVertical,Edit2,Check,useQueryClient,tauriApi). -
Library horizontal scrollbar on scrollbar thumb drag: dragging the vertical scrollbar thumb in the Library caused a horizontal scrollbar (with a white-ish thumb) to appear at the bottom of the page. Root cause:
handleContentMouseDown(drag-to-select rectangle handler) excluded clicks on cards and interactive elements but not clicks on the scrollbar itself. A scrollbar click fires on the<main>element at an X position beyondclientWidth, so the handler created aselectionBoxdiv positioned outside the content bounds — extendingscrollWidthand triggering a horizontal scrollbar (whose thumb appeared white due to.custom-scrollbar:hover). Fixed by adding an early-return guard inhandleContentMouseDownthat comparese.clientX/e.clientYagainstcurrentTarget.clientWidth/clientHeight(which exclude the scrollbar) and bails out when the click lands in the scrollbar gutter. -
i18n (CustomDatePicker): the date picker was hardcoded in French (month/day names, placeholder "Sélectionner...",
dd/mm/yyyydisplay and parsing, Monday-first calendar). Replaced withuseTranslation()keys under a newdatePickernamespace,formatDateFr()for locale-aware display (dd/mm/yyyyFR /mm/dd/yyyyEN), locale-aware parsing (dd/mm/yyyyFR /mm/dd/yyyyEN), and Sunday-first week for EN. Impacts MediaCreate, MediaEdit, Library filters, ObjectiveCreate and ObjectiveFormModal. MadeformatDateFr()timezone-safe foryyyy-mm-ddinputs and addedgetCurrentLocale()/getFirstDayOfWeek()helpers.
- Library detailed view (list rows): the progression bar used a hardcoded
from-blue-500 to-purple-500gradient that didn't match any theme accent. Replaced with a theme-aware progress bar that adapts automatically to all 5 themes via CSS variables (--theme-accent,--theme-accent-dark,--theme-accent-rgb): Nebula (violet), Midnight (blue), Ember (orange), Forest (green), Arctic (cyan). The fill now uses a subtle same-hue gradient (accent-dark → accent), a glossy top highlight (vertical white→transparent overlay on the upper half), a soft accent glow (box-shadow: 0 0 6px rgba(accent, 0.45)), and the track usesrgba(accent, 0.15)with an inset shadow for a recessed look. Pure CSS, no extra DOM nodes.
- Library grid oscillation: at certain window widths, the media card grid oscillated between N and N+1 columns (e.g. 6→7→6→7), causing cards to grow/shrink and jump between 1 and 2 rows in an infinite loop. Root cause:
MainContentuseduseHasScrollbarto dynamically adjust right padding (pr-10→pr-[28px], -12px) when the scrollbar appeared. This width change causedgridTemplateColumns: repeat(auto-fill, minmax(...))to switch column count → height changed → scrollbar appeared/disappeared → width changed again → infinite loop. Fixed by replacing theuseHasScrollbar+ dynamic padding system withscrollbar-gutter: stable, which permanently reserves scrollbar space (6px) keepingclientWidthconstant regardless of scrollbar visibility. Right padding set topr-[34px](40px - 6px gutter = 40px visual). Also removed the now-obsoletemain.custom-scrollbar { margin-right: 3px }andmain.no-scrollbar { margin-right: 0 }rules fromglobal.css. - i18n (CollectionEdit): tracking display description used
.replace()to inject an HTML<span>string into a translated string, which React rendered as literal text (visible<span ...>tags). SplittrackingDisplayintotrackingDisplayPrefix,trackingDisplaySeparator, andcurrentSuffixkeys with proper JSX elements. Also fixed hardcoded French"actuel"→currentSuffixkey ("current" in EN). - i18n (CollectionEdit): hardcoded French
"Affiché dans les objectifs :"→objectivesDisplaykey. Hardcoded fallbacks'Consommer'and'médias'→t('common.consume')andt('common.media')in both the objectives display and capacity hint. - i18n (ObjectiveCreate): hardcoded French title
"Modifier l'objectif"/"Nouvel objectif"and subtitles → existingeditObjective/newObjectivekeys + neweditObjectiveSubtitle/newObjectiveSubtitlekeys. - i18n (ObjectiveFormModal, ObjectiveCard): hardcoded French fallback
'Consommer'→t('common.consume'). - i18n (GravityMarkdownEditor): hardcoded French strings in the media link/mention tool — toolbar button title
'Média'and hint'Lier un média (Ctrl+M)', and footer keyboard hintsnaviguer,sélectionner,annuler— replaced withi18next.t()calls using newmediaMentionTitle,mediaMentionHint,navigateHint,selectHint,cancelHintkeys. - i18n (MediaDetail): hardcoded French
"Modifier"on the edit button →t('media.edit')(existing key). Hardcoded fallback'Durée totale'→t('mediaDetail.duration')(existing key). Hardcoded"Passer"and"Mettre à jour"on the manga reader progress prompt → newmangaReader.skipandmangaReader.updatei18n keys (EN/FR). - i18n (Stats): hardcoded French strings in
PeriodSelector("Toute la période","Période perso.") andCollectionFilter("Toutes les collections") →t('stats.allTime'),t('stats.customPeriod'),t('stats.allCollections')i18n keys (EN/FR). - i18n (Notifications): all notification titles and messages in
notificationRules.tswere hardcoded in French (e.g."Cette œuvre est en cours depuis plus de 30 jours sans progression."). Replaced all 7 notification rule strings + monthly report (including month names) withi18next.t()calls using newnotifications.rules.*keys with interpolation params ({{title}},{{progress}},{{days}},{{count}},{{month}},{{year}},{{completed}},{{abandoned}},{{rating}}) in EN/FR. Also added missingnotifications.nonekey and fixed hardcoded'Dashboard'label inSharedHeader.tsx→t('navigation.dashboard'). - i18n (Notifications rendering): notification titles and messages were generated by the Rust backend with hardcoded French strings (e.g.
"{} stagne","Cette œuvre est en cours depuis plus de 30 jours sans progression.") and stored in the database, ignoring the app's active language. The frontend now renders notification titles/messages via i18n at display time usingnotification.type+notification.data(JSON), via a newgetNotificationDisplay()helper innotificationConfig.ts. This helper mapssnake_casenotification types tocamelCasei18n keys (stagnant_media→stagnantMedia, etc.) and passes interpolation params from thedatafield. Applied to bothNotifications.tsx(full page) andSharedHeader.tsx(bell dropdown). Backend (notifications.rs) enriched:stagnant_mediaandwaiting_medianow compute actual days stagnant/waiting via SQLjulianday()and includetitle+daysindata;objective_deadlinecomputes realdays_until_endfromend_dateinstead of hardcoded 7;objective_achievedincludescount;monthly_reportincludescompleted,abandoned,ratingshortcuts. i18n messages updated:stagnantMedia.messageandwaitingMedia.messagenow use{{days}}instead of hardcoded "30 jours"/"90 jours". - i18n (MediaCreate, MediaDetail):
getRatingCategoryinratingColors.tsreturned hardcoded French strings ("Chef-d'œuvre", "Parfait", "Très bon", etc.) instead of using i18n. Replaced all 12 category labels withi18next.t('common.ratingCategory.*')calls, using the existingcommon.ratingCategorykeys added in this release. - Dashboard stats: average rating, median, and standard deviation were all broken. Three root causes in
get_dashboard_stats(Rust backend):AVG(user_rating)included ratings of 0 (unrated media), inflating the average and making it inconsistent withrated_countwhich filtered> 0. Fixed by addingAND user_rating > 0to the AVG subquery.- Ratings were read from the database as
i32viarow.get::<_, i32>(0), butuser_ratingis stored asREAL(float). rusqlite silently failed the conversion, andfilter_map(|r| r.ok())discarded all rows — resulting in an emptyratingsvector, hence median = 0 and std dev = 0. Fixed by reading asf64then casting toi32. - An incorrect scale conversion
(r / 10.0) * 100.0multiplied already-0-100 ratings by 10 (e.g. 68 → 680). Removed since ratings are stored on a 0-100 scale per the CHECK constraint. - Frontend (
UnifiedStatsCard): the average rating circle displayed a non-zero value even whenrated_countwas 0. Now forcesrating100 = 0when no media are rated.
- i18n (miscellaneous front-end): added missing
common.ok/common.deletekeys and fixed hardcoded UI strings inLanguagePicker(search placeholder, no-results, clear),Import(in-development step),Settings/ProfileSection(password/storage messages, window styles, avatar alt),Settings/PersonalizationSection(theme names, window control labels),Settings/DataSection(no other profile for merge),Library(numeric filter operators, min/value labels),MediaDetail(read more/collapse, lightbox hint),MangaReader(page number),TemplateManagement(new/edit/delete/default states), and crop modals alt text. Added matching EN/FR keys undercommon,languagePicker,themes,personalization.windowStyle,import,library.operators,mediaDetail,mangaReader.pageNumber,settings.profile, andtemplateManagement.
- Welcome tutorial: interactive coach-marks/spotlight tutorial that automatically starts after onboarding on first Dashboard visit. Guides new users through creating their first collection and first media item across 24 steps organized in 3 phases (Getting Started → Collection Setup → Media Creation). Steps cover: Dashboard welcome → Library navigation → collection bar hover discovery → "+" button → collection naming → dynamic fields intro → creator section → dates section → progression section → advanced section → visual column → save collection → Library "New" button → media collection dropdown → title → creator → status/date → synopsis → cover → genres → progress → review → rating → gallery → attachments → save media. Features include:
- SVG mask spotlight with clean cutout, glassmorphism tooltip cards matching existing app style, pulse/glow animation on targeted elements
- Phase-aware compact stepper indicator (shows current step within its phase, not just global position)
- Action steps: interactive steps that require the user to click the highlighted element (e.g. navigate to Library, click "+", save collection, click "New") with an animated "Click to continue" banner; auto-advance on navigation to the target page
- Info steps: non-blocking steps that highlight and explain a UI section; user clicks Next to proceed
- Completion step: final step with "Don't show again" checkbox that persists
tutorial_has_completedto backend - Progressive section locking: form sections (creator, dates, progression, advanced, visual on collection-edit; title, creator, status/date, synopsis, cover, genres, progress, review, rating, gallery, attachments on media-create) are locked until the tutorial reaches the corresponding step, preventing users from interacting with sections not yet explained
- Portal/dropdown detection: the spotlight automatically includes open popovers, dropdowns, dialogs, and absolute/fixed children that extend beyond the target element; touching/overlapping rects are merged into a single exact rectilinear-union outline (L-shape supported) via grid decomposition polygon tracing, so no double edges or invented area appear
- Tooltip collision avoidance: tooltip auto-repositions (flips to opposite side) if it overlaps the highlighted target, clamped to viewport boundaries
- Header clamping: spotlight padding is trimmed at the header's bottom edge so the highlight border never bleeds into the header area
- Click-blocking backdrop: clicks outside the active target area are blocked; the target itself remains interactive for action steps
- Auto-scroll: target element is scrolled into view on each step change
- Waiting state: spinner overlay shown while navigating between pages during multi-page steps
- Keyboard shortcuts: Esc to skip, Enter to go next (on info steps), ArrowLeft to go back
- Tutorial invitation badge: shimmer badge on the Dashboard for users who haven't seen the tutorial yet, offering to start it or dismiss it (persists
tutorial_has_seen_invitation) - Per-profile persistence via backend settings sync (
tutorial_has_seen_invitation,tutorial_has_completed) - Tutorial is optional and interruptible at any point (Skip button always visible)
- Rating category labels (i18n):
common.ratingCategorywith 12 quality tiers (notRated, masterpiece, perfect, excellent, veryGood, good, fine, decent, average, passable, bad, terrible) in EN/FR - Media mention:
mediaMentionTitleandmediaMentionHint(Ctrl+M) i18n keys for linking media in reviews, plusnavigateHint,selectHint,cancelHintkeyboard hint labels - Reader:
skipandupdatebuttons on the progress update prompt when finishing a CBZ/chapter - Attachment drop hint:
attachmentDropHinti18n key replacingnotesAndPoints, with drag-and-drop file browsing text (saves, configs, ebooks, archives...) - Stats:
allYears,clickYearHint,clickMonthHinti18n keys for the Activity over time drill-down chart (EN/FR)
- Stats page: replaced the "All Collections" dropdown filter with interactive click-to-toggle filtering directly on the Collection Breakdown donut chart. Clicking a collection segment or legend item toggles its selection (multi-select); all stats update to reflect only the selected collections. Empty selection = all collections shown. The donut always displays all collections (from period-filtered media) regardless of selection, with unselected segments dimmed. A "Reset" button appears in the section header when collections are selected. Removed the
CollectionFilterdropdown component and theFiltericon import. - Stats page: replaced the flat monthly "Activity over time" bar chart with an interactive drill-down chart (year → month → day). Default view shows one bar per year; clicking a year drills into 12 monthly bars; clicking a month drills into daily bars. Breadcrumb navigation at the top allows clicking "All Years" or the year name to navigate back up. Clicking a year/month also sets the global period filter for the entire page, so all other charts/stats update to reflect the selected time range. The chart uses media filtered by collection + status (but not period) so it always shows all available years.
- Stats page: made the Status Breakdown donut chart interactive with click-to-toggle filtering, matching the Collection Breakdown pattern. Clicking a status segment (e.g. "Completed", "Not Started") filters all other charts on the page to only show media with that status. Multiple statuses can be selected. A "Reset" button appears in the section header when statuses are selected. The donut always displays all statuses (from period + collection filtered media) regardless of selection, with unselected segments dimmed.
- Stats page: removed the global
PeriodSelectordropdown from the toolbar. Period filtering is now handled exclusively by the Activity over time drill-down chart. Removed thePeriodSelectorcomponent,getAvailableYearshelper, andChevronDownicon import as they are no longer used. - Stats page: moved the "Activity over time" section above the Status/Collection breakdown sections for better visual hierarchy.
- Stats page: updated the filter pipeline to support three independent filters (period + collections + statuses) with intermediate filtered media levels so each donut chart respects the other's filter but not its own, preventing circular filtering.
- Stats page: redesigned the Top Rated / Worst Rated media lists (
MediaRankList) from simple text rows to compact inline cards. Each item now displays a cover image (40×80px) with a rating badge overlay, the media title, creator pills (matchingMediaCardstyle with+Noverflow), and collection + progress status badges stacked in a right column. All items have a fixed height for visual consistency. Each card is clickable and navigates to the corresponding media detail page.
- MediaCard flip (back face) normal mode: multi-line titles were clipped — the second line showed only its top half due to flex compression. Added
shrink-0to the title and header to preserve their natural height. - Tooltip (
onlyWhenTruncated): truncation detection now also checks vertical overflow (scrollHeight > clientHeight), enabling tooltips online-clampelements that are truncated vertically. - Escape key in MediaDetail: when a viewer (MangaReader CBZ or gallery Lightbox) was open, pressing Escape closed the viewer AND triggered a navigation back. Now Escape only closes the viewer; a second Escape is needed to go back. Fixed by adding
data-escape-to-closeattribute to viewer containers and updating the global shortcut handler to check for it before callinggoBack(). Also moved the Lightbox keyboard listener fromdocumenttowindowto ensure correct event ordering. - MediaCard hover: after deleting a media in the library, the first hover on any card caused the title to jump up (info bar expanded to 140px for one frame before the measured height was applied). Fixed by changing the CSS fallback for
--card-info-h-hoverfrom140pxtovar(--card-info-h, 64px), so the default hover height matches the non-hover height untilhandleMouseEntersets the correct measured value. - Export page (ZIP mode): the selection tick on export level cards (DB only / DB + images / Full backup) slid between cards when switching mode due to
layoutIdanimation. Replaced with a simple fade + scale transition (0.15s) so the tick appears and disappears in place instead of sliding. - Export page (format selection): same
layoutIdsliding issue on the format cards (CSV / TSV / Markdown). Replaced with the same fade + scale transition. - Export page (collection filter): same
layoutIdsliding issue on the collection scope cards (All / Specific). Replaced with the same fade + scale transition. - Export page: large empty gap between the stepper and content during step transitions. Caused by
AnimatePresence mode="sync"leaving both old and new content in the DOM simultaneously. Changed tomode="wait"and reduced stepper bottom margin frommb-8tomb-4. - Image upload: files uploaded during media editing could silently overwrite existing images on disk. Filenames were generated using the upload
position(e.g.cover_100_full.webp), and sincestartPosition = 100is reused on every edit, uploading a file with the same name as an existing image overwrote it. Fixed by replacing the position in the filename with a timestamp + random suffix ({stem}_{YYMMDD-HHMMSS}_{random}_full.webp).
- MediaCard flip (back face) normal mode: genres now limited to 2 visible pills (was 3) with
max-w-[60px]truncation andflex-nowrapto prevent multi-line wrapping that pushed the attachments count out of view. Tooltip on each genre pill shows the full name on hover. The+Nbadge also has a tooltip listing remaining genres. - Export ZIP: switched compression method from
DeflatedtoStoredfor all files. Media files (WebP, JPEG, PDF) are already compressed — recompressing them wasted CPU for negligible size gain. The ZIP is now written significantly faster on large backups (e.g. 7+ GB full backup). - Export ZIP: files are now streamed to the ZIP in 64 KB chunks via
BufReaderinstead of being loaded entirely into memory withstd::fs::read(), reducing RAM usage on large exports.
- Export ZIP: real-time progress bar during archive creation. Shows percentage, current file name (left-aligned, fixed), file counter X/Y (right-aligned), and bytes processed/total. Uses Tauri's
ChannelIPC for live progress events from the Rust backend. - MediaCard flip (back face): tooltip on the title when truncated, showing the full title on hover in all density modes (compact, normal, large)
- Library: "Flip all cards" toggle button in the toolbar (between sort and view mode buttons) — when activated, all media cards flip to their back face simultaneously, loading details asynchronously for each card
- Attachment rename: inline rename of CBZ/files attached to a media directly from MediaDetail (pencil icon → edit name → Enter/blur to confirm, Escape to cancel). Renames both the database record and the physical file on disk.
- Attachment drag & drop reorder in MediaCreate: attachments can be reordered via drag & drop (using dnd-kit) during media creation/editing. Order is persisted on save via
reorder_media_attachments. - Attachment drag & drop reorder in MediaDetail: attachments can be reordered inline with immediate persistence. Uses optimistic updates with rollback on error.
- Attachment inline rename in MediaCreate: existing attachments can be renamed inline during media editing, with changes saved on submit.
- Database migration: added
positioncolumn tomedia_attachmentstable with auto-migration for existing databases. Attachments are now ordered bypositioninstead ofcreated_at. - New Tauri commands:
rename_media_attachment(renames file on disk + updates DB) andreorder_media_attachments(batch updates positions in a transaction).
- Database corruption prevention: added
PRAGMA wal_checkpoint(TRUNCATE)on app exit viaRunEvent::ExitRequestedhandler, ensuring WAL data is properly merged into the main database file before shutdown create_mediaandupdate_media: wrapped all 3 operations (media insert/update + genre linking + credit linking) in a single atomic transaction instead of separate unchecked transactions, preventing partial writes if one step failsswitch_profile: lock file is now acquired for the new profile BEFORE updating the manifest, preventing an inconsistent state where the manifest says profile B is active but the connection is still on profile Aswitch_profile: WAL checkpoint on the old connection before swapping to the new profile's database
- Lock file (
logia.db.lock) using WindowsLockFileAPI to prevent two instances of Logia from using the same profile database simultaneously. Lock is acquired at startup and on profile switch, with RAII cleanup on exit. - GFS (Grandfather-Father-Son) automatic database backup: creates a daily copy of
logia.dbinbackups/on app startup and on profile switch. Rotation keeps 7 daily, 4 weekly, and 12 monthly backups, deleting older ones automatically.
genres::link_to_mediaandpeople::link_to_media: removed innerunchecked_transactionsince callers (create_media/update_media) now wrap operations in a global transaction- Backup rotation: weekly grouping now uses
(year, week)instead ofweekalone to avoid cross-year collisions - Backup rotation: monthly retention now uses calendar date comparison instead of
age_days <= 365for precise 12-month retention
- MediaCard flip (back face) compact mode: creators were hidden, now visible with icon-based compact layout (1 creator +
+Ncount) - MediaCard flip compact mode: genres now hidden to free space for creator display
- MediaCard flip compact mode: creator
+Ncount merged as superscript next to the pill instead of a separate truncated element - MediaCard flip all modes: creator pills with long names (e.g. "Everything Unlimited Ltd.") now truncate with ellipsis and show full name in a tooltip on hover
- Library: creator filter popup showed an empty list because
useDistinctCreatorswas only enabled when the filter presets menu was open (isFilterOpen), not when a creator filter pill popup was opened. Now also enabled when an active creator filter exists.
- CSV import: fixed incorrect column name
progressCurrent→progress_currentin the INSERT query, which caused "table media has no column named progressCurrent" errors on every imported row - Images not loading in production build: fixed CSP
img-srcdirective fromhttps://asset.localhosttohttp://asset.localhostto match Tauri v2's asset protocol scheme on Windows - Media sort by "recently added" broken after CSV import: imported media used RFC 3339 timestamps (
2026-07-20T17:47:00+00:00) while media created normally used SQLite format (2026-07-20 17:47:00). Since SQLite sorts DATETIME lexicographically,T(ASCII 84) > space (ASCII 32), causing all imported media to sort before manually created media regardless of actual date. Fixed by using SQLite-compatible timestamp format in CSV/profile imports. - CSP: added
blob:toimg-srcdirective to allow blob URL images to load
- Fonts: replaced Google Fonts CDN (
fonts.googleapis.com) with local@fontsource/interpackage to eliminate external network dependency and improve offline reliability