Skip to content

feat: native comments, bug reports, dictionary, type safety#200

Merged
Hugo0 merged 2 commits into
mainfrom
feat/comments-reports-type-fixes
Apr 13, 2026
Merged

feat: native comments, bug reports, dictionary, type safety#200
Hugo0 merged 2 commits into
mainfrom
feat/comments-reports-type-fixes

Conversation

@Hugo0
Copy link
Copy Markdown
Owner

@Hugo0 Hugo0 commented Apr 13, 2026

Summary

  • Native comments replacing Giscus on word pages — flat list, auth to post, real-time game result badges (single SQL query, no stored metadata)
  • Bug report modal replacing GitHub Issues link — stores in DB, optional email via Resend
  • Word dictionary & translations — full Wiktionary entries, cross-language links with SSR-safe names
  • Hydration fix — WordTranslations was causing 80+ hydration mismatches (client-side langNames cache vs server codes), corrupting Vue's component tree and breaking teleports/sign-in modal
  • useHead dispose fix — merged 3 separate useHead/useSeoMeta calls into 1 to avoid @unhead/vue crash on unmount
  • Type safety — 45 → 0 TypeScript errors across 25+ files
  • Passkey auth — LoginModal rewrite with multi-step flow
  • Prisma schema — Comment model, TargetNeighbor model (was orphaned 4.4M-row table)

SEO audit

All word page SEO elements verified in SSR output:

  • <title>, description, OG tags, Twitter cards, canonical, robots — identical to before
  • JSON-LD enriched with pronunciation + etymology from kaikki data
  • Translations now SSR-rendered correctly (were hydration-mismatched before)
  • Comments server-rendered (Giscus was client-only iframe)

Security audit

  • SQL injection: all user inputs parameterized, appearances capped at 20
  • XSS: Vue {{ }} auto-escapes, no v-html
  • Rate limiting: 5 comments/10min per IP, 3 reports/hour per IP
  • Moderation: profanity regex, URL blocking, repeated char detection
  • Input validation: comment 500 chars, report 2000 chars, cursor date validated

Test plan

  • vue-tsc --noEmit — 0 errors
  • pnpm test — 590 passed (1 flaky DB timeout, pre-existing)
  • SSR output verified: title, meta, JSON-LD, canonical all present
  • Comments render with badges on /en/word/light (seeded test data)
  • Hydration mismatch eliminated (verified via Chrome console)
  • Sign-in modal works on word pages (was broken by hydration corruption)
  • Test comment posting when logged in
  • Test report modal from sidebar
  • Dark mode + RTL layout verification
  • Mobile responsive check

Summary by CodeRabbit

Release Notes

  • New Features

    • Added in-app report/feedback submission functionality
    • Added community discussion section on word pages
    • Added dictionary definitions with etymology and pronunciation
    • Added word translations across languages
    • Added word images with clickable expansion
    • Added word appearance history across game modes
    • Enhanced login flow with improved passkey support and platform preferences
  • Improvements

    • Added informational tooltips for semantic explorer features
    • Improved definition retrieval from database sources
  • Bug Fixes

    • Fixed word image API caching behavior

…overhaul

Comments system:
- Replace Giscus (GitHub Discussions) with native comment section on word pages
- Comments table in Postgres (shared with bug reports via type field)
- Real-time game result badges via single SQL query with correlated subquery
- Badge rules: N/maxGuesses for wordle modes, N guesses for semantic, skip speed
- Mode label only shown when user has 2+ badges for the same word
- Basic moderation: profanity filter, URL blocking, rate limiting, hidden flag
- Flat list, auth required to post, public to read

Bug reports & feedback:
- In-app report modal replacing GitHub Issues link in sidebar
- Reports stored in comments table (type='report'/'feedback')
- Auto-collects URL, browser, screen size, PWA status, language, mode
- Optional email notification via Resend (works without it)

Word page improvements:
- WordDictionary: full Wiktionary entries (senses, etymology, IPA, forms)
- WordTranslations: cross-language word links with SSR-safe language names
- Fix hydration mismatch: translations now use server-resolved names instead
  of client-side cache (useNuxtData), eliminating 80+ hydration warnings that
  were corrupting Vue's component tree and breaking teleports/event handlers
- Fix @unhead/vue dispose crash: merge 3 useHead/useSeoMeta calls into 1
- JSON-LD enriched with pronunciation and etymology from kaikki data
- Image handling moved from HEAD-probe to server-provided image_url

Type safety (45 errors → 0):
- Add type annotations to all implicit-any parameters across server utils
- Extend UiStrings and LanguageHelp types with missing optional properties
- Fix null vs undefined mismatches in Vue components
- Fix Nuxt route type recursion (ofetch import bypasses typed $fetch)
- Add trackCustomEvent to analytics composable
- Fix semantic.vue notification, game complete params, and compass types
- Add TargetNeighbor model to Prisma schema (was orphaned, 4.4M rows)

