diff --git a/.github/workflows/ci-patched.yml b/.github/workflows/ci-patched.yml index 38d31deb..ee25bfd6 100644 --- a/.github/workflows/ci-patched.yml +++ b/.github/workflows/ci-patched.yml @@ -17,7 +17,7 @@ jobs: 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' + if: github.repository == 'codeon89/Atlas-Patched' runs-on: ubuntu-latest timeout-minutes: 15 steps: diff --git a/.github/workflows/nightly-patched.yml b/.github/workflows/nightly-patched.yml index a048e9f7..58994ad4 100644 --- a/.github/workflows/nightly-patched.yml +++ b/.github/workflows/nightly-patched.yml @@ -23,7 +23,7 @@ jobs: # This file must never publish from anywhere else: not from a fork of this # fork, not from a feature branch. Downstream jobs cascade-skip when this # one does, so guarding here covers the whole workflow. - if: github.repository == 'codeon89/Atlas' && github.ref == 'refs/heads/nightly-patched' + if: github.repository == 'codeon89/Atlas-Patched' && github.ref == 'refs/heads/nightly-patched' runs-on: ubuntu-latest timeout-minutes: 20 outputs: diff --git a/.github/workflows/pr-policy.yml b/.github/workflows/pr-policy.yml index f054251e..1424ab8c 100644 --- a/.github/workflows/pr-policy.yml +++ b/.github/workflows/pr-policy.yml @@ -5,7 +5,7 @@ name: PR Policy # the heavier CI job so contributors find out about a wrong base branch in # seconds rather than after a fifteen-minute build. # -# Fork divergence (codeon89/Atlas): Rule 1 also accepts `nightly-patched` +# Fork divergence (codeon89/Atlas-Patched): Rule 1 also accepts `nightly-patched` # (the fork's integration branch); changelog and AI-disclosure rules are not # enforced. Review upstream changes to this file on every merge-down. diff --git a/CHANGELOG.PATCHED.md b/CHANGELOG.PATCHED.md index b05d5213..0b802185 100644 --- a/CHANGELOG.PATCHED.md +++ b/CHANGELOG.PATCHED.md @@ -1,15 +1,18 @@ # CHANGELOG - PATCHED +**v0.9.9-patched.nightly.494.3** + - Importer and Library folder scheme now support `{atlasId}`, so installs can be matched back to AtlasDB on re-import / rebuild.[#404](https://github.com/towerwatchman/Atlas/pull/404) + **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.[#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. [#20](https://github.com/codeon89/Atlas/pull/20) + - Nightly-Patched: support Linux builds & patch ci runs. [#20](https://github.com/codeon89/Atlas-Patched/pull/20) **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.[#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.[#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.[#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`.[#19](https://github.com/codeon89/Atlas/pull/19) + - 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.[#19](https://github.com/codeon89/Atlas-Patched/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`.[#19](https://github.com/codeon89/Atlas-Patched/pull/19) **Previously merged before packaging** diff --git a/CHANGELOG.md b/CHANGELOG.md index f8d5da7d..0b7a4439 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,6 @@ - Path resolution highlighting: red if invalid, green if path exists or pass the check. ### Added -- LewdCorner member tier detection. Atlas now scrapes your LewdCorner account's shop page to determine your membership tier (Standard / Plus / non-logged in) 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. Tier is shown immediately on first connect, rechecked periodically (configurable via `lcTierRecheckHours`), and forced on re-link. A dev-only console warning fires when the scraped tier disagrees with the stored value. (`electron/accounts/xenforoAuth.js`, `electron/accounts/accountStore.js`, `electron/db/catalogIndex.js`, `src/components/settings/Accounts.jsx`) - Custom media uploads in the Game Details Media tab: add preview images from local files, a drag-and-drop zone, or an image URL, with live progress. Previews can be reordered by drag and the order persists in a new `preview_sort` table keyed by remote URL (or relative path for custom uploads), so it survives re-downloads, stream/download switches and metadata refreshes. Previews now carry a source logo and a storage-location badge, and custom previews can be deleted independently of downloaded ones. - Add Buzzheavier host support (`buzzheavier.com`, `bzzhr.to`, `bzzhr.co`). The download route is behind a Cloudflare challenge, so the resolve runs in a browser window: it clicks the htmx download button, captures the `HX-Redirect` (or an attachment via `will-download`), and hands the resolved CDN link plus the browser's own cookies/UA back to the downloader. The resolve partition is persistent so a solved challenge survives a restart -- only Cloudflare's own challenge cookies are kept, everything else is stripped after each resolve -- and resolves run one at a time, since a shared session cannot carry two concurrently. Each time your IP changes there is a brief auto-resolve window while the challenge is re-solved. - The version readout in the topnav and sidebar is now a button that opens that version's GitHub release page. The tag it builds matches what the release workflows publish -- `v` for stable and `v-nightly.` for nightly -- so it lands on the real release rather than a 404. (#143) diff --git a/PATCHES.md b/PATCHES.md index 851f3a31..7106b1d9 100644 --- a/PATCHES.md +++ b/PATCHES.md @@ -2,6 +2,7 @@ ## Pending Patched Changes *Changes that's already on the fork and waiting to be reviewed for merge into original Atlas* + - Importer and Library folder scheme now support `{atlasId}`, so installs can be matched back to AtlasDB on re-import / rebuild.[#404](https://github.com/towerwatchman/Atlas/pull/404) - Removed the stale restart popup and hint on the Show debug console toggle — it applies immediately to all open windows.[#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.[#398](https://github.com/towerwatchman/Atlas/pull/398) @@ -13,9 +14,9 @@ ## 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. [#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.[#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`. [#19](https://github.com/codeon89/Atlas/pull/19) + - Support Linux builds & patch ci runs. [#20](https://github.com/codeon89/Atlas-Patched/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.[#19](https://github.com/codeon89/Atlas-Patched/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`. [#19](https://github.com/codeon89/Atlas-Patched/pull/19) ## Merged to thetowerman/Atlas's Nightly diff --git a/docs/FORK-PATCHED-RELEASES.md b/docs/FORK-PATCHED-RELEASES.md index 9151907d..e9a7f515 100644 --- a/docs/FORK-PATCHED-RELEASES.md +++ b/docs/FORK-PATCHED-RELEASES.md @@ -1,4 +1,4 @@ -# Nightly-Patched releases (codeon89/Atlas) +# Nightly-Patched releases (codeon89/Atlas-Patched) Plain-language reference for the fork's own release line. Procedures live in `docs/FORK-PATCHED-RUNBOOK.md`. diff --git a/docs/FORK-PATCHED-RUNBOOK.md b/docs/FORK-PATCHED-RUNBOOK.md index 7cb5a126..0a1dfd8d 100644 --- a/docs/FORK-PATCHED-RUNBOOK.md +++ b/docs/FORK-PATCHED-RUNBOOK.md @@ -1,4 +1,4 @@ -# Nightly-Patched runbook (codeon89/Atlas) +# Nightly-Patched runbook (codeon89/Atlas-Patched) Evergreen fork-only operations. Plain-language overview: `docs/FORK-PATCHED-RELEASES.md`. diff --git a/electron/ipc/importer.js b/electron/ipc/importer.js index 842729cd..11e0d2ee 100644 --- a/electron/ipc/importer.js +++ b/electron/ipc/importer.js @@ -4771,6 +4771,7 @@ ipcMain.handle("downloads-install", async (event, { id, version, onComplete, kee currentConfig?.Library?.libraryFolderStructure, { f95Id: record.f95_id || record.f95Id || "", + atlasId: record.atlas_id || record.atlasId || "", engine: record.engine || "Unknown", creator: record.creator, title: record.title, diff --git a/electron/library/importRules.js b/electron/library/importRules.js index 3c1a830b..3ed7ecf4 100644 --- a/electron/library/importRules.js +++ b/electron/library/importRules.js @@ -73,6 +73,7 @@ function buildStructuredImportPath(targetLibrary, format, game) { if (key === "version") return normalizeVersionName(game.version); if (key === "engine") return game.engine || "Unknown"; if (key === "f95id") return game.f95Id || "Unknown"; + if (key === "atlasid") return game.atlasId || game.atlas_id || "Unknown"; if (key === "lcid" || key === "lewdcornerid") return game.lcId || game.lewdCornerId || "Unknown"; return "Unknown"; }), diff --git a/electron/main.js b/electron/main.js index 1e2a0288..c0efc6fc 100644 --- a/electron/main.js +++ b/electron/main.js @@ -657,7 +657,7 @@ function configureAppUpdateBranch(branch, { resetStatus = false } = {}) { // upstream. A fork build pointed at upstream would offer to replace itself // with an official build that lacks the fork feed entirely. owner: normalizedBranch === 'patched' ? 'codeon89' : 'towerwatchman', - repo: 'Atlas', + repo: normalizedBranch === 'patched' ? 'Atlas-Patched' : 'Atlas', channel, }) diff --git a/package.json b/package.json index 8dbd7f92..58210462 100644 --- a/package.json +++ b/package.json @@ -108,7 +108,7 @@ "publish": { "provider": "github", "owner": "codeon89", - "repo": "Atlas", + "repo": "Atlas-Patched", "releaseType": "draft", "channel": "patched", "vPrefixedTagName": true diff --git a/scripts/build-patched.js b/scripts/build-patched.js index 82e42963..9e32a130 100644 --- a/scripts/build-patched.js +++ b/scripts/build-patched.js @@ -86,7 +86,7 @@ async function run() { // Deliberately explicit: neither an inherited upstream config nor a new // remote can redirect a personal publish into someone else's repository. publish: { - provider: 'github', owner: 'codeon89', repo: 'Atlas', channel: 'patched', + provider: 'github', owner: 'codeon89', repo: 'Atlas-Patched', channel: 'patched', releaseType: 'draft', vPrefixedTagName: true, }, }, diff --git a/src/components/downloads/LibraryStructureModal.jsx b/src/components/downloads/LibraryStructureModal.jsx index 529060aa..8d7fe07b 100644 --- a/src/components/downloads/LibraryStructureModal.jsx +++ b/src/components/downloads/LibraryStructureModal.jsx @@ -59,7 +59,8 @@ const previewPath = (pattern) => .replace(/\{title\}/g, SAMPLE.title) .replace(/\{version\}/g, SAMPLE.version) .replace(/\{engine\}/g, 'RenPy') - .replace(/\{f95Id\}/g, '12345'), + .replace(/\{f95Id\}/g, '12345') + .replace(/\{atlasId\}/g, '6789'), ) .filter(Boolean) diff --git a/src/components/importer/Importer.jsx b/src/components/importer/Importer.jsx index bf23408c..dca0d775 100644 --- a/src/components/importer/Importer.jsx +++ b/src/components/importer/Importer.jsx @@ -995,6 +995,7 @@ const Importer = () => { engine: groups.engine || '', f95Id: groups.f95id || '', lcId: groups.lcid || '', + atlasId: groups.atlasid || '', } if (!cancelled) { setLivePreview({ diff --git a/src/components/importer/steps/SettingsStep.jsx b/src/components/importer/steps/SettingsStep.jsx index 0238bbff..f5c54304 100644 --- a/src/components/importer/steps/SettingsStep.jsx +++ b/src/components/importer/steps/SettingsStep.jsx @@ -31,6 +31,7 @@ export default function SettingsStep({ { label: 'Creator / Title - Version', value: '{creator}/{title} - {version}' }, { label: 'Title / Version, Creator', value: '{title}/{version},{creator}' }, { label: 'F95 ID / Title / Version', value: '{f95Id}/{title}/{version}' }, + { label: 'Atlas ID / Title / Version', value: '{atlasId}/{title}/{version}' }, { label: 'LewdCorner ID / Title / Version', value: '{lcId}/{title}/{version}' }, ] // "Auto detect" (unstructured name guessing) has been removed for now, so the @@ -167,6 +168,7 @@ export default function SettingsStep({ ['Engine', livePreview.fields.engine, false], ['F95 ID', livePreview.fields.f95Id, false], ['LC ID', livePreview.fields.lcId, false], + ['Atlas ID', livePreview.fields.atlasId, false], ].filter(([, value, always]) => always || value).map(([label, value]) => (
{label}
@@ -221,7 +223,7 @@ export default function SettingsStep({ onChange={(e) => setLibraryFormat(e.target.value)} spellCheck={false} placeholder="{creator}/{title}/{version}" - title="Destination path template. Tokens: {creator} {title} {version} {f95Id} {lcId}" + title="Destination path template. Tokens: {creator} {title} {version} {f95Id} {lcId} {atlasId}" className="sm:ml-2 flex-1 min-w-0 bg-secondary text-text border border-border rounded-buttonTheme p-1 focus:outline-none focus:ring-1 focus:ring-accent font-mono text-xs" />
diff --git a/src/components/settings/Library.jsx b/src/components/settings/Library.jsx index c072fb4e..ec754fac 100644 --- a/src/components/settings/Library.jsx +++ b/src/components/settings/Library.jsx @@ -277,7 +277,7 @@ const Library = () => {

Used when imports are moved or archives are extracted into the default library folder. Options: {"{creator}"}, {"{title}"}, {"{version}"},{" "} - {"{engine}"}, {"{f95Id}"}. + {"{engine}"}, {"{f95Id}"}, {"{atlasId}"}.
Example: {"{f95Id}/{creator}/{title}/{version}"}

diff --git a/src/utils/releaseUrl.js b/src/utils/releaseUrl.js index e32ed97f..a3ea7f41 100644 --- a/src/utils/releaseUrl.js +++ b/src/utils/releaseUrl.js @@ -18,6 +18,6 @@ import { LINKS } from './links.js' */ export function releaseUrlFor(version) { const tag = String(version ?? '').trim().replace(/^v+/i, '') - const repo = tag.includes('-patched.') ? 'https://github.com/codeon89/Atlas' : LINKS.github + const repo = tag.includes('-patched.') ? 'https://github.com/codeon89/Atlas-Patched' : LINKS.github return `${repo}/releases/tag/v${tag}` } diff --git a/tests/importer-helpers.test.js b/tests/importer-helpers.test.js index ea104ca6..281895f8 100644 --- a/tests/importer-helpers.test.js +++ b/tests/importer-helpers.test.js @@ -79,6 +79,17 @@ describe('buildStructuredImportPath', () => { const out = T.buildStructuredImportPath('/lib', '{lcid}', { lcId: 'LC9' }) expect(out).toBe(path.join('/lib', 'LC9')) }) + + it('supports atlasid from camelCase or snake_case, any casing', () => { + expect(T.buildStructuredImportPath('/lib', '{atlasId}', { atlasId: '456' })).toBe(path.join('/lib', '456')) + expect(T.buildStructuredImportPath('/lib', '{atlasId}', { atlas_id: 456 })).toBe(path.join('/lib', '456')) + expect(T.buildStructuredImportPath('/lib', '{AtlasID}', { atlasId: '456' })).toBe(path.join('/lib', '456')) + }) + + it('falls back to Unknown for a missing atlas id, even mid-segment', () => { + const out = T.buildStructuredImportPath('/lib', '{title} [{atlasId}]/{version}', { title: 'My Game', version: '1.0' }) + expect(out).toBe(path.join('/lib', 'My Game [Unknown]', '1.0')) + }) }) @@ -121,7 +132,7 @@ test('downloads-install passes required fields in buildStructuredImportPath', () if (!obj) throw new Error('buildStructuredImportPath was not given an object literal') const keys = obj.properties.map((p) => p.key.name) - const required = ['f95Id', 'engine', 'creator', 'title', 'version'] + const required = ['f95Id', 'atlasId', 'engine', 'creator', 'title', 'version'] for (const key of required) { expect(keys).toContain(key) diff --git a/tests/patched-ci-workflow.test.js b/tests/patched-ci-workflow.test.js index 05b955f8..da6b719e 100644 --- a/tests/patched-ci-workflow.test.js +++ b/tests/patched-ci-workflow.test.js @@ -12,7 +12,7 @@ const upstream = fs.readFileSync(path.join(here, '../.github/workflows/ci.yml'), describe('ci-patched workflow', () => { it('runs only in the fork repo', () => { - expect(workflow).toContain("github.repository == 'codeon89/Atlas'") + expect(workflow).toContain("github.repository == 'codeon89/Atlas-Patched'") }) it('restores node_modules and skips reinstall on a hit', () => { diff --git a/tests/patched-release-workflow.test.js b/tests/patched-release-workflow.test.js index af863361..79d5d054 100644 --- a/tests/patched-release-workflow.test.js +++ b/tests/patched-release-workflow.test.js @@ -12,7 +12,7 @@ const workflow = fs.readFileSync(path.join(here, '../.github/workflows/nightly-p describe('nightly-patched release workflow', () => { it('fires only on the fork branch and only in the fork repo', () => { expect(workflow).toContain('branches: [nightly-patched]') - expect(workflow).toContain("github.repository == 'codeon89/Atlas' && github.ref == 'refs/heads/nightly-patched'") + expect(workflow).toContain("github.repository == 'codeon89/Atlas-Patched' && github.ref == 'refs/heads/nightly-patched'") }) it('queues runs instead of cancelling (no duplicate release numbers)', () => { diff --git a/tests/patched-updates.test.js b/tests/patched-updates.test.js index f7369ed5..182490eb 100644 --- a/tests/patched-updates.test.js +++ b/tests/patched-updates.test.js @@ -65,7 +65,8 @@ describe('patched update channel', () => { ])('routes %s to its own feed and installed-version baseline', (branch, owner, channel, prerelease, baseline) => { const ctx = updaterContext() ctx.configureAppUpdateBranch(branch) - expect(ctx.autoUpdater.setFeedURL).toHaveBeenCalledWith({ provider: 'github', owner, repo: 'Atlas', channel }) + const repo = branch === 'patched' ? 'Atlas-Patched' : 'Atlas' + expect(ctx.autoUpdater.setFeedURL).toHaveBeenCalledWith({ provider: 'github', owner, repo, channel }) expect(ctx.autoUpdater.channel).toBe(channel) expect(ctx.autoUpdater.allowPrerelease).toBe(prerelease) expect(ctx.autoUpdater.allowDowngrade).toBe(false) diff --git a/tests/release-url.test.js b/tests/release-url.test.js index 2c85eae2..d015cbe4 100644 --- a/tests/release-url.test.js +++ b/tests/release-url.test.js @@ -46,7 +46,7 @@ describe('releaseUrlFor', () => { // even when the user currently follows an official channel. it('points patched builds at the fork releases', () => { expect(releaseUrlFor('0.9.9-patched.nightly.494.1')).toBe( - 'https://github.com/codeon89/Atlas/releases/tag/v0.9.9-patched.nightly.494.1' + 'https://github.com/codeon89/Atlas-Patched/releases/tag/v0.9.9-patched.nightly.494.1' ) }) })