Commit Graph

10181 Commits

Author SHA1 Message Date
Georges-Antoine Assi
dfdf44f74c cleanup 2026-06-22 22:10:48 -04:00
Claude
5a9eda9eeb docs: split v2 constitution into skills, make CLAUDE.md holistic
Reorient the repo for agentic development by outside contributors.

- Rewrite CLAUDE.md from a frontend-v2-only constitution into a lean,
  holistic guide covering both stacks, the v1/v2 split, repo-wide rules
  (English, AI disclosure, branch/PR flow, Trunk, verification), a skills
  index, and a quick command reference.
- Extract the v2 constitution and add backend guidance as seven focused
  skills under .claude/skills/:
  - frontend-v2-components (tiers, file/SFC conventions, anti-patterns)
  - frontend-v2-theming (tokens, dual theme, zero-hex-literal policy)
  - frontend-v2-input (universal input + responsive viewport)
  - frontend-v2-patterns (errors, loading, sockets, persistence, forms,
    permissions, confirmations)
  - frontend-i18n (locale parity rule + check script)
  - backend-development (FastAPI/SQLAlchemy layering, scopes, migrations,
    OpenAPI -> frontend type pipeline, uv/pytest/trunk workflow)
  - pre-pr-verification (per-stack pre-PR gate mirroring CI)
- Track shared skills in git while keeping personal/local Claude config
  ignored.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012EigwEL43km8mskHjT6sjr
2026-06-23 01:49:08 +00:00
Georges-Antoine Assi
e7dd013cfb Merge pull request #3580 from rommapp/claude/youthful-wozniak-ocjhc0
feat(v2): interactive 3D box art on the game detail hero
2026-06-22 21:37:02 -04:00
Georges-Antoine Assi
111fc969be run fmt 2026-06-22 19:40:56 -04:00
Claude
d7c5ab7e06 feat(v2): RBox3D faces right, self-spins, and coasts on flick
- Default orientation now faces right (initialYaw flipped).
- Idle auto-spin actually resumes: the time-based check moved into the RAF
  loop, since a computed reading performance.now() cached and never restarted
  after the first interaction. Quiet window is 2s.
- Flick momentum without a JS decay loop: the release velocity is handed to
  the box as one extra rotation and a CSS ease-out curve coasts it to a stop.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019itLXRfJXGGbhPY3JyqnuN
2026-06-22 23:23:37 +00:00
Claude
1b02bf6a3e fix(v2): RBox3D adopts box-2D dimensions even when cached
Read the front (box-2D) and spine image natural ratios on mount, not only on
the load event — a cached cover decodes before the listener binds, which left
the box stuck at the default ratio instead of the real box-2D proportions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019itLXRfJXGGbhPY3JyqnuN
2026-06-22 22:17:22 +00:00
Claude
ab2b61783b fix(v2): RBox3D rotates on drag instead of selecting faces
Add user-select: none to the box and pointer-events: none to the faces so a
drag always lands on the root (which owns the rotation listeners) and never
starts a text / image selection.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019itLXRfJXGGbhPY3JyqnuN
2026-06-22 22:12:48 +00:00
Georges-Antoine Assi
2631f0e45d Merge pull request #3577 from Spinnich/feature/oidc-allow-registration
feat(auth): add OIDC_ALLOW_REGISTRATION toggle to gate OIDC auto-registration
2026-06-22 18:06:13 -04:00
Georges-Antoine Assi
d38be187f3 Merge branch 'master' into claude/youthful-wozniak-ocjhc0 2026-06-22 18:04:37 -04:00
Georges-Antoine Assi
01b9226265 Merge pull request #3579 from rommapp/v2-ui-tweaks-gg
V2 UI tweaks gg
2026-06-22 18:04:26 -04:00
Georges-Antoine Assi
c4bfbac872 Merge branch 'master' into v2-ui-tweaks-gg 2026-06-22 17:45:59 -04:00
Claude
77980a79ec feat(v2): interactive 3D box art on game detail hero
Adds an RBox3D primitive that builds a rotatable, fake-3D game box from
three flat ScreenScraper scans (front, back, spine) using CSS 3D
transforms. Box proportions derive from the images themselves; it rotates
via pointer drag, arrow keys / gamepad D-pad, and the right analog stick,
drifts gently when idle, and honours prefers-reduced-motion.