Passkey auth improvements (LoginModal rewrite):
- Multi-step flow: main → passkey choice → register/authenticate
- Platform-aware ordering (passkey first on iOS, Google first elsewhere)
- Email sign-in placeholder

Other:
- Dictionary import script (scripts/import_dictionary.py)
- Gitignore: design/, kaikki_processed/, public exploration files
- TODO: leaderboard comments, comment admin tooling, Resend setup
@Hugo0
Copy link
Copy Markdown
Owner Author

Hugo0 commented Apr 13, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 13, 2026

✅ 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.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 13, 2026

Warning

Rate limit exceeded

@Hugo0 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 49 minutes and 9 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 49 minutes and 9 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d4e5bd2e-0aab-4927-8fd1-50fb19f76aae

📥 Commits

Reviewing files that changed from the base of the PR and between c324f82 and 636a073.

📒 Files selected for processing (20)
  • components/account/LoginModal.vue
  • components/app/AppSidebar.vue
  • components/shared/BaseTooltip.vue
  • components/shared/ReportModal.vue
  • components/word/NearbyInMeaning.vue
  • components/word/WordComments.vue
  • components/word/WordDictionary.vue
  • components/word/WordTranslations.vue
  • pages/[lang]/word/[slug].vue
  • scripts/import_dictionary.py
  • server/api/[lang]/semantic/guess.post.ts
  • server/api/comments.get.ts
  • server/api/report.post.ts
  • server/api/user/game-result.post.ts
  • server/api/user/profile.get.ts
  • server/api/webauthn/authenticate.post.ts
  • server/routes/auth/google.get.ts
  • server/utils/_semantic-db.ts
  • server/utils/moderation.ts
  • server/utils/word-selection.ts
📝 Walkthrough

Walkthrough

This pull request introduces a comprehensive system for user feedback and word discovery, adding in-app bug report/feedback submission, native word discussion comments, structured dictionary content, word images, and redesigned passkey authentication. It includes new database models, a Python dictionary ingestion pipeline, and multiple frontend components with supporting server APIs.

Changes

Cohort / File(s) Summary
Report & Feedback System
.env.example, app.vue, components/shared/ReportModal.vue, composables/useReportModal.ts, server/api/report.post.ts, components/app/AppSidebar.vue
Added REPORT_EMAIL config, ReportModal component with form/validation, useReportModal composable for global state, and /api/report endpoint with rate-limiting, moderation checks, and optional Resend email notifications. Replaced external GitHub issue links with in-app modal invocation.
Word Comments System
components/word/WordComments.vue, server/api/comments.get.ts, server/api/comments.post.ts, server/utils/comments.ts, server/utils/moderation.ts
Implemented native comment section component with post/fetch APIs, cursor-based pagination, optional badge aggregation by mode/appearance, comment moderation with regex blocklists and spam detection. Includes per-IP rate-limiting and signed-in user requirement for posting.
Dictionary & Translations
components/word/WordDictionary.vue, components/word/WordTranslations.vue, components/word/NearbyInMeaning.vue, composables/useWordData.ts, server/utils/definitions.ts
Added collapsible dictionary component with structured senses/etymology/pronunciation/forms, translations grid, and tooltip-annotated section headers. Extended definitions.ts to derive structured content from Kaikki DB and fallback to LLM when structured data unavailable. Updated WordBasic type to include dictionary/images/appearances.
Word Page Redesign
pages/[lang]/word/[slug].vue, server/api/[lang]/word/[slug].get.ts, server/api/[lang]/word-image/[word].get.ts
Redesigned word page to display expandable image strip, multi-appearance tracking across modes, consolidated SEO meta/JSON-LD via single useHead call, and rendered dictionary/translations/comments in sequence. Image endpoint now serves cached WebP without requiring API key if available. Added findWordAppearances API support.
Passkey Authentication Redesign
components/account/LoginModal.vue, server/api/webauthn/register.post.ts, server/api/webauthn/authenticate.post.ts
Converted single-step login to 3-step flow (main → passkey → success) with separate register/authenticate handlers, platform-aware auth method ordering (passkey first on iOS), success animation, and distinct error handling per method. Register endpoint now enforces platform authenticator preferences.
Database Schema & Ingestion
prisma/schema.prisma, scripts/import_dictionary.py, pyproject.toml, server/utils/word-selection.ts
Added Comment, TargetNeighbor, and Definition-enriched models. Implemented 600-line Python pipeline (download/process/upload commands) to ingest Kaikki JSONL dumps, merge native+English editions, extract etymology/forms/senses/examples/translations, and upsert structured definitions. Added word-appearance reverse indexing and mode day-index mapping.
UI Components & Navigation
components/shared/BaseTooltip.vue, components/app/AppShell.vue, components/app/SidebarItem.vue, components/game/PageShell.vue, components/game/MultiBoardLayout.vue
Added reusable BaseTooltip with auto-positioning and mobile handling. Updated AppShell to pass undefined instead of null for currentMode. Enhanced SidebarItem with smart hash-based scrolling and route awareness. Wired sidebar items to close on navigation. Fixed container-width measurement to use numeric sentinel (0) instead of null.
Analytics & Type Definitions
composables/useAnalytics.ts, pages/[lang]/semantic.vue, utils/types.ts, pages/[lang]/speed.vue
Added trackCustomEvent() method to useAnalytics API. Exposed new semantic UI text keys (subtitle, rank, compass, map, oracle) and sidebar labels. Updated semantic page to use new tracking method and include streak_after in game-over payload. Added fallbacks for speed/semantic countdown and title when config values are missing.
Type Annotations & Infrastructure
server/utils/prisma.ts, server/utils/rate-limit.ts, server/utils/badge-evaluator.ts, server/utils/_semantic-db.ts, server/utils/name-generator.ts, server/api/user/sync.post.ts, server/api/user/game-result.post.ts, server/api/user/profile.get.ts, nuxt.config.ts, prisma/prisma.config.ts, plugins/sync.client.ts, composables/useDefinitions.ts
Tightened TypeScript types throughout: Prisma singleton from PrismaClient to InstanceType; explicit row-shape annotations in $queryRaw mappings; badge type annotations; Retry-After header as numeric; replaced Nuxt $fetch with ofetch alias; added Prisma earlyAccess config with type suppression.
Configuration & Documentation
.env.example, .gitignore, TODO.md, server/api/auth/forgot-password.post.ts, server/api/auth/register.post.ts, server/api/[lang]/words.get.ts
Added REPORT_EMAIL config, design/ and scripts/.kaikki_processed/ ignore patterns. Restructured TODO as Phase-based checklist marking completed items. Updated auth endpoints to assert non-null emails. Migrated word definitions from filesystem cache to async fetchDefinition call.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant ReportModal as ReportModal Component
    participant API as POST /api/report
    participant Toast as Toast Notification
    
    User->>ReportModal: Click "Report a Bug/Feedback"
    ReportModal->>ReportModal: Show modal form
    User->>ReportModal: Enter message, select type
    User->>ReportModal: Submit (Ctrl+Enter or button)
    ReportModal->>ReportModal: Validate length (≥5 chars)
    ReportModal->>ReportModal: Collect context (URL, lang, UA, PWA flag)
    ReportModal->>API: POST with type/message/metadata
    API->>API: Rate-limit check (3/hour per IP)
    API->>API: Validate & trim message
    API->>API: Optional: Send email via Resend
    API-->>ReportModal: 200 { ok: true }
    ReportModal->>ReportModal: Show "Report received" state
    User->>ReportModal: Close modal
    ReportModal->>Toast: Trigger success toast
    Toast-->>User: Display toast notification
