[WIP-Feat] Heroic Backup System and Wizard#5522
Draft
flavioislima wants to merge 21 commits intomainfrom
Draft
Conversation
Add the skeleton for the backup import/export feature. Declares the manifest + stage types, registers stub async IPC handlers (exportHeroicBackup / validateHeroicBackup / applyHeroicBackup / getRollbackSnapshot / rollbackHeroicBackup / restartHeroic), wires the preload API, adds LogPrefix.ImportExport, and provisions the importExportRollbackStore electron-store. Also adds the adm-zip dependency used by the later export/apply commits.
Implement exportHeroicBackup — writes a zip archive with verbatim byte copies of every source file (global config, per-game settings, fixes, themes, credentials, library caches + installed manifests, sideload library, wine downloader store) plus a manifest.json describing the format version, heroic version, platform and counts. Only stages the caller selects are packaged; missing files are skipped without failing the export. Backed by adm-zip so the same zip API is available for the later apply-path verbatim reads. Unit-tested against a tmp-dir fixture to lock in the byte-for-byte invariant for config.json and per-game files.
Implement validateHeroicBackup — opens the zip, reads the manifest, cross-references the included stages against the local machine and returns a structured ValidationReport the wizard consumes. Surfaces format-version / platform mismatches, broken install + prefix paths per-game, missing wine versions (split into downloadable vs. not by consulting the local wineDownloaderInfoStore), and credential presence with a display name parsed from each store's user file. Game titles are read from the bundled store_cache libraries (installed manifests don't carry titles), matching the per-game list UI in the wizard.
Implement the write-side of the backup flow: - applyHeroicBackup opens the archive, writes a pre-apply snapshot of the same stages to a persistent rollback archive (path recorded in importExportRollbackStore so the rollback button survives restarts), then applies each selected stage. - Per-game settings, installed.json files and the sideload library are patched conservatively — never via Object.entries().map() — so sibling keys like version and explicit are preserved. Only the appName block or install_path/path is rewritten, and only when the user explicitly overrode it in the wizard (Browse / Skip / Install-after / Use-default-prefix). - After writing credential files, Legendary and Nile are mirrored into configStore via getUserInfo/getUserData so the frontend login state updates without a restart. GOG and Zoom keep their own kv stores. - Wine stage refreshes wineDownloaderInfoStore via updateWineVersionInfos(true) before matching and fires background installs for missing versions when the user keeps the default toggle. - rollbackHeroicBackup replays the snapshot archive with overwriteGlobalSettings=true and clears the snapshot entry. - restartHeroic calls app.relaunch() + app.exit(0). Apply semantics are locked in by unit tests against the "version"/"explicit" preservation invariant, per-runner skip/queue/patch branches, and the rollback snapshot write.
Add a new Settings > Import/Export section and a 7-step import wizard.
Settings section:
- Collapsible Export card with per-stage toggles, "N of M included"
summary and a date+time-stamped default filename
(heroic-backup-YYYY-MM-DD_HH-MM-SS.zip).
- Collapsible Import card that launches the wizard and surfaces a
Rollback button only when a persistent rollback snapshot exists.
Wizard:
- 820px dialog, 760px body, 520px min-height, horizontal stepper with
past steps clickable and a step counter ("Step X of 7").
- Uses existing design-system tokens (--text-*, --space-*,
--status-success/warning/danger, --accent, --modal-background,
--input-background) — no hardcoded colors.
- Reuses existing UI primitives: ToggleSwitch, InfoIcon, StoreLogos,
PathSelectionBox, Dialog/DialogHeader/DialogContent/DialogFooter.
- Steps: pick file -> summary + manifest + platform check ->
global settings (with opt-in overwrite toggle defaulting off) ->
per-game settings (titles from store_cache libraries, filter +
select-all/deselect-all) -> credentials (always importable, shown
with status pill only) -> library & system (collapsible details,
per-game Browse/Keep/Skip/Download actions, prefix row also offers
Use-default-prefix, wine versions with Download default on) ->
Done (stage list with exclusion labels, rollback hint and a restart
banner that calls the new restartHeroic IPC).
- Fade-in transition on each step, disabled for users with
prefers-reduced-motion.
Registered under /settings/importexport in the sidebar submenu and
plumbed through pnpm i18n so the new keys land in en/translation.json.
Address review feedback on the import/export UI:
- InfoIcon is a hover-only tooltip component (its span is hidden by
default CSS) — it's meant to sit next to a label, not stand in as
visible hint text. Replace every standalone InfoIcon block in the
Settings section and the wizard steps 1, 3, 5, 6 and 7 with a
proper .ImportExportSettings__hint / .ImportExportWizard__hint
paragraph styled secondary (var(--text-secondary), --text-sm,
max-width 65ch).
- Export card now pre-fills the output folder with the user's home
directory on mount via a new getHomeDir async IPC
(app.getPath('home')) so the Export button is usable without first
picking a folder.
- Summary step now shows user-facing store names ("Epic: 1, GOG: 2,
Amazon: 1") instead of internal runner ids.
- Per-game settings step and Store logins step no longer render the
StoreLogos SVG — the store is indicated inline as "(Epic)" /
"(Amazon)" text next to the title and name, which is cleaner in a
dense list.
When the library is genuinely empty (no stores logged in, no sideloaded games), add a secondary line under the existing "log in" message that links to Settings > Import/Export so a user on a clean install can restore their previous state instead of starting from scratch. The hint is visually subdued (secondary text color, separator rule) so it does not compete with the primary login call to action.
Coming from the library's "Restore from backup" link, opening the Export card first is counter-intuitive — the user is there to import, not export. Pass openImport=true via react-router's NavLink state; the Settings section reads location.state on mount, flips the cards so the Import card is the one expanded (Export is collapsed), and auto-opens the import wizard so the user lands on "Pick a backup file" in a single click.
Symmetric treatment for both subsections on step 6: - Missing Wine / Proton versions: replace the all-or-nothing master toggle with a per-version checkbox list and Select all / Deselect all buttons, matching the per-game settings pattern. Versions that aren't downloadable on this machine have the checkbox disabled; the status pill stays next to the name for context. Only the ticked versions are queued at apply time. - Missing install paths: same approach as above — master toggle, per-game checkbox, select-all/deselect-all, no filter. Backend contract: replace HeroicApplyOptions.downloadMissingWine: boolean with includedWineVersions: string[]. The wine apply stage now intersects that list with what's currently available locally and fires only those downloads. Rollback calls pass an empty array. Existing apply tests were updated to the new shape; all 138 backend tests still pass.
Add an installedOK: InstalledGameSummary[] field to HeroicBackupValidationReport, emitted by the validator for any installed game in the backup whose install_path still exists on the local filesystem. Rendered on step 6 as a collapsible section (closed by default) that lists each game with a green checkmark, the runner as a parenthetical, and the install path as secondary text. This gives the user reassurance about what is being re-registered as-is in addition to the already-shown lists of things that need attention. Validator test case added to cover the split between OK games and games with broken paths.
Break the monolithic 1341-line index.tsx into: - steps/StepPickFile.tsx - steps/StepSummary.tsx - steps/StepGlobalSettings.tsx - steps/StepPerGame.tsx - steps/StepCredentials.tsx - steps/StepLibrarySystem.tsx - steps/StepDone.tsx - steps/index.ts (barrel) - shared.tsx — PathActionRow, StatusPill, runnerLabel(), stageFriendlyLabel() and the PathChoice / PathAction types used across multiple steps index.tsx shrinks to 379 lines and now owns only the wizard orchestration (state, navigation, the apply call) plus the dialog chrome. No behavioural change; codecheck, lint, full test suite and electron-vite build all clean.
…bar buttons - Step 1 drops the explicit "Choose file" button and wires the PathSelectionBox folder icon straight to the validation handler — picking a file via onPathChange triggers pickBackupFile. The box is disabled while validation is running, and a small "Validating..." hint appears instead of the old in-button label. - Fix the Back button flow: navigating from step 2 back to step 1 preserved filePath + validation state, but the Next button was disabled by an extra step === 0 guard that forced the user to re-pick the file. Drop that condition; the !validation check alone already covers the real "no file yet" case. - Shrink the Select all / Deselect all buttons on step 4 and step 6 to the same compact sizing as the per-row path action buttons (var(--text-sm), var(--space-3xs) var(--space-sm)) so the toolbar no longer looks heavier than the list rows beneath it.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A pretty complete backup system for Heroic that backups Logins, Settings, Game Settings, checks for missing paths for Games and Wineprefixes, check for missing wine versions, and more. With options to override and fix missing locations.
Should also convert from Flatpak to other linux installs and vice-versa.
Not planned for this MVP: Proper Game files backup.
Use the following Checklist if you have changed something on the Backend or Frontend: