Skip to content

v4: Daily/Unlimited system, Accounts, Semantic Explorer, Profile & Badges#190

Merged
Hugo0 merged 24 commits into
mainfrom
v4
Apr 11, 2026
Merged

v4: Daily/Unlimited system, Accounts, Semantic Explorer, Profile & Badges#190
Hugo0 merged 24 commits into
mainfrom
v4

Conversation

@Hugo0

@Hugo0 Hugo0 commented Apr 10, 2026

Copy link
Copy Markdown
Owner

Summary

Major release bringing four interconnected feature systems to Wordle Global.

1. Daily/Unlimited Play Type System

Every game mode now supports both daily (deterministic, same words for everyone) and unlimited (random) play types.

  • supportedPlayTypes on all game modes — classic, speed, dordle through duotrigordle, semantic
  • ?play=unlimited query param on any mode route for unlimited play
  • usePlayType composable — reads URL, validates against mode config, persists choice to localStorage
  • Product-wide streaks — any daily mode in any language continues your streak (isDailyStatsKey)
  • Play-type-aware persistencebuildSaveKey/buildStatsKey produce backward-compatible keys
  • Server API?play=daily|unlimited param, speed_daily_words for daily speed (50 deterministic words)
  • Post-game cross-pollination — StatsModal shows "Unlimited →" after daily, "Play today's daily →" after unlimited
  • Archive redirect/en/words → 301 → /en/archive with mode filter tabs

2. Sidebar Restructure

Reduced from 10 flat items to 5 organized entries with sub-panel navigation.

  • Daily Puzzle + Unlimited as top-level items
  • Speed Streak, Multi-Board, Semantic Explorer with icon-only sub-panel (daily/unlimited picker slides from sidebar edge)
  • Multi-Board bundles Dordle through Duotrigordle in one expandable section
  • Sub-panel auto-opens when tapping header subtitle (hidden interaction for power users)
  • useAutoHeight for smooth expand/collapse animation
  • RTL support on sub-panel

3. User Accounts & Profiles

Full authentication system with Google OAuth, email/password, and server-side persistence.

  • Google OAuth + email/password authentication via nuxt-auth-utils
  • Profile page (/profile) with avatar, stats, editorial-style badge grid
  • 16 badges — first-blood, perfect-game, polyglot series, streak milestones, mode-master, daily-completionist
  • Badge evaluation on every game result with toast notifications
  • Game result sync to server (per-game + bulk upload on login)
  • Settings sync — darkMode, hardMode, highContrast, preferredLanguage synced to server
  • Login CTAs in StatsModal and sidebar

4. Homepage Redesign

Three-tier personalization based on user state.

  • Cold (never visited): Masthead + mode cards + language grid
  • Warm (has localStorage): Streak badge + "Continue Playing" cards (most-played modes/languages first) + sign-in CTA + PWA install prompt
  • Signed in: Avatar + name + streak + continue cards
  • NYT-style Daily Puzzle hero card with W-O-R-D-S colored tiles
  • Semantic Explorer always visible with English flag badge for non-English users
  • Language preference persisted to localStorage and synced to server

5. Semantic Explorer Improvements

  • Game state persistence to localStorage (survives page reload)
  • Mobile layout overhaul — fixed input bar, map height constraints, responsive compass/leaderboard
  • Hardcoded fonts → CSS variables (65 instances)
  • buildSemanticGradientFromCSS() reads design tokens at runtime

6. Animation & Modal Polish

  • Backdrop blur separated from dialog transition (prevents frame drops)
  • FLIP expand composable rewritten with View Transitions API
  • All modals: ESC close, click-outside close, consistent backdrop
  • StatsModal and GameModePicker consolidated to use BaseModal
  • Page transitions and dark mode color transitions

Breaking Changes

  • Multi-board modes now defaultPlayType: 'daily' (was unlimited). Existing unlimited saves preserved via backward-compatible key format.
  • /en/words 301-redirects to /en/archive
  • unlimited removed from GAME_MODE_ORDER (sidebar no longer shows it as standalone mode)

