Merge branch 'master' into claude/server-side-rom-patching-EaQck

This commit is contained in:
Georges-Antoine Assi
2026-06-23 21:25:07 -04:00
3 changed files with 35 additions and 2 deletions

View File

@@ -1,6 +1,6 @@
import { mount } from "@vue/test-utils";
import { createPinia, setActivePinia } from "pinia";
import { beforeEach, describe, expect, it } from "vitest";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { defineComponent } from "vue";
import storeGalleryRoms from "@/v2/stores/galleryRoms";
import { getCoverRatio, setCoverRatio, useGalleryCoverRatios } from "./index";
@@ -58,6 +58,30 @@ describe("useGalleryCoverRatios", () => {
onCardRatio({ romId: 301, ratio: 0.9 }); // delta 0.2 ≥ 0.01 → updates
expect(ratioAt(0)).toBeCloseTo(0.9);
});
// Regression: GameCover writes the shared `ratioByKey` store (setCoverRatio)
// synchronously BEFORE emitting `@ratio`. Deduping the re-pack against that
// same store made the first report look like "no change", so the flow-packer
// never reflowed off its default ratio and wide covers overflowed the row.
it("schedules a re-pack when the shared store was pre-seeded by the cover", () => {
vi.useFakeTimers();
try {
storeGalleryRoms().romIdIndex = [501];
const { ratioVersion, onCardRatio } = withComposable(() =>
useGalleryCoverRatios(),
);
// Cover paints first: seed the shared map, then emit the same ratio.
setCoverRatio({ romId: 501 }, 1.0);
const before = ratioVersion.value;
onCardRatio({ romId: 501, ratio: 1.0 });
vi.advanceTimersByTime(400);
expect(ratioVersion.value).toBe(before + 1);
} finally {
vi.useRealTimers();
}
});
});
describe("getCoverRatio / setCoverRatio (shared store)", () => {

View File

@@ -79,6 +79,8 @@ export function useGalleryCoverRatios() {
const ratioVersion = ref(0);
const maxRatio = ref(0);
let bumpTimer: ReturnType<typeof setTimeout> | null = null;
// Ratios already folded into the packed layout.
const packedRatio = new Map<number, number>();
/** Record a cover's measured ratio (GameCard's `@ratio` handler). Reads
* /writes the map directly by numeric rom id — no key object / string. */
@@ -86,8 +88,9 @@ export function useGalleryCoverRatios() {
// Track the max on every report (even cached re-paints with an
// unchanged ratio) so it rebuilds correctly after a context reset.
if (payload.ratio > maxRatio.value) maxRatio.value = payload.ratio;
const prev = ratioByKey.get(payload.romId);
const prev = packedRatio.get(payload.romId);
if (prev != null && Math.abs(prev - payload.ratio) < RATIO_EPSILON) return;
packedRatio.set(payload.romId, payload.ratio);
ratioByKey.set(payload.romId, payload.ratio);
if (bumpTimer) return;
bumpTimer = setTimeout(() => {

View File

@@ -118,6 +118,12 @@ export default defineConfig(({ mode }) => {
extensions: [".js", ".json", ".jsx", ".mjs", ".ts", ".tsx", ".vue"],
},
server: {
watch: {
// Never crawl the served library resources: this path is a symlink
// into the user's library (covers, screenshots) and can hold hundreds
// of thousands of files, which OOMs the dev server's file watcher.
ignored: ["**/assets/romm/resources/**", "**/assets/romm/resources"],
},
proxy: {
"/api": {
target: `http://127.0.0.1:${backendPort}`,