The game detail hero (CoverColumn) upgrades to the spinning box when the
"3D box" boxart style is selected and the rom has the full set of faces,
falling back to the flat cover otherwise.

Backend: persist the box-2D-side (spine) scan locally, mirroring the
existing box-2D-back handling — new BOX2D_SIDE media type + box2d_side_path
on ss_metadata, opt-in via scan.media.

- RBox3D primitive + Storybook story (controls + keyboard-rotation play())
- useBoxFaces composable resolving the three faces + a `complete` gate
- box3d-alt i18n key across all locales
- backend BOX2D_SIDE persistence + tests

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019itLXRfJXGGbhPY3JyqnuN
2026-06-22 21:45:39 +00:00
Georges-Antoine Assi
af4b057894 feat(v2): screenshot-forward covers + cover-art PIP for continue-playing & activity
Show a "where you left off" screenshot on the Home continue-playing rail and
the live-activity board, with a small cover-art thumbnail (PIP) in the corner
so the game stays identifiable. Both render at the image's natural aspect.

Backend:
- New shared util `continue_playing_screenshot(rom, latest_save)` resolving the
  image in priority order: latest save's screenshot, then title screen, then
  first gameplay screenshot (None → frontend falls back to cover art).
- `SimpleRomSchema.screenshot_path` populated only on the `last_played` query;
  `get_latest_saves_for_roms` batch handler (+ tests).
- ActivityEntry / ActivityEntrySchema gain `screenshot_path`, computed from the
  session player's latest save in both the socket and REST heartbeat paths.

Frontend:
- New shared `CoverArtPip.vue` (bottom-right 2D cover thumbnail), reused by
  GameCard and ActivityCard.
- Home continue-playing rail uses `screenshot_path` + PIP, natural aspect (no
  forced hero/style).
- Activity board: screenshot-forward cover + PIP, and a wrapping flex layout so
  cards share a uniform height with natural-ratio widths (gallery-card
  behavior).
- GameCover only keys the measured ratio by rom id for the rom's own cover, so
  a `coverSrc` override (screenshot) never pollutes the gallery's ratio cache.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 17:40:56 -04:00
Georges-Antoine Assi
e471efb987 feat(v2): morph gallery cover into the player on Play
Clicking Play at the center of a gallery cover now runs the shared-element
view transition into the /ejs (EmulatorJS) or /ruffle hero, matching the
card → details morph.

- useGameActions gains an optional `coverEl` resolver; `play()` wraps the
  navigation in `morphTransition` (cover → player hero, same `rom-cover-<id>`
  tag the player paints statically) and awaits the push so the snapshot is
  taken after the player renders. GameCard supplies its GameCover box.
- The player heroes only seeded `rom` from `currentRom` (set via GameDetails),
  so a direct gallery→play left `rom` null and the `v-if`-gated hero never
  rendered — nothing to morph into. Seed a lightweight `heroSeed` SimpleRom
  from the gallery store (new `galleryRoms.getRomById`) so the cover paints
  its morph tag immediately; `rom` fills in on mount. Play is disabled until
  the full payload loads.
- Enable hover-motion on both player heroes so the cover spin / hover video
  work there too.
- Arcade systems (arcade / neogeoaes / neogeomvs) skip the cartridge slot-in
  animation (new `isArcadeSystem`).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 12:46:31 -04:00
Georges-Antoine Assi
4a49a762a8 fix(v2): port cover animations to v1's compose-by-property model
Rework useCoverAnimation to mirror v1's useGameAnimation, which composes
two motions on different CSS properties so they don't fight: the spin
owns `transform: rotate` (set per-frame) while the disc/cartridge slot-in
owns `margin-top` (eased in CSS with an overshoot). A single transform for
both couldn't ease the slide independently of the per-frame spin.

- CD launch spins at max while sliding the cover down into the drive;
  cartridge launch seats fully into its bay (1/3 height).
- Drop the cartridge hover animation entirely — matches v1, which only
  spins discs and plays the hover video on hover.
- Enable hover-motion on the EmulatorJS and Ruffle player heroes so the
  hover spin / video work there too.
- Fix a `.ame-cover__img` typo (missing "g") that was silently breaking
  the cover image base styles, and ease `margin` in CSS on `.game-cover__img`
  so the slide composes with the bloom reveal instead of overriding it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 12:19:03 -04:00