New Files

  • composables/usePlayType.ts — play type from URL + localStorage
  • composables/useProductStreak.ts — product-wide streak composable
  • composables/useToast.ts — toast notification system
  • components/account/BadgeGrid.vue — editorial-style badge display
  • components/account/BadgeEarned.vue — badge earned toast
  • components/account/LoginModal.vue — auth modal (Google + email)
  • components/shared/AppToast.vue — toast component
  • components/app/BoardPickerModal.vue — multi-board mode picker
  • components/app/LanguagePickerModal.vue — in-game language switcher
  • server/middleware/archive-redirect.ts — 301 redirect
  • server/middleware/stats-redirect.ts — legacy stats redirect
  • server/utils/name-generator.ts — random display name generation
  • server/utils/rate-limit.ts — API rate limiting
  • server/utils/session.ts — session utilities
  • server/api/auth/* — full auth endpoint suite
  • server/api/user/* — profile, settings, game-result, sync endpoints
  • server/api/avatar/* — avatar proxy
  • pages/[lang]/archive.vue — renamed from words.vue
  • pages/profile.vue — user profile + badges + stats

Test plan

  • Play daily classic → verify streak increments → play unlimited → verify no streak change
  • Navigate to /en/dordle?play=daily → verify deterministic daily words
  • Open sidebar → verify 5 items, multi-board expandable, sub-panel works
  • Tap header subtitle → verify sidebar opens with sub-panel for current mode
  • Complete a daily game → verify "Unlimited →" button in stats modal
  • Visit /en/words → verify 301 redirect to /en/archive
  • Sign in with Google → verify settings sync, badge evaluation
  • Homepage: cold visitor vs returning user vs signed-in user
  • Mobile: semantic game, sidebar sub-panel, homepage cards
  • Dark mode: all new components render correctly
  • RTL: sidebar sub-panel, header subtitle

Hugo0 added 13 commits April 10, 2026 12:50
Includes: unified MeaningMap (SVG), MapFrame (zoom/pan/expand),
compass needle, label overlap avoidance, staggered neighbour reveal,
celebration pulse, dark mode fix, bounce-in animation, word page,
game page, server endpoints, PCA/UMAP projections, axis system.

Temporary checkpoint commit — not ready for PR.
Includes all work from multiple agents:
- Semantic Explorer mode (embeddings, compass, word map, hints)
- Word detail pages + meaning map
- Daily/Unlimited for all game modes
- Octordle, Sedecordle, Duotrigordle modes
- Accounts system: Prisma 7, Google OAuth, email/password auth
- Sync composable (localStorage ↔ server)
- Badge system (16 badges, evaluator, toast notification)
- Profile page, login CTAs, header profile icon
- SEO hreflang overhaul, sitemap improvements
- Design system refinements
… hero

Homepage rewrite:
- 3-tier personalization: cold visitors see mode cards, returning users
  see Continue Playing cards with stacked mode+flag icons, streak badge,
  sign-in CTA, and featured Semantic (English-only, auto-detected)
- 4 mode cards: Classic, Semantic (featured/NEW), Speed, Multi-Board
  (opens BoardPickerModal). Semantic hidden for non-English languages.
- Product-wide streak calculation from buildDailyResultMap
- Auth integration (useAuth, useLoginModal)
- PWA install CTA for eligible returning users
- BoardPickerModal integration for multi-board picker

Game board layout fix:
- SeoNoscript moved to named #seo slot in PageShell, rendered outside
  the h-[100dvh] game viewport div. Fixes game board squished to ~5px
  when SeoNoscript's min-h-[100dvh] competed for flex space.
- overflow-hidden safety net on PageShell wrapper
- All 5 game pages updated to use <template #seo>

Profile page:
- Product-wide streak hero (any daily mode, any language) with animated
  counter, best streak, and "play today's daily" prompt when streak is 0
- BadgeGrid editorial component (4-col grid with 1px rules)

Archive page:
- Mode filter tabs: Classic (active), Speed/Multi-Board/Semantic (Soon)

Word page:
- "Appeared In" section replacing placeholder daily stats

Other:
- Added mode_*_label keys to UiConfig type
- Fixed BoardPickerModal @click.native -> @click (Vue 3)
- Updated stats tests for unified unlimited/classic key bucket
- Excluded .claude/worktrees/ from vitest to prevent duplicate test runs
- Wrap PageShell template in single root div to prevent Vue fragment
  warnings ("does not have a single root node") that break Nuxt page
  transitions. The seo slot + game viewport div were two root nodes.
- Fix "1 plays" → "1 play" singular grammar in archive word cards.
StatsModal:
- "Unlimited" button → "Keep Playing" (clearer value proposition)
- Sign-in CTA redesigned: subtle text button "Sign in to protect your
  streak →" replacing the full-width banner bar
- Ad slot location identified (comment placeholder between distribution
  and action buttons)

PostGamePanel (keyboard flip):
- "Play unlimited →" → "Keep playing →" (consistent copy)
- Removed 'unlimited' from mode discovery grid (it's now a play type,
  not a separate mode)
- Added sign-in nudge for logged-out users
- Added commented ad slot placeholder for future Ezoic/Mediavine banner
Daily Puzzle card gets the NYT Wordle-style treatment: W-O-R-D-S mini
tiles with correct/semicorrect/incorrect colors, large serif heading,
longer description, CLASSIC tag. Other modes (Semantic Explorer, Speed
Streak, Multi-Board) use the compact list layout with Lucide icons.
- Selecting a language in the grid now updates the homepage language
  (detectedLanguageCode + hpLangOverride), so mode cards and UI strings
  reflect the chosen language immediately.
- Semantic Explorer always shows in mode cards regardless of language.
  When user's language isn't English, an English flag badge appears on
  the icon to indicate language restriction. Route always goes to
  /en/semantic.
- Moved badges section below Languages (was between profile info and
  streak hero, displacing more important content)
- Badges sorted: earned first, locked after
- Collapsed to 8 badges by default with "Show all 16 badges" expand
- Added language flag icons to Your Languages section
- Imported useFlag composable for flag resolution
- Homepage reads `preferred_language` from localStorage as top priority
  for language detection, before game results and browser detection.
- Selecting a language in the homepage grid persists it to localStorage.
- Game pages update `preferred_language` on mount, so navigating to
  /fi sets Finnish as preferred for the next homepage visit.
- Remove featured Semantic CTA from personalized hub — now redundant
  since Semantic is always visible in mode cards list.
- Remove unused showSemanticFeature computed.
- Sync preferredLanguage to server via PUT /api/user/settings.
- Server settings endpoint now accepts string keys (preferredLanguage)
  in addition to boolean keys.
- Pull preferredLanguage from server on login and write to localStorage.
When removing a word from the auto-selected context (no ?context= in
URL), the remove handler now promotes the full auto-context to explicit
URL state first (minus the removed word), so the removal persists.
Previously, removing auto-selected words did nothing because they
weren't in the URL query.
- Word page: onRemoveContextWord promotes auto-context to URL before
  removing, so removing auto-selected words actually persists
- NearbyInMeaning: availableAxes includes active slice pair even when
  partner is outside top 6, so MeaningMap legend can render axis labels
- Profile: fixed orphaned </div> causing template compile error
Merge of all v4 work: daily/unlimited play types across all game modes,
user accounts with Google OAuth, profile page with badges, semantic
explorer improvements, and homepage redesign.
@coderabbitai

coderabbitai Bot commented Apr 10, 2026

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 196 files, which is 46 over the limit of 150.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6a5576d3-32fd-42fa-8817-de2a8b80dffb

📥 Commits

Reviewing files that changed from the base of the PR and between 1472ad3 and c8dd2a7.

⛔ Files ignored due to path filters (92)
  • .claude/scheduled_tasks.lock is excluded by !**/*.lock
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • public/images/modes/classic/ar.png is excluded by !**/*.png
  • public/images/modes/classic/az.png is excluded by !**/*.png
  • public/images/modes/classic/bg.png is excluded by !**/*.png
  • public/images/modes/classic/bn.png is excluded by !**/*.png
  • public/images/modes/classic/br.png is excluded by !**/*.png
  • public/images/modes/classic/ca.png is excluded by !**/*.png
  • public/images/modes/classic/ckb.png is excluded by !**/*.png
  • public/images/modes/classic/cs.png is excluded by !**/*.png
  • public/images/modes/classic/da.png is excluded by !**/*.png
  • public/images/modes/classic/de.png is excluded by !**/*.png
  • public/images/modes/classic/el.png is excluded by !**/*.png
  • public/images/modes/classic/en.png is excluded by !**/*.png
  • public/images/modes/classic/eo.png is excluded by !**/*.png
  • public/images/modes/classic/es.png is excluded by !**/*.png
  • public/images/modes/classic/et.png is excluded by !**/*.png
  • public/images/modes/classic/eu.png is excluded by !**/*.png
  • public/images/modes/classic/fa.png is excluded by !**/*.png
  • public/images/modes/classic/fi.png is excluded by !**/*.png
  • public/images/modes/classic/fo.png is excluded by !**/*.png
  • public/images/modes/classic/fr.png is excluded by !**/*.png
  • public/images/modes/classic/fur.png is excluded by !**/*.png
  • public/images/modes/classic/fy.png is excluded by !**/*.png
  • public/images/modes/classic/ga.png is excluded by !**/*.png
  • public/images/modes/classic/gd.png is excluded by !**/*.png
  • public/images/modes/classic/gl.png is excluded by !**/*.png
  • public/images/modes/classic/ha.png is excluded by !**/*.png
  • public/images/modes/classic/he.png is excluded by !**/*.png
  • public/images/modes/classic/hi.png is excluded by !**/*.png
  • public/images/modes/classic/hr.png is excluded by !**/*.png
  • public/images/modes/classic/hu.png is excluded by !**/*.png
  • public/images/modes/classic/hy.png is excluded by !**/*.png
  • public/images/modes/classic/hyw.png is excluded by !**/*.png
  • public/images/modes/classic/ia.png is excluded by !**/*.png
  • public/images/modes/classic/id.png is excluded by !**/*.png
  • public/images/modes/classic/ie.png is excluded by !**/*.png
  • public/images/modes/classic/is.png is excluded by !**/*.png
  • public/images/modes/classic/it.png is excluded by !**/*.png
  • public/images/modes/classic/ja.png is excluded by !**/*.png
  • public/images/modes/classic/ka.png is excluded by !**/*.png
  • public/images/modes/classic/ko.png is excluded by !**/*.png
  • public/images/modes/classic/la.png is excluded by !**/*.png
  • public/images/modes/classic/lb.png is excluded by !**/*.png
  • public/images/modes/classic/lt.png is excluded by !**/*.png
  • public/images/modes/classic/ltg.png is excluded by !**/*.png
  • public/images/modes/classic/lv.png is excluded by !**/*.png
  • public/images/modes/classic/mi.png is excluded by !**/*.png
  • public/images/modes/classic/mk.png is excluded by !**/*.png
  • public/images/modes/classic/mn.png is excluded by !**/*.png
  • public/images/modes/classic/mr.png is excluded by !**/*.png
  • public/images/modes/classic/ms.png is excluded by !**/*.png
  • public/images/modes/classic/nb.png is excluded by !**/*.png
  • public/images/modes/classic/nds.png is excluded by !**/*.png
  • public/images/modes/classic/ne.png is excluded by !**/*.png
  • public/images/modes/classic/nl.png is excluded by !**/*.png
  • public/images/modes/classic/nn.png is excluded by !**/*.png
  • public/images/modes/classic/oc.png is excluded by !**/*.png
  • public/images/modes/classic/pa.png is excluded by !**/*.png
  • public/images/modes/classic/pau.png is excluded by !**/*.png
  • public/images/modes/classic/pl.png is excluded by !**/*.png
  • public/images/modes/classic/pt.png is excluded by !**/*.png
  • public/images/modes/classic/qya.png is excluded by !**/*.png
  • public/images/modes/classic/ro.png is excluded by !**/*.png
  • public/images/modes/classic/ru.png is excluded by !**/*.png
  • public/images/modes/classic/rw.png is excluded by !**/*.png
  • public/images/modes/classic/sk.png is excluded by !**/*.png
  • public/images/modes/classic/sl.png is excluded by !**/*.png
  • public/images/modes/classic/sq.png is excluded by !**/*.png
  • public/images/modes/classic/sr.png is excluded by !**/*.png
  • public/images/modes/classic/sv.png is excluded by !**/*.png
  • public/images/modes/classic/sw.png is excluded by !**/*.png
  • public/images/modes/classic/tk.png is excluded by !**/*.png
  • public/images/modes/classic/tl.png is excluded by !**/*.png
  • public/images/modes/classic/tlh.png is excluded by !**/*.png
  • public/images/modes/classic/tr.png is excluded by !**/*.png
  • public/images/modes/classic/uk.png is excluded by !**/*.png
  • public/images/modes/classic/ur.png is excluded by !**/*.png
  • public/images/modes/classic/uz.png is excluded by !**/*.png
  • public/images/modes/classic/vi.png is excluded by !**/*.png
  • public/images/modes/classic/yo.png is excluded by !**/*.png
  • public/images/modes/dordle/he.png is excluded by !**/*.png
  • public/images/modes/duotrigordle/fa.png is excluded by !**/*.png
  • public/images/modes/duotrigordle/he.png is excluded by !**/*.png
  • public/images/modes/octordle/fa.png is excluded by !**/*.png
  • public/images/modes/octordle/he.png is excluded by !**/*.png
  • public/images/modes/quordle/he.png is excluded by !**/*.png
  • public/images/modes/sedecordle/fa.png is excluded by !**/*.png
  • public/images/modes/sedecordle/he.png is excluded by !**/*.png
  • public/images/modes/speed/he.png is excluded by !**/*.png
  • public/images/modes/tridle/he.png is excluded by !**/*.png
  • public/images/og-classic.png is excluded by !**/*.png