Loading
sequenceDiagram
    participant User
    participant WordComments as WordComments Component
    participant API as POST /api/comments
    participant DB as Database
    
    User->>WordComments: Scroll to comments section
    WordComments->>WordComments: Fetch existing comments (GET)
    WordComments-->>User: Render comment list
    User->>WordComments: Type comment message
    User->>WordComments: Press Ctrl+Enter to submit
    WordComments->>WordComments: Validate length (5-500 chars)
    WordComments->>API: POST with targetType/targetKey/body
    API->>API: Rate-limit check (5/10min per user)
    API->>API: Moderation scan (blocklist/spam)
    API->>DB: Create comment row + fetch badges
    API->>API: Query user badges for appearances
    API-->>WordComments: Return new comment + badges
    WordComments->>WordComments: Prepend to list, increment count
    User-->>User: See comment posted
Loading
sequenceDiagram
    participant Browser
    participant WordPage as Word Page Component
    participant WordAPI as GET /api/[lang]/word/[slug]
    participant DB as Database
    participant Prisma as Prisma
    
    Browser->>WordPage: Navigate to word page
    WordPage->>WordAPI: Fetch word data
    WordAPI->>Prisma: Query definition, appearances, image
    Prisma->>DB: Select from Definition, Result models
    DB-->>Prisma: Return structured dictionary + appearances
    WordAPI->>WordAPI: Enrich translations with language metadata
    WordAPI->>WordAPI: Format appearances by mode/date
    WordAPI-->>WordPage: Return basicData with dictionary/images/appearances
    WordPage->>WordPage: Render title, image strip, dictionary
    WordPage->>WordPage: Render appearances list (multi-mode)
    WordPage->>WordPage: Render comments section
    Browser-->>Browser: Display complete word page
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 A rabbit hops through feedback gleaned,
Comments now nested, cleanly preened!
Words dressed in dictionaries fine,
With images bright and translations align—
Our burrow grows richer, line by line! 🥕✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 41.03% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat: native comments, bug reports, dictionary, type safety' directly and clearly summarizes the four main feature areas of this changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/comments-reports-type-fixes

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 merged commit 6ca8c48 into main Apr 13, 2026
2 of 5 checks passed
@Hugo0 Hugo0 deleted the feat/comments-reports-type-fixes branch April 15, 2026 17:55
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