Zurdi
5dfe72b1e8 Merge pull request #3578 from rommapp/chore/fix-tiles-animation
feat: tile animations, semantic log module names, and log module filter
2026-06-22 18:14:26 +02:00
zurdi
e232e9e00e test(v2): align cover-ratio debounce test with 350ms re-pack delay
The re-pack debounce in useGalleryCoverRatios was raised from 150ms to
350ms (c5d844e1) to avoid a re-pack storm during fast scroll, but the
test still advanced fake timers by 150ms and expected the debounced
ratioVersion bump to have fired — so it asserted 1 and got 0. Advance
by the new 350ms delay at all three sites. Pre-existing failure on
master, unrelated to this branch's changes; bundled here to unblock CI.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 16:09:13 +00:00
Zurdi
b282a4cac7 Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-22 18:03:51 +02:00
zurdi
8c20fd6547 feat(logging): enhance logging module with dynamic module name resolution and add module filtering options 2026-06-22 15:51:22 +00:00
Georges-Antoine Assi
7b8b35fbc1 fix miximage view preview 2026-06-22 10:06:03 -04:00
Spinnich
11a81c756f feat(auth): add OIDC_ALLOW_REGISTRATION toggle
Gate automatic account creation on OIDC login behind a new OIDC_ALLOW_REGISTRATION environment variable. Defaults to true, preserving the current auto-provisioning behavior; set it to false to run OIDC in an "existing users only" mode, where a login from an email without an existing RomM account is rejected with a 403 instead of silently creating one. Existing users are unaffected either way.

Adds the config constant, env.template and docs entries, and tests covering the enabled/disabled and existing-user paths.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 12:33:00 +00:00
Georges-Antoine Assi
e10a715cf3 improve hotpath 2026-06-22 08:07:24 -04:00
Georges-Antoine Assi
76417cb4b1 Merge branch 'master' into v2-ui-tweaks-gg 2026-06-22 07:49:39 -04:00
Georges-Antoine Assi
367626c21e some UI tweaks 2026-06-22 07:48:52 -04:00
Zurdi
11989296cc Merge pull request #3576 from rommapp/chore/improve-gallery-responsiveness-z
feat(v2): improve gallery responsiveness
2026-06-22 13:47:27 +02:00
zurdi
c5d844e118 feat(v2): improve gallery responsiveness and optimize game actions handling 2026-06-22 11:36:39 +00:00
zurdi
8a4d286cfa feat(activity): add total sessions label to activity localization files
- Added "total-sessions" key to various language JSON files for activity localization.
- Updated ActivityCard component to reflect changes in the activity view.
- Enhanced Activity view to display a live session counter with a tooltip.
- Introduced virtual scroll debugging to monitor performance in the gallery.
- Adjusted layout styles for better responsiveness and visual consistency across components.
2026-06-22 09:23:39 +00:00
Georges-Antoine Assi
ebf535dfc2 Merge pull request #3573 from rommapp/aspect-ratio-no-no
feat(v2): render cover art at its natural aspect ratio
2026-06-21 21:45:36 -04:00
Georges-Antoine Assi
e7d383b285 style(v2): right-align list-view cover in its column
Cover hugs the title side of its fixed-width column (and the skeleton
matches), so the empty space sits on the left and the cover reads as
attached to its title.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 21:43:59 -04:00
Georges-Antoine Assi
41ec6f4de6 fix(v2): give list-view cover its own column so titles align
With natural-aspect covers, the list-row cover lived inside the title cell
and its variable width pushed each row's title/meta to a different x. Add a
dedicated fixed-width (64px) cover column to the shared list grid template —
big enough for any portrait→square game cover — so the title column starts
at the same x on every row. Header and skeleton pick it up via the shared
column config; the cover renders left-aligned at its natural aspect.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 21:33:09 -04:00
Georges-Antoine Assi
44016129b5 feat(v2): list-view covers respect natural aspect; scale placeholder title
- Gallery list view: drop the `fixed` opt-out on its GameCard so the
  row thumbnail renders at the cover's natural aspect (fixed height,
  natural width) like everywhere else. Removes the now-unused `fixed`
  prop / class / CSS branch from GameCard.
- CoverPlaceholder: the title now scales with the cover box (container
  query units, clamp(8px, 8cqmin, 18px), em padding) instead of a fixed
  12px — readable on a tiny list thumb, proportionate on the detail hero.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 21:09:30 -04:00
Georges-Antoine Assi
cdb1f17902 feat(v2): add section headings to HLTB strip and screenshots in overview
The overview's screenshots and How-Long-To-Beat blocks rendered with no
label, unlike the related-games sections. Wrap both in the same labelled
section pattern (renamed `overview-tab__related-*` → `__section-*` since it
now covers non-related blocks too). The HLTB heading is gated on a
`hasHltb` computed so it doesn't show when the strip is empty.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 20:11:33 -04:00
Georges-Antoine Assi
14a7b40475 details cleanup 2026-06-21 20:10:07 -04:00
Georges-Antoine Assi
72fb96ec4c fix(v2): consistent gap between cover and fields in the edit dialog
The edit dialog's hero used a fixed 240px cover column with the cover
centered, so a natural-width cover left variable leftover space — making
the gap to the fields vary by cover shape. Size the column to the cover
(`auto`) so the gap is exactly the grid gap for any cover, and give it a
touch more room (24px).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 19:57:00 -04:00
Georges-Antoine Assi
c8362ed22e feat(v2): use natural cover aspect ratio across remaining surfaces
Extend natural-aspect cover rendering beyond the gallery to the rest of
the app (continue-playing stays a fixed 16:9 hero, by request).

- GameCard size tiers now render fixed-height / natural-width like the
  default card (size just picks the height); covers EditRomDialog (lg)
  and ManageCollectionsDialog (xs) for free. Adds a `fixed` opt-out for
  dense aligned tables.
- GameListRow (compact list view) opts into `fixed` so its cover column
  stays uniform for row alignment.
- Compact fixed-column thumbnails (ScanPlatformRow, DeleteRomDialog,
  RefreshMetadataDialog, ActivityCard) stop cropping (object-fit
  cover → contain): the whole cover shows at its true aspect while the
  slot stays uniform.

Deliberately unchanged: collection mosaics (composite collage art),
save/state screenshots (16:9, not covers), and the provider cover-picker
comparison grids (uniform tiles aid side-by-side selection).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 19:46:01 -04:00
Georges-Antoine Assi
74ef594609 feat(v2): render the Random Pick cover at its native aspect ratio
Swap the hand-rolled RImg + useCoverArt cover resolution in
RandomPickWidget for the shared GameCover, which measures the image's
natural ratio. The thumbnail now renders at a fixed 70px height with
natural width (no crop), matching the gallery, and the widget's cover
plumbing (coverSrc / coverContain / placeholder fallback) collapses into
GameCover.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 19:24:05 -04:00
Georges-Antoine Assi
662dc89a24 Merge branch 'master' into aspect-ratio-no-no 2026-06-21 19:20:09 -04:00
Georges-Antoine Assi
d1a9dbb4fb refactor(v2): extract gallery cover-ratio measurement into a composable
Move the measured-ratio map, debounced ratioVersion, onCardRatio handler,
and ratioAt resolver out of GalleryShell into useGalleryCoverRatios. The
composable owns its debounce-timer cleanup (onBeforeUnmount), so the shell
no longer tracks ratioBumpTimer. Behaviour is unchanged; adds a unit test
for the dedup / debounce / position→rom→ratio mapping.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 19:10:36 -04:00
Georges-Antoine Assi
84ccba6996 Merge pull request #3308 from tmgast/feature/device-flow-authorization
Add device authorization flow for TV-app-style pairing (RFC 8628)
2026-06-21 19:07:08 -04:00
Georges-Antoine Assi
d5f30d1fab docs(v2): collapse verbose comment blocks from the aspect-ratio work
Tighten the multi-line comments added across GameCover/GameCard/
GalleryShell/useGalleryVirtualItems/useResponsiveColumns and the
packFlowRows test to one or two lines each, keeping the load-bearing
rationale and dropping the prose.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 18:06:23 -04:00
Georges-Antoine Assi
8f25796201 refactor(v2): drop unused rowIndex from gallery row item
Since rows became variable-length contiguous runs, rowIndex was hardcoded
to 0 at both build sites and read nowhere. Remove it so a future reader
doesn't mistake 0 for a meaningful index.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 17:58:17 -04:00
Georges-Antoine Assi
fd7d40aa1a docs(v2): note the gallery ratio cache is intentionally unbounded
ratioByRomId is keyed by rom id so a re-visited platform/collection
re-packs instantly without re-waiting on image loads. It's never pruned,
but each entry is two numbers (a few hundred KB even at tens of thousands
of distinct ROMs), so an LRU ceiling isn't worth it. Document the choice
so it reads as deliberate.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 17:55:33 -04:00
Georges-Antoine Assi
e490706028 fix(v2): pin gallery cards to flex-shrink: 0 so covers never crop
The flow-packer sizes a row from cardHeight * ratio (floating point) and
the browser rounds rendered widths, so a "just fits" row can land a hair
over the container. With shrink enabled, fixed-height cards absorb that by
narrowing — cropping the cover via object-fit. Pin shrink to 0 on the
row's children: trades the rare sub-pixel crop for a hair of ragged
overflow, and keeps loading skeletons (default shrink: 1) at packed width.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 17:54:35 -04:00
Georges-Antoine Assi
1f98b2df14 run fmt 2026-06-21 17:47:06 -04:00
Georges-Antoine Assi
4724361f6d refactor: remove per-platform cover aspect-ratio setting
The platform aspect_ratio setting is dropped from the UI and the API
(platform update body + response schema) — nothing consumed it for
rendering, and covers now size to their image's natural aspect.