📒 Files selected for processing (196)
  • .claude/worktrees/semantic-poc
  • .env.example
  • CONTRIBUTING.md
  • TODO.md
  • app.vue
  • app/router.options.ts
  • assets/css/design-system.css
  • assets/css/main.css
  • components/account/BadgeEarned.vue
  • components/account/BadgeGrid.vue
  • components/account/LoginModal.vue
  • components/app/AppHeader.vue
  • components/app/AppShell.vue
  • components/app/AppSidebar.vue
  • components/app/BoardPickerModal.vue
  • components/app/GameModePicker.vue
  • components/app/LanguagePickerModal.vue
  • components/app/SidebarItem.vue
  • components/game/GameHeader.vue
  • components/game/HelpModal.vue
  • components/game/NotificationToast.vue
  • components/game/PageShell.vue
  • components/game/PostGamePanel.vue
  • components/game/SeoNoscript.vue
  • components/game/SettingsModal.vue
  • components/game/StatsModal.vue
  • components/semantic/SemanticCompassHints.vue
  • components/semantic/SemanticInput.vue
  • components/semantic/SemanticLeaderboard.vue
  • components/semantic/SemanticStatsModal.vue
  • components/shared/AppToast.vue
  • components/shared/BaseModal.vue
  • components/shared/MapFrame.vue
  • components/shared/MeaningMap.vue
  • components/word/AddCompareWord.vue
  • components/word/NearbyInMeaning.vue
  • components/word/WordDefinition.vue
  • components/word/WordHeadline.vue
  • components/word/WordImage.vue
  • composables/useAnalytics.ts
  • composables/useAnimatedNumber.ts
  • composables/useAuth.ts
  • composables/useAutoHeight.ts
  • composables/useBadgeNotification.ts
  • composables/useContextWords.ts
  • composables/useEmbed.ts
  • composables/useFlipExpand.ts
  • composables/useGameModes.ts
  • composables/useGamePage.ts
  • composables/useGameSeo.ts
  • composables/useGameShare.ts
  • composables/useHreflang.ts
  • composables/useLoginModal.ts
  • composables/useMultiBoardPage.ts
  • composables/usePageDirection.ts
  • composables/usePlayType.ts
  • composables/useProductStreak.ts
  • composables/useSemanticGame.ts
  • composables/useSync.ts
  • composables/useToast.ts
  • composables/useWordData.ts
  • data/default_language_config.json
  • data/semantic/.gitignore
  • data/semantic/README.md
  • data/semantic/axes.json
  • data/semantic/pca2d.json
  • data/semantic/targets.json
  • data/semantic/umap.json
  • data/semantic/valid_words.json
  • data/semantic/vocabulary.json
  • e2e/gameplay.spec.ts
  • e2e/helpers.ts
  • e2e/integration.spec.ts
  • e2e/smoke.spec.ts
  • e2e/sync.spec.ts
  • layouts/game.vue
  • middleware/page-direction.global.ts
  • nuxt.config.ts
  • package.json
  • pages/[lang]/[mode].vue
  • pages/[lang]/archive.vue
  • pages/[lang]/best-starting-words.vue
  • pages/[lang]/index.vue
  • pages/[lang]/semantic.vue
  • pages/[lang]/speed.vue
  • pages/[lang]/unlimited.vue
  • pages/[lang]/word/[id].vue
  • pages/[lang]/word/[slug].vue
  • pages/[lang]/words.vue
  • pages/accessibility.vue
  • pages/index.vue
  • pages/profile.vue
  • pages/stats.vue
  • playwright.config.ts
  • plugins/sync.client.ts
  • plugins/view-transition-direction.client.ts
  • prisma/migrations/0001_init/migration.sql
  • prisma/prisma.config.ts
  • prisma/schema.prisma
  • prisma/seed.ts
  • public/design-explorations/daily-unlimited-system.html
  • public/design-explorations/direction-a-editorial.html
  • public/design-explorations/homepage-redesign.html
  • public/design-explorations/stats-redesign.html
  • public/llms.txt
  • public/robots.txt
  • scripts/generate_mode_og_images.py
  • scripts/semantic/.gitignore
  • scripts/semantic/__init__.py
  • scripts/semantic/find_axes.py
  • scripts/semantic/gameplay_pairs.json
  • scripts/semantic/generate.py
  • scripts/semantic/generate_pca2d.py
  • scripts/semantic/promote_axes.py
  • scripts/semantic/test_hints.py
  • server/api/[lang]/data.get.ts
  • server/api/[lang]/semantic/guess.post.ts
  • server/api/[lang]/semantic/hint.post.ts
  • server/api/[lang]/semantic/reveal.post.ts
  • server/api/[lang]/semantic/start.post.ts
  • server/api/[lang]/semantic/vocab.get.ts
  • server/api/[lang]/starting-words.get.ts
  • server/api/[lang]/word-explore/[slug].get.ts
  • server/api/[lang]/word/[id].get.ts
  • server/api/[lang]/word/[slug].get.ts
  • server/api/[lang]/words.get.ts
  • server/api/auth/dev-login.get.ts
  • server/api/auth/forgot-password.post.ts
  • server/api/auth/generate-name.get.ts
  • server/api/auth/login.post.ts
  • server/api/auth/logout.post.ts
  • server/api/auth/register.post.ts
  • server/api/auth/reset-password.post.ts
  • server/api/auth/verify-email.get.ts
  • server/api/avatar/[id].get.ts
  • server/api/badges.get.ts
  • server/api/user/badge-progress.get.ts
  • server/api/user/game-result.post.ts
  • server/api/user/profile.get.ts
  • server/api/user/profile.put.ts
  • server/api/user/settings.get.ts
  • server/api/user/settings.put.ts
  • server/api/user/speed-result.post.ts
  • server/api/user/stats.get.ts
  • server/api/user/sync.post.ts
  • server/api/webauthn/authenticate.post.ts
  • server/api/webauthn/register.post.ts
  • server/lib/day-index.ts
  • server/middleware/archive-redirect.ts
  • server/middleware/stats-redirect.ts
  • server/plugins/prisma.ts
  • server/plugins/semantic-warmup.ts
  • server/routes/auth/google.get.ts
  • server/routes/sitemap-main.xml.ts
  • server/routes/sitemap-words-[lang].xml.ts
  • server/types/auth.d.ts
  • server/utils/badge-evaluator.ts
  • server/utils/data-loader.ts
  • server/utils/day-index.ts
  • server/utils/definitions.ts
  • server/utils/email.ts
  • server/utils/hreflang.ts
  • server/utils/name-generator.ts
  • server/utils/prisma.ts
  • server/utils/rate-limit.ts
  • server/utils/semantic.ts
  • server/utils/semanticGenerate.ts
  • server/utils/session.ts
  • server/utils/stats-key-parser.ts
  • server/utils/wiktionary.ts
  • server/utils/word-selection.ts
  • stores/game.ts
  • stores/stats.ts
  • tests/day-index.test.ts
  • tests/design-system.test.ts
  • tests/game-modes.test.ts
  • tests/hreflang.test.ts
  • tests/setup-server.ts
  • tests/sitemap-hreflang.test.ts
  • tests/ssr.test.ts
  • tests/stats-key-parser.test.ts
  • tests/stores/game.test.ts
  • tests/stores/stats.test.ts
  • tests/sync.test.ts
  • tests/test_language_config.py
  • tests/vitest-unit.config.ts
  • utils/easing.ts
  • utils/game-modes.ts
  • utils/locale.ts
  • utils/mapProjection.ts
  • utils/semanticColor.ts
  • utils/storage.ts
  • utils/streak-dates.ts
  • utils/types.ts
  • utils/wordUrls.ts
  • vitest.config.ts

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch v4

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Hugo0

