From be2565ef2a72f41463a9708a6d9cf318d6263c6f Mon Sep 17 00:00:00 2001
From: Codeon <313085171+codeon89@users.noreply.github.com>
Date: Tue, 1 Sep 2026 14:36:49 -0700
Subject: [PATCH 1/2] changed: console doesn't always auto open in dev mode and
fallback to the dev console settings
---
CHANGELOG.PATCHED.md | 2 ++
CHANGELOG.md | 1 -
src/components/settings/Interface.jsx | 5 +----
tests/interface-debug-console.test.js | 28 +++++++++++++++++++++++++++
4 files changed, 31 insertions(+), 5 deletions(-)
create mode 100644 tests/interface-debug-console.test.js
diff --git a/CHANGELOG.PATCHED.md b/CHANGELOG.PATCHED.md
index 6de1182..048b829 100644
--- a/CHANGELOG.PATCHED.md
+++ b/CHANGELOG.PATCHED.md
@@ -2,6 +2,8 @@
## Fork's Nightly Changes
*Changes that's already on the fork and waiting to be reviewed for merge into original Atlas*
+- (Dev-Only) DevTools no longer auto-opens in dev mode unless explicitly enabled in config.
+- Removed the stale restart popup and hint on the Show debug console toggle — it applies immediately to all open windows.
- Update debounce logic for Browse and Library: Search in Catalog Browse and Library now debounces the text input and waits for a pause before filtering. Previously every keystroke updated `activeFilters.text` and ran `filterGamesWithState` (Library) or scheduled a catalog fetch, causing input lag on large libraries and a wasted local-filter pass even while browsing the server-side catalog. The input still echoes instantly from local state; clear bypasses the delay.[PR#398](https://github.com/towerwatchman/Atlas/pull/398)
## Independent Changes
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1b1f512..f8d5da7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,7 +3,6 @@
## Unreleased
### Changed
-- (Dev-Only) DevTools no longer auto-opens in dev mode unless explicitly enabled in config.
- Allow setting folder and program locations by typing or paste a path directly besides using Browse / Select Folder button.
- Affected locations: Atlas Importer, Library path settings,and Emulators. The 7z field path is not touched as it have more requirements than the based one.
- Path resolution highlighting: red if invalid, green if path exists or pass the check.
diff --git a/src/components/settings/Interface.jsx b/src/components/settings/Interface.jsx
index 2fecff1..7cb631d 100644
--- a/src/components/settings/Interface.jsx
+++ b/src/components/settings/Interface.jsx
@@ -136,7 +136,6 @@ const Interface = () => {
const handleDebugConsoleChange = () => {
setShowDebugConsole(!showDebugConsole);
saveSettings({ showDebugConsole: !showDebugConsole });
- alert("Changing the debug console setting requires a restart.");
};
const handleStartupUpdateCheckChange = () => {
@@ -308,6 +307,7 @@ const Interface = () => {
override this for a single query.
+ {/* No restart needed: save-settings opens/closes DevTools on all open windows live. */}
{
onChange={handleDebugConsoleChange}
/>
-
- Enabling or Disabling the debug console will require a restart
-
diff --git a/tests/interface-debug-console.test.js b/tests/interface-debug-console.test.js
new file mode 100644
index 0000000..da7a375
--- /dev/null
+++ b/tests/interface-debug-console.test.js
@@ -0,0 +1,28 @@
+import { describe, test, expect } from 'vitest'
+import fs from 'fs'
+import path from 'path'
+
+const src = fs.readFileSync(
+ path.join(__dirname, '..', 'src', 'components', 'settings', 'Interface.jsx'),
+ 'utf8',
+)
+
+// The debug-console toggle applies live via save-settings (main opens/closes
+// DevTools on all open windows), so it must not show a restart popup or hint.
+describe('Interface debug console (no restart)', () => {
+ test('toggling never pops a restart alert', () => {
+ expect(src).not.toMatch(/debug console setting requires a restart/i)
+ })
+
+ test('no restart hint remains next to the toggle', () => {
+ expect(src).not.toMatch(/debug console will require a restart/i)
+ })
+
+ test('toggle still saves the setting (guard against over-deletion)', () => {
+ expect(src).toMatch(/saveSettings\(\{\s*showDebugConsole:/)
+ })
+
+ test('language restart note is untouched', () => {
+ expect(src).toMatch(/system language will require a restart/i)
+ })
+})
From 06f905a7240ab1ac210c0b441aff43d062bb3acd Mon Sep 17 00:00:00 2001
From: Codeon <313085171+codeon89@users.noreply.github.com>
Date: Thu, 3 Sep 2026 19:51:47 -0700
Subject: [PATCH 2/2] feat: support linux builds for fork & re-arrange patches
& changelog details
---
.github/workflows/ci-patched.yml | 56 +++++++++++++++
.github/workflows/nightly-patched.yml | 95 ++++++++++++++++++++++----
CHANGELOG.PATCHED.md | 60 ++++++++--------
PATCHES.md | 41 +++++++++++
docs/FORK-PATCHED-RELEASES.md | 4 +-
docs/FORK-PATCHED-RUNBOOK.md | 11 +--
scripts/build-patched.js | 15 +++-
tests/patched-build.test.js | 14 +++-
tests/patched-ci-workflow.test.js | 32 +++++++++
tests/patched-release-workflow.test.js | 16 +++++
10 files changed, 293 insertions(+), 51 deletions(-)
create mode 100644 .github/workflows/ci-patched.yml
create mode 100644 PATCHES.md
create mode 100644 tests/patched-ci-workflow.test.js
diff --git a/.github/workflows/ci-patched.yml b/.github/workflows/ci-patched.yml
new file mode 100644
index 0000000..38d31de
--- /dev/null
+++ b/.github/workflows/ci-patched.yml
@@ -0,0 +1,56 @@
+name: CI-Patched
+
+# Fork-only copy of upstream's CI with a node_modules cache in front of
+# `npm ci`. Upstream's ci.yml stays byte-identical so nightly merges stay
+# clean; the upstream CI workflow is disabled in the fork's Actions settings
+# so PRs run this one instead of both. Same triggers, same steps, same gate.
+on:
+ push:
+ branches: [main, nightly]
+ pull_request:
+
+permissions:
+ contents: read
+
+jobs:
+ checks:
+ name: Patched checks
+ # Never runs outside the fork: if this file ever reaches upstream by a bad
+ # merge, it stays silent there instead of double-running their CI.
+ if: github.repository == 'codeon89/Atlas'
+ runs-on: ubuntu-latest
+ timeout-minutes: 15
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: 22
+ cache: npm
+
+ - name: Restore node_modules
+ id: nm-cache
+ uses: actions/cache@v4
+ with:
+ path: node_modules
+ key: nm-v1-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
+
+ # Cache hit means the lock file is unchanged, so the tree on disk is
+ # exactly what `npm ci` would produce -- skip the ~2 min reinstall.
+ - name: Install dependencies
+ if: steps.nm-cache.outputs.cache-hit != 'true'
+ run: npm ci --no-audit --no-fund
+
+ - name: Build browser extension
+ run: npm run build:extension
+
+ - name: Run Vitest suite
+ run: npm run test:run
+
+ - name: Run legacy check scripts + build
+ # `npm run check` also runs test:run (harmless) and vite build, giving us
+ # the syntax checks, asset/layout checks, the test suite, and a renderer
+ # build in one gate. git diff --check catches whitespace/conflict markers.
+ run: npm run check
diff --git a/.github/workflows/nightly-patched.yml b/.github/workflows/nightly-patched.yml
index f8d6db2..a048e9f 100644
--- a/.github/workflows/nightly-patched.yml
+++ b/.github/workflows/nightly-patched.yml
@@ -1,8 +1,9 @@
name: Atlas Nightly-Patched Prerelease
# Fork-only release flow. Modelled on upstream's nightly.yml (draft-first,
-# verify-then-publish): single Windows leg, full `npm run check` gate inline,
-# versions naming the upstream nightly they were built from.
+# verify-then-publish): Windows + Linux legs sharing one draft release, full
+# `npm run check` gate inline, versions naming the upstream nightly they were
+# built from.
on:
push:
branches: [nightly-patched]
@@ -146,11 +147,14 @@ jobs:
needs: prepare
# Skip when prepare is skipped (repo guard) or opted out via [no-build].
if: needs.prepare.result == 'success' && needs.prepare.outputs.skip != 'true'
- runs-on: windows-latest
- timeout-minutes: 45
- defaults:
- run:
- shell: pwsh
+ runs-on: ${{ matrix.os }}
+ strategy:
+ # One platform failing must not cancel the other mid-upload, which would
+ # leave the draft holding a partial asset set.
+ fail-fast: false
+ matrix:
+ os: [windows-latest, ubuntu-latest]
+ timeout-minutes: 60
env:
PATCHED_NIGHTLY: ${{ needs.prepare.outputs.nightly }}
PATCHED_BUILD_NUMBER: ${{ needs.prepare.outputs.number }}
@@ -171,27 +175,76 @@ jobs:
- name: Install dependencies
run: npm ci
+ - name: Install Linux system dependencies
+ if: matrix.os == 'ubuntu-latest'
+ shell: bash
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y libarchive-tools rpm
+ # appimagetool (bundled by electron-builder for the AppImage target)
+ # is itself an AppImage and needs FUSE2 to run. Ubuntu 24.04
+ # renamed libfuse2 -> libfuse2t64; keep the old name as fallback.
+ sudo apt-get install -y libfuse2t64 || sudo apt-get install -y libfuse2
+
# Version, owner (fork repo) and channel (patched) come from
- # scripts/build-patched.js — never from package.json at rest.
+ # scripts/build-patched.js — never from package.json at rest. The script
+ # picks nsis vs deb/AppImage/pacman from the runner OS, so both legs run
+ # the same command and upload to the same draft.
- name: Build and upload Windows installer
+ if: matrix.os == 'windows-latest'
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ shell: pwsh
+ run: npm run publish
+
+ - name: Build and upload Linux packages
+ if: matrix.os == 'ubuntu-latest'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ shell: bash
run: npm run publish
- - name: Assert installer exists
+ - name: Assert installer exists (Windows)
+ if: matrix.os == 'windows-latest'
+ shell: pwsh
run: |
$exe = Get-ChildItem -Path "release/patched-$env:PATCHED_NIGHTLY-$env:PATCHED_BUILD_NUMBER" -Filter *.exe
if (-not $exe) { throw 'No installer was produced.' }
$exe | ForEach-Object { Write-Output "$($_.Name) ($($_.Length) bytes)" }
+ - name: Assert packages exist (Linux)
+ if: matrix.os == 'ubuntu-latest'
+ shell: bash
+ run: |
+ shopt -s nullglob
+ files=(release/patched-"$PATCHED_NIGHTLY"-"$PATCHED_BUILD_NUMBER"/*.deb release/patched-"$PATCHED_NIGHTLY"-"$PATCHED_BUILD_NUMBER"/*.AppImage release/patched-"$PATCHED_NIGHTLY"-"$PATCHED_BUILD_NUMBER"/*.pacman)
+ if [ ${#files[@]} -eq 0 ]; then
+ echo "No Linux packages were produced."
+ exit 1
+ fi
+ printf '%s\n' "${files[@]}"
+
- name: Upload Windows artifact
+ if: matrix.os == 'windows-latest'
uses: actions/upload-artifact@v4
with:
name: windows-patched-installer
path: release/patched-*/*.exe
if-no-files-found: error
+ - name: Upload Linux artifact
+ if: always() && matrix.os == 'ubuntu-latest'
+ uses: actions/upload-artifact@v4
+ with:
+ name: linux-patched-packages
+ path: |
+ release/patched-*/*.deb
+ release/patched-*/*.AppImage
+ release/patched-*/*.pacman
+ if-no-files-found: error
+
virus-scan:
name: ClamAV scan of patched artifacts
needs: build
@@ -209,6 +262,13 @@ jobs:
path: scan/windows
continue-on-error: true
+ - name: Download Linux artifact
+ uses: actions/download-artifact@v4
+ with:
+ name: linux-patched-packages
+ path: scan/linux
+ continue-on-error: true
+
- name: Install ClamAV
run: |
sudo apt-get update
@@ -223,7 +283,7 @@ jobs:
run: |
set +e
shopt -s nullglob globstar
- files=(scan/**/*.exe)
+ files=(scan/**/*.exe scan/**/*.AppImage scan/**/*.deb scan/**/*.pacman)
if [ ${#files[@]} -eq 0 ]; then
echo "No patched artifacts were downloaded to scan." | tee -a "$GITHUB_STEP_SUMMARY"
echo "detections=0" >> "$GITHUB_OUTPUT"
@@ -289,8 +349,9 @@ jobs:
echo "$names"
# Publishing with a missing installer, blockmap or channel manifest
- # would ship a release the updater cannot use. Refuse and leave the
- # draft for a re-run instead.
+ # would ship a release the updater cannot use. Either platform
+ # missing refuses the whole release and leaves the draft for a
+ # re-run instead.
for suffix in ".exe" ".exe.blockmap"; do
if ! echo "$names" | grep -qxF "Atlas-Setup-${version}${suffix}"; then
echo "::error::Missing or incomplete asset: Atlas-Setup-${version}${suffix}."
@@ -301,6 +362,16 @@ jobs:
echo "::error::Missing or incomplete asset: patched.yml."
exit 1
fi
+ for ext in ".deb" ".AppImage" ".pacman"; do
+ if ! echo "$names" | grep -qE "\\${ext}$"; then
+ echo "::error::Missing or incomplete asset: no *${ext} on $tag."
+ exit 1
+ fi
+ done
+ if ! echo "$names" | grep -qxF 'patched-linux.yml'; then
+ echo "::error::Missing or incomplete asset: patched-linux.yml."
+ exit 1
+ fi
gh release edit "$tag" --draft=false --prerelease --latest=false
echo "Published $tag"
diff --git a/CHANGELOG.PATCHED.md b/CHANGELOG.PATCHED.md
index 048b829..d2f9492 100644
--- a/CHANGELOG.PATCHED.md
+++ b/CHANGELOG.PATCHED.md
@@ -1,35 +1,35 @@
-# Changelog - PATCHED
+# CHANGELOG - PATCHED
-## Fork's Nightly Changes
-*Changes that's already on the fork and waiting to be reviewed for merge into original Atlas*
-- (Dev-Only) DevTools no longer auto-opens in dev mode unless explicitly enabled in config.
-- Removed the stale restart popup and hint on the Show debug console toggle — it applies immediately to all open windows.
-- Update debounce logic for Browse and Library: Search in Catalog Browse and Library now debounces the text input and waits for a pause before filtering. Previously every keystroke updated `activeFilters.text` and ran `filterGamesWithState` (Library) or scheduled a catalog fetch, causing input lag on large libraries and a wasted local-filter pass even while browsing the server-side catalog. The input still echoes instantly from local state; clear bypasses the delay.[PR#398](https://github.com/towerwatchman/Atlas/pull/398)
+**v0.9.9-patched.nightly.494.2**
+ - Removed the stale restart popup and hint on the Show debug console toggle — it applies immediately to all open windows.[PR#399](https://github.com/towerwatchman/Atlas/pull/399)
+ - (Dev-Only) DevTools no longer auto-opens in dev mode unless explicitly enabled in config.
+ - Nightly-Patched: support Linux builds & patch ci runs. [PR#20](https://github.com/codeon89/Atlas/pull/20)
-## Independent Changes
-*Any fork-only changes that is not accepted for merged but valid, or independent changes to make the fork repo releasable (e.g. custom version or preventing updates)*
-- Support `[no-build]` detection in your HEAD commit message when pushing without wanting a release (docs or changelog-only pushes) or merge existing change from original Atlas.
-- Nightly-Patched releases: the fork ships its own Windows installer line (`{base}-patched.nightly.{nightly}.{p}`) with a third update channel under Settings > App Updates. Patched builds check only the fork feed; upstream nightlies surface as a read-only notice, never a download. See `docs/FORK-PATCHED-RUNBOOK.md`.
-- LewdCorner member tier detection. Atlas now scrapes your LewdCorner account's shop page to determine your membership tier (Standard / Plus) and stores it alongside your credentials. Browse filters content by tier so Plus users see everything while Standard or non-login users only see what their subscription allows.[PR#391](https://github.com/towerwatchman/Atlas/pull/391)
+**v0.9.9-patched.nightly.494.1**
+ - Update debounce logic for Browse and Library: Search in Catalog Browse and Library now debounces the text input and waits for a pause before filtering. Previously every keystroke updated `activeFilters.text` and ran `filterGamesWithState` (Library) or scheduled a catalog fetch, causing input lag on large libraries and a wasted local-filter pass even while browsing the server-side catalog. The input still echoes instantly from local state; clear bypasses the delay.[PR#398](https://github.com/towerwatchman/Atlas/pull/398)
+ - LewdCorner member tier detection. Atlas now scrapes your LewdCorner account's shop page to determine your membership tier (Standard / Plus) and stores it alongside your credentials. Browse filters content by tier so Plus users see everything while Standard or non-login users only see what their subscription allows.[PR#391](https://github.com/towerwatchman/Atlas/pull/391)
+ - Support `[no-build]` detection in your HEAD commit message when pushing without wanting a release (docs or changelog-only pushes) or merge existing change from original Atlas.[PR#19](https://github.com/codeon89/Atlas/pull/19)
+ - Nightly-Patched releases: the fork ships its own Windows + Linux installer line (`{base}-patched.nightly.{nightly}.{p}`) with a third update channel under Settings > App Updates. Patched builds check only the fork feed; upstream nightlies surface as a read-only notice, never a download. See `docs/FORK-PATCHED-RUNBOOK.md`.[PR#19](https://github.com/codeon89/Atlas/pull/19)
+
+**Previously merged before packaging**
+ - Catalog tag filtering now matches Library and use exact-token filtering (avoid issue of -male +female return no result).[PR#394](https://github.com/towerwatchman/Atlas/pull/394)
+ - Fixed MEGA v1 test timeout — legacy key derivation is intentionally slow and needed a longer test timeout.[PR#392](https://github.com/towerwatchman/Atlas/pull/392)
+ - Fix multiple executable chooser rendering logic, and redo the executable chooser UI.[PR#389](https://github.com/towerwatchman/Atlas/pull/389)
+ - Fixed bat file launcher for Atlas.[PR#388](https://github.com/towerwatchman/Atlas/pull/388)
+ - Allow setting folder and program locations by typing or paste a path directly besides using Browse / Select Folder button.[PR#385](https://github.com/towerwatchman/Atlas/pull/385)
+ - Allow Win download links to show up for Linux platform since Linux can run both Linux version and also use Wine to run Win executable.[PR#377](https://github.com/towerwatchman/Atlas/pull/377)
+ - Support Local Previews Management: [PR#379](https://github.com/towerwatchman/Atlas/pull/379)
+ - Add Media Upload UI, support Custom Previews via file picker, drag upload or URL upload.
+ - Add drag sort interaction in MediaTab, preserve sorting order.
+ - Fix existing Downloaded Assets Issues not skip already-download entries.
+ - Add scrolling to Downloads page.The scrollbar is hidden but it will show up if hover on the right side.[PR#376](https://github.com/towerwatchman/Atlas/pull/376)
+ - Add Buzzheavier host support (`buzzheavier.com`, `bzzhr.to`, `bzzhr.co`). Note: Each time IP change there will be a quick Cloudflare auto-resolve window, and the challenge result will persist (certain cookies from the throwaway partition is persist instead of complete partition removal prior).[PR#375](https://github.com/towerwatchman/Atlas/pull/375)
+ - Add release verstion github page redirect when clicking on app version [PR#373](https://github.com/towerwatchman/Atlas/pull/373)
+ - Fixed the colour pickers in the Banner Editor's Layout tab closing as soon as the colour changed, so the slider and shade square could only be click-selected and never dragged open. The per-field editor (`Inspector`) was defined *inside* the editor's render body, which makes React treat it as a new component type on every re-render; the first `onChange` re-rendered the editor, remounted the whole inspector, and destroyed the `` the native dialog was bound to. `Inspector` is now module-scope and takes its state as props, so its identity is stable and the picker stays open through a drag. Size & Image and Panels tabs were unaffected.[PR#372](https://github.com/towerwatchman/Atlas/pull/372)
+ - Implement add/remove wishlist in Browse mode context menu that trigger `toggleWishlist` action for non-local rows, Using optimistc UI approach to dispatch the db update, and the success broadcast triggers the renderer so grid view without triggering full refresh. The `wishlist-updated` broadcast is now source-tagged: context-menu toggles skip the catalog refetch (optimistic UI already flipped the row), while the extension path keeps it (no optimistic UI exists there). [PR#368](https://github.com/towerwatchman/Atlas/pull/368)
+ - Fixed slow "wishlist only" filtering in Browse and Library by 1. Adding indexes on columns used in query and 2. Splitting a single multi-OR subquery into separate EXISTS clauses. [PR#367](https://github.com/towerwatchman/Atlas/pull/367)
+ - Fix and remove the redundant isWishlistEntry memory flag which was set but never unset and cause unexpected behavior on entry display regarding wishlist. The isWishlisted logic will check the data from wishlist_entries instead. Note: the IPC behavior is not related and not updated. [PR#366](https://github.com/towerwatchman/Atlas/pull/366)
+ - Remove the 'has Steam mapping' quick filter. [PR#360](https://github.com/towerwatchman/Atlas/pull/360)
-## Merged to thetowerman/Atlas's Nightly
-- Catalog tag filtering now matches Library and use exact-token filtering (avoid issue of -male +female return no result).[PR#394](https://github.com/towerwatchman/Atlas/pull/394)
-- Fixed MEGA v1 test timeout — legacy key derivation is intentionally slow and needed a longer test timeout.[PR#392](https://github.com/towerwatchman/Atlas/pull/392)
-- Fix multiple executable chooser rendering logic, and redo the executable chooser UI.[PR#389](https://github.com/towerwatchman/Atlas/pull/389)
-- Fixed bat file launcher for Atlas.[PR#388](https://github.com/towerwatchman/Atlas/pull/388)
-- Allow setting folder and program locations by typing or paste a path directly besides using Browse / Select Folder button.[PR#385](https://github.com/towerwatchman/Atlas/pull/385)
-- Allow Win download links to show up for Linux platform since Linux can run both Linux version and also use Wine to run Win executable.[PR#377](https://github.com/towerwatchman/Atlas/pull/377)
-- Support Local Previews Management: [PR#379](https://github.com/towerwatchman/Atlas/pull/379)
- - Add Media Upload UI, support Custom Previews via file picker, drag upload or URL upload.
- - Add drag sort interaction in MediaTab, preserve sorting order.
- - Fix existing Downloaded Assets Issues not skip already-download entries.
-- Add scrolling to Downloads page.The scrollbar is hidden but it will show up if hover on the right side.[PR#376](https://github.com/towerwatchman/Atlas/pull/376)
-- Add Buzzheavier host support (`buzzheavier.com`, `bzzhr.to`, `bzzhr.co`). Note: Each time IP change there will be a quick Cloudflare auto-resolve window, and the challenge result will persist (certain cookies from the throwaway partition is persist instead of complete partition removal prior).[PR#375](https://github.com/towerwatchman/Atlas/pull/375)
-- Add release verstion github page redirect when clicking on app version [PR#373](https://github.com/towerwatchman/Atlas/pull/373)
-- Fixed the colour pickers in the Banner Editor's Layout tab closing as soon as the colour changed, so the slider and shade square could only be click-selected and never dragged open. The per-field editor (`Inspector`) was defined *inside* the editor's render body, which makes React treat it as a new component type on every re-render; the first `onChange` re-rendered the editor, remounted the whole inspector, and destroyed the `` the native dialog was bound to. `Inspector` is now module-scope and takes its state as props, so its identity is stable and the picker stays open through a drag. Size & Image and Panels tabs were unaffected.[PR#372](https://github.com/towerwatchman/Atlas/pull/372)
-- Implement add/remove wishlist in Browse mode context menu that trigger `toggleWishlist` action for non-local rows, Using optimistc UI approach to dispatch the db update, and the success broadcast triggers the renderer so grid view without triggering full refresh. The `wishlist-updated` broadcast is now source-tagged: context-menu toggles skip the catalog refetch (optimistic UI already flipped the row), while the extension path keeps it (no optimistic UI exists there). [PR#368](https://github.com/towerwatchman/Atlas/pull/368)
-- Fixed slow "wishlist only" filtering in Browse and Library by 1. Adding indexes on columns used in query and 2. Splitting a single multi-OR subquery into separate EXISTS clauses. [PR#367](https://github.com/towerwatchman/Atlas/pull/367)
-- Fix and remove the redundant isWishlistEntry memory flag which was set but never unset and cause unexpected behavior on entry display regarding wishlist. The isWishlisted logic will check the data from wishlist_entries instead. Note: the IPC behavior is not related and not updated. [PR#366](https://github.com/towerwatchman/Atlas/pull/366)
-- Remove the 'has Steam mapping' quick filter. [PR#360](https://github.com/towerwatchman/Atlas/pull/360)
\ No newline at end of file
diff --git a/PATCHES.md b/PATCHES.md
new file mode 100644
index 0000000..a98fba3
--- /dev/null
+++ b/PATCHES.md
@@ -0,0 +1,41 @@
+# CUMMULATIVE PATCHES
+
+## Pending Patched Changes
+ *Changes that's already on the fork and waiting to be reviewed for merge into original Atlas*
+ - Removed the stale restart popup and hint on the Show debug console toggle — it applies immediately to all open windows.[PR#399](https://github.com/towerwatchman/Atlas/pull/399)
+ - (Dev-Only) DevTools no longer auto-opens in dev mode unless explicitly enabled in config.
+ - Update debounce logic for Browse and Library: Search in Catalog Browse and Library now debounces the text input and waits for a pause before filtering. Previously every keystroke updated `activeFilters.text` and ran `filterGamesWithState` (Library) or scheduled a catalog fetch, causing input lag on large libraries and a wasted local-filter pass even while browsing the server-side catalog. The input still echoes instantly from local state; clear bypasses the delay.[PR#398](https://github.com/towerwatchman/Atlas/pull/398)
+
+
+## Exclusive Fork Changes
+ *Any fork-only changes that is not accepted for merged from upstream*
+ - LewdCorner member tier detection. Atlas now scrapes your LewdCorner account's shop page to determine your membership tier (Standard / Plus) and stores it alongside your credentials. Browse filters content by tier so Plus users see everything while Standard or non-login users only see what their subscription allows.[PR#391](https://github.com/towerwatchman/Atlas/pull/391)
+
+## Fork's System Change
+ *internal change that's not features but backend requirement for forks to release self-builds*
+ - Support Linux builds & patch ci runs. [PR#20](https://github.com/codeon89/Atlas/pull/20)
+ - Support `[no-build]` detection in your HEAD commit message when pushing without wanting a release (docs or changelog-only pushes) or merge existing change from original Atlas.[PR#19](https://github.com/codeon89/Atlas/pull/19)
+ - Nightly-Patched releases: the fork ships its own Windows + Linux installer line (`{base}-patched.nightly.{nightly}.{p}`) with a third update channel under Settings > App Updates. Patched builds check only the fork feed; upstream nightlies surface as a read-only notice, never a download. See `docs/FORK-PATCHED-RUNBOOK.md`. [PR#19](https://github.com/codeon89/Atlas/pull/19)
+
+
+## Merged to thetowerman/Atlas's Nightly
+ - Catalog tag filtering now matches Library and use exact-token filtering (avoid issue of -male +female return no result).[PR#394](https://github.com/towerwatchman/Atlas/pull/394)
+ - Fixed MEGA v1 test timeout — legacy key derivation is intentionally slow and needed a longer test timeout.[PR#392](https://github.com/towerwatchman/Atlas/pull/392)
+ - Fix multiple executable chooser rendering logic, and redo the executable chooser UI.[PR#389](https://github.com/towerwatchman/Atlas/pull/389)
+ - Fixed bat file launcher for Atlas.[PR#388](https://github.com/towerwatchman/Atlas/pull/388)
+ - Allow setting folder and program locations by typing or paste a path directly besides using Browse / Select Folder button.[PR#385](https://github.com/towerwatchman/Atlas/pull/385)
+ - Allow Win download links to show up for Linux platform since Linux can run both Linux version and also use Wine to run Win executable.[PR#377](https://github.com/towerwatchman/Atlas/pull/377)
+ - Support Local Previews Management: [PR#379](https://github.com/towerwatchman/Atlas/pull/379)
+ - Add Media Upload UI, support Custom Previews via file picker, drag upload or URL upload.
+ - Add drag sort interaction in MediaTab, preserve sorting order.
+ - Fix existing Downloaded Assets Issues not skip already-download entries.
+ - Add scrolling to Downloads page.The scrollbar is hidden but it will show up if hover on the right side.[PR#376](https://github.com/towerwatchman/Atlas/pull/376)
+ - Add Buzzheavier host support (`buzzheavier.com`, `bzzhr.to`, `bzzhr.co`). Note: Each time IP change there will be a quick Cloudflare auto-resolve window, and the challenge result will persist (certain cookies from the throwaway partition is persist instead of complete partition removal prior).[PR#375](https://github.com/towerwatchman/Atlas/pull/375)
+ - Add release verstion github page redirect when clicking on app version [PR#373](https://github.com/towerwatchman/Atlas/pull/373)
+ - Fixed the colour pickers in the Banner Editor's Layout tab closing as soon as the colour changed, so the slider and shade square could only be click-selected and never dragged open. The per-field editor (`Inspector`) was defined *inside* the editor's render body, which makes React treat it as a new component type on every re-render; the first `onChange` re-rendered the editor, remounted the whole inspector, and destroyed the `` the native dialog was bound to. `Inspector` is now module-scope and takes its state as props, so its identity is stable and the picker stays open through a drag. Size & Image and Panels tabs were unaffected.[PR#372](https://github.com/towerwatchman/Atlas/pull/372)
+ - Implement add/remove wishlist in Browse mode context menu that trigger `toggleWishlist` action for non-local rows, Using optimistc UI approach to dispatch the db update, and the success broadcast triggers the renderer so grid view without triggering full refresh. The `wishlist-updated` broadcast is now source-tagged: context-menu toggles skip the catalog refetch (optimistic UI already flipped the row), while the extension path keeps it (no optimistic UI exists there). [PR#368](https://github.com/towerwatchman/Atlas/pull/368)
+ - Fixed slow "wishlist only" filtering in Browse and Library by 1. Adding indexes on columns used in query and 2. Splitting a single multi-OR subquery into separate EXISTS clauses. [PR#367](https://github.com/towerwatchman/Atlas/pull/367)
+ - Fix and remove the redundant isWishlistEntry memory flag which was set but never unset and cause unexpected behavior on entry display regarding wishlist. The isWishlisted logic will check the data from wishlist_entries instead. Note: the IPC behavior is not related and not updated. [PR#366](https://github.com/towerwatchman/Atlas/pull/366)
+ - Remove the 'has Steam mapping' quick filter. [PR#360](https://github.com/towerwatchman/Atlas/pull/360)
+
+
diff --git a/docs/FORK-PATCHED-RELEASES.md b/docs/FORK-PATCHED-RELEASES.md
index 57431c3..9151907 100644
--- a/docs/FORK-PATCHED-RELEASES.md
+++ b/docs/FORK-PATCHED-RELEASES.md
@@ -6,7 +6,9 @@ Plain-language reference for the fork's own release line. Procedures live in
## What it is
Nightly-Patched is this fork's installable release line: upstream Atlas nightly
-plus the fork's patches, as a Windows installer with working auto-update.
+plus the fork's patches, as Windows and Linux installers with working
+auto-update (Windows `exe`, Linux `deb`/`AppImage`/`pacman`, all on the one
+draft release).
Settings offers three update channels — Stable and Nightly (upstream's,
explicit opt-in with a replacing warning) and Nightly-Patched (this fork, the
default for fork builds).
diff --git a/docs/FORK-PATCHED-RUNBOOK.md b/docs/FORK-PATCHED-RUNBOOK.md
index fb4ee89..7cb5a12 100644
--- a/docs/FORK-PATCHED-RUNBOOK.md
+++ b/docs/FORK-PATCHED-RUNBOOK.md
@@ -45,9 +45,10 @@ No prior releases exist, so counting starts at zero and the first release is
## Fork repository settings (one-time)
-In the fork's GitHub Actions settings, disable the upstream `main` and
-`nightly` workflows so a stray push can never publish upstream. `pr-policy`
-stays enabled: it is fork-aware (accepts `nightly-patched` bases, no
-changelog or AI-disclosure enforcement), so it gates fork PRs on base and
-tests instead of failing them.
+In the fork's GitHub Actions settings, disable the upstream `main`, `nightly`,
+and `CI` workflows so a stray push can never publish upstream and PRs run the
+fork's cached `ci-patched.yml` instead of both (same gate, skips `npm ci` when
+the lock file is unchanged). `pr-policy` stays enabled: it is fork-aware
+(accepts `nightly-patched` bases, no changelog or AI-disclosure enforcement),
+so it gates fork PRs on base and tests instead of failing them.
Review upstream changes to it on every merge-down.
diff --git a/scripts/build-patched.js b/scripts/build-patched.js
index 968dc10..82e4296 100644
--- a/scripts/build-patched.js
+++ b/scripts/build-patched.js
@@ -31,6 +31,13 @@ function readUpstreamNightly() {
return parseNonNegativeInt(raw, 'UPSTREAM_NIGHTLY')
}
+// Which builder target to use follows the runner OS, so one script serves
+// both CI legs without flags to get wrong. PATCHED_PLATFORM overrides it
+// for local checks (e.g. asserting the Linux branch resolves on Windows).
+function resolvePatchedTarget(platform = process.env.PATCHED_PLATFORM || process.platform) {
+ return platform === 'linux' ? 'linux' : 'windows'
+}
+
// Builds `0.9.9-patched.nightly.494.1` from its parts. The base is everything
// before the first hyphen, so a prerelease base can't leak into the tail.
function resolvePatchedVersion(pkgVersion, nightly, number) {
@@ -58,9 +65,13 @@ async function run() {
}
const version = resolvePatchedVersion(pkg.version, nightly, number)
const commit = execFileSync('git', ['rev-parse', 'HEAD'], { cwd: projectDir, encoding: 'utf8', windowsHide: true }).trim()
+ // One leg per runner OS; both upload to the same draft release.
+ const target = resolvePatchedTarget()
await build({
projectDir,
- targets: Platform.WINDOWS.createTarget(['nsis'], Arch.x64),
+ targets: target === 'linux'
+ ? Platform.LINUX.createTarget(['deb', 'AppImage', 'pacman'], Arch.x64)
+ : Platform.WINDOWS.createTarget(['nsis'], Arch.x64),
publish: publish ? 'always' : 'never',
config: {
directories: { output: `release/patched-${nightly}-${number}` },
@@ -90,4 +101,4 @@ if (require.main === module) {
})
}
-module.exports = { resolvePatchedVersion, parseNonNegativeInt, readUpstreamNightly }
+module.exports = { resolvePatchedVersion, parseNonNegativeInt, readUpstreamNightly, resolvePatchedTarget }
diff --git a/tests/patched-build.test.js b/tests/patched-build.test.js
index b65cfe5..53bf876 100644
--- a/tests/patched-build.test.js
+++ b/tests/patched-build.test.js
@@ -4,7 +4,7 @@ import { createRequire } from 'node:module'
const require = createRequire(import.meta.url)
// Required, never executed: the builder only runs under `require.main`, so
// importing is side-effect free (no electron-builder load, no git, no disk).
-const { resolvePatchedVersion, parseNonNegativeInt, readUpstreamNightly } = require('../scripts/build-patched.js')
+const { resolvePatchedVersion, parseNonNegativeInt, readUpstreamNightly, resolvePatchedTarget } = require('../scripts/build-patched.js')
// Same semver copy electron-updater compares with: a version that only parses
// under a different copy is a version the updater itself would reject.
const semver = require('electron-updater/node_modules/semver')
@@ -73,6 +73,18 @@ describe('fork version ordering (Decision 2)', () => {
})
})
+describe('resolvePatchedTarget', () => {
+ // One script serves both CI legs: the runner OS decides nsis vs
+ // deb/AppImage/pacman, so there is no flag to pass wrong.
+ it.each([
+ ['linux', 'linux'],
+ ['win32', 'windows'],
+ ['darwin', 'windows'],
+ ])('maps %p to %p', (platform, expected) => {
+ expect(resolvePatchedTarget(platform)).toBe(expected)
+ })
+})
+
describe('readUpstreamNightly', () => {
// Asserts shape, not the pinned number: this file is bumped at every
// merge-down and the test must survive that bump.
diff --git a/tests/patched-ci-workflow.test.js b/tests/patched-ci-workflow.test.js
new file mode 100644
index 0000000..05b955f
--- /dev/null
+++ b/tests/patched-ci-workflow.test.js
@@ -0,0 +1,32 @@
+import { describe, it, expect } from 'vitest'
+import fs from 'node:fs'
+import path from 'node:path'
+import { fileURLToPath } from 'node:url'
+
+// String assertions, deliberately: pins the fork CI's load-bearing design
+// (fork-only guard, cache-before-install, same gate as upstream) without a
+// YAML parser dependency, same convention as patched-release-workflow.test.js.
+const here = path.dirname(fileURLToPath(import.meta.url))
+const workflow = fs.readFileSync(path.join(here, '../.github/workflows/ci-patched.yml'), 'utf8')
+const upstream = fs.readFileSync(path.join(here, '../.github/workflows/ci.yml'), 'utf8')
+
+describe('ci-patched workflow', () => {
+ it('runs only in the fork repo', () => {
+ expect(workflow).toContain("github.repository == 'codeon89/Atlas'")
+ })
+
+ it('restores node_modules and skips reinstall on a hit', () => {
+ expect(workflow).toContain('path: node_modules')
+ expect(workflow).toContain("steps.nm-cache.outputs.cache-hit != 'true'")
+ expect(workflow.indexOf('Restore node_modules')).toBeLessThan(workflow.indexOf('Install dependencies'))
+ })
+
+ // Same gate as upstream CI: extension build, vitest, full check. A step
+ // dropped here silently weakens every fork PR, so pin them all.
+ it('runs the same gate steps as upstream CI', () => {
+ for (const step of ['npm run build:extension', 'npm run test:run', 'npm run check']) {
+ expect(workflow).toContain(step)
+ expect(upstream).toContain(step)
+ }
+ })
+})
diff --git a/tests/patched-release-workflow.test.js b/tests/patched-release-workflow.test.js
index f11d749..af86336 100644
--- a/tests/patched-release-workflow.test.js
+++ b/tests/patched-release-workflow.test.js
@@ -48,6 +48,22 @@ describe('nightly-patched release workflow', () => {
expect(workflow.indexOf('.exe.blockmap')).toBeGreaterThan(verifyAt)
})
+ // Both legs share one draft: publishing with either platform missing would
+ // ship a half-populated prerelease, the failure upstream's gate exists for.
+ it('builds both platforms and gates publish on both', () => {
+ expect(workflow).toContain('os: [windows-latest, ubuntu-latest]')
+ expect(workflow).toContain('libarchive-tools')
+ expect(workflow).toContain('linux-patched-packages')
+ expect(workflow).toContain('patched-linux.yml')
+ expect(workflow).toContain('.AppImage')
+ })
+
+ it('scans Linux packages alongside the Windows installer', () => {
+ const scanAt = workflow.indexOf('Scan artifacts')
+ expect(scanAt).toBeGreaterThan(-1)
+ expect(workflow.slice(scanAt)).toContain('*.deb')
+ })
+
it('publishes as a non-latest prerelease only after verification', () => {
const editAt = workflow.indexOf('gh release edit')
expect(editAt).toBeGreaterThan(-1)