- SettingsTab: remove the cover-style / aspect-ratio picker (and its
  now-dead helpers, CSS, and unused imports); collapse to a single column.
- update_platform: drop the `aspect_ratio` body field; PlatformSchema no
  longer returns it; utils/platforms stops seeding the default.
- Regenerate the affected frontend types (PlatformSchema, update body).

The DB column stays (out of the update/response scope; dropping it would
be a separate destructive migration) but is no longer read or written
through the API.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 17:43:40 -04:00
Georges-Antoine Assi
389025942f changes from bot review 2026-06-21 17:42:25 -04:00
Zurdi
bce3dfd02e Merge pull request #3572 from rommapp/feat/v2-remove-from-continue-playing
feat(v2): restore "remove from continue playing" action
2026-06-21 23:41:28 +02:00
copilot-swe-agent[bot]
c5148222cc fix: gate canRemoveFromContinuePlaying on roms.user.write scope
Co-authored-by: zurdi15 <34356590+zurdi15@users.noreply.github.com>
2026-06-21 21:34:03 +00:00
Georges-Antoine Assi
e9ab144d39 feat(v2): render cover art at its natural aspect ratio
Stop forcing a fixed box ratio on gallery covers. Cards now share one
height and vary in width to match each cover image's natural aspect, with
no cropping.

- GameCover measures the rendered image's natural ratio on load and drives
  its own aspect-ratio from it (style ratio kept only as a pre-load seed);
  emits the measured ratio.
- GameCard default (gallery) card: fixed art height, natural width; size
  tiers and hero keep their fixed footprints. Forwards the ratio event.
- Gallery grid becomes flow-packed wrapping rows: useGalleryVirtualItems
  greedily packs same-height / natural-width cards per row (ragged right),
  measuring ratios client-side (cached by rom id, debounced re-pack). Row
  height stays uniform so RVirtualScroller, AlphaStrip, scroll restoration
  and grid-nav are unchanged.
- useResponsiveColumns additionally exposes usableWidth for width packing.
- Unit tests for packFlowRows.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 17:33:28 -04:00
zurdi
58ecb681ec feat(v2): restore "remove from continue playing" action
The per-user "remove from Continue Playing" option existed in v1's
AdminMenu but was missing in v2. The API, store action
(removeFromContinuePlaying) and data shape were already shared; only
the v2 UI layer and action handler were absent.

- useGameActions: add removeFromContinuePlaying() (clears last_played,
  prunes the cached continue-playing list, snackbar feedback) plus a
  canRemoveFromContinuePlaying gate (only when the ROM has last_played).
- GameActionsList: surface the action as an RMenuItem in the more-menu.
- locales: add snackbar-removed-from-playing and
  snackbar-remove-from-playing-failed to all locales (translated for
  es/de/fr/it/pt/ru, English placeholder elsewhere).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 20:44:40 +00:00