Hugo0 commented Apr 10, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Apr 10, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Hugo0 added 11 commits April 11, 2026 01:00
- Storage keys namespaced per user (game_results:{userId}) with
  migration from v1 unscoped to v2 scoped layout
- Sync plugin: Pattern B (server=truth, localStorage=cache),
  visibilitychange delta pull, anonymous result claiming on login
- Profile page: tab-based stats (overview/distribution/languages/speed),
  mode icons from shared config, cleaner auth flow
- Storage key constants (STORAGE_KEYS) used consistently across codebase
- Logout reloads stores from anonymous keys
- Sidebar defaults to empty mode/playType when not on a game page
Without this, switching between daily and unlimited on the same
mode (e.g. /en/dordle → /en/dordle?play=unlimited) reuses the
existing component instance, which doesn't re-initialize the game.
Storage:
- User-scoped localStorage keys (game_results:{userId}) for safe multi-user
- One-time v1→v2 migration, STORAGE_KEYS constants, visibilitychange pull
- Pending sync queue: expire after 24h, don't retry 4xx errors

Bug fixes:
- Speed mode & unlimited: remove minimal=1 so word_list loads for validation
- "Keep Playing" button: include ?play in page key for component remount
- Mode card tiles: use --color-muted (not nonexistent --color-incorrect)
- profile.vue: remove duplicate sortedModes declaration (SSR crash)
- LanguageText type: match actual config keys (underscore, not kebab)
- Add @vue/reactivity as explicit dependency

CI fixes:
- Vitest server startup timeout: 60s → 120s for cold CI builds
- Python config tests: check for unknown keys (typos), not missing keys
  (runtime merges with defaults, so missing keys aren't bugs)
- Ruff: fix unused vars, zip strict, f-string, ternary in semantic scripts
- Format all Python and TypeScript
…l heading

- Merge earnedAt from profile into badge definitions for date display
- Pass progress data to BadgeGrid for progress bar rendering
- Editorial heading: "Achievement Badges" with "N of M earned" subtitle
- Remove old show-more button (BadgeGrid handles tier collapsing)
Promise.all with badge-progress meant if auth failed, the entire
badge section disappeared. Now badges+profile load first, progress
is fetched separately.
The `Map` import from lucide-vue-next overwrote the built-in Map,
causing `new Map()` in the badge earnedAt merge to throw
"Map is not a constructor". Aliased to MapIcon.
…profile

Lucide's Map component shadowed the global Map constructor in both
BadgeGrid.vue and profile.vue. Aliased to MapIcon in BadgeGrid.
Removed the entire BADGE_ICONS map from profile.vue — it was dead
code since BadgeGrid handles its own icon resolution.
- Removed grid background color so empty trailing cells don't show
  as colored rectangles. Each card has its own border instead.
- Default to showing 8 badges (2 rows) with "Show all N badges"
  expand button.
Nitro JIT compilation takes 50s+ in CI, leaving <10s for data loading.
Bumped both Playwright webServer and vitest setup-server timeouts.
@Hugo0
Hugo0 merged commit bd6e853 into main Apr 11, 2026
3 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant