Commit Graph
100 Commits
Author SHA1 Message Date
emilandClaude Opus 4.7 e6dec1eb95 fix(deploy): apply drizzle migrations to prod on container startup
The deploy pipeline was building the app image with only `dist/` inside
and rsync'ing only `dist package*.json docker-compose.yml Dockerfile`
to the server. The drizzle migrations folder never reached prod, so
new schema changes (Wave 1 added `tier`, `boosty_verified_at`, and 5
tables) silently went missing. App requests then failed with
"column does not exist" errors at runtime.

Wave-1 CI step `npm run db:migrate` ran against the in-job postgres
service, not against prod — so it never helped.

Changes:
- src/db/migrate.ts -> src/db/migrate.mjs: plain JS so the prod image
  can run it via `node` without devDependencies (no tsx needed).
- Dockerfile: COPY drizzle and src/db/migrate.mjs into the image,
  prepend `node ./src/db/migrate.mjs &&` to the CMD. Container fails
  to start if migrations fail — better than serving with stale schema.
- .github/workflows/deploy.yml: rsync now also sends `drizzle` and
  `src` so the build context on the server has what the Dockerfile
  COPYs reference.
- package.json: `db:migrate` script switched to `node src/db/migrate.mjs`.
- eslint.config.mjs: enable node globals for the migrator script.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 00:29:32 +03:00
emilandClaude Opus 4.7 4b78e9edad fix(auth): defer env validation to first access so build works without secrets
`validateAuthEnv()` was running at module-load time, which fired during
`astro build` while constructing the route manifest — before any actual
request needed the secrets. CI build failed even though build-time code
doesn't use JWT_SECRET / OAuth secrets.

- src/lib/auth/env.ts: wrap authEnv in a Proxy that runs validation
  on first property read; cache the validated object after.
- src/lib/auth/jwt.ts: defer `new TextEncoder().encode(...)` of the
  secret behind a memoised getSecret() helper.
- src/lib/auth/oauth.ts: same for the JWT secret; vkOAuthConfig and
  yandexOAuthConfig switched to getter-based properties so credential
  access is also lazy.
- src/lib/auth/auth.test.ts: 3 tests previously asserted "throws at
  module load"; updated to assert "throws on first access" — semantic
  guarantee (invalid env throws) is preserved.

Runtime fail-fast is intact: any auth route reading authEnv.X will
throw with the same descriptive Zod error if a var is missing. Build
just no longer crashes when secrets aren't in env (e.g. CI deploy
pipeline running tsc/lint/build without secrets).

Verified: npm run build succeeds with no auth env vars set; tsc 0,
lint 0, vitest 339/339.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-15 23:45:03 +03:00
emilandClaude Opus 4.7 f2afaf60a3 fix(lint): unblock CI — add scoped eslint-disable for APIContext mocks
The 6 API/CORS test files mock the Astro APIContext by spreading
`{} as any` to satisfy fields not under test (cookies, redirect,
clientAddress, etc.). Replacing each cast individually would require
either full typed contexts (obscures tests) or 70+ inline disables.

Add one file-level `eslint-disable @typescript-eslint/no-explicit-any`
with a comment explaining the rationale to each affected file.

Production code remains lint-clean. Verified: tsc 0, lint 0,
vitest 339/339.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-15 23:39:50 +03:00
emilandClaude Opus 4.7 2f7453e571 chore(qa): Final Wave cleanup — resolve lint findings from F2 review
Production code (all findings cleared):
- GenerationHistory: drop unused fetchProHistory(userId) param,
  change let activeId to const
- InitiativeTracker: remove write-only currentSessionName state
- NotesPanel: remove unused isLoading flag
- Open5eReference: drop unused getCachedTranslation/setCachedTranslation
  imports; use fetchTranslation result directly instead of re-lookup
- ai/openrouter.ts: drop unused z + Monster imports; attach cause to
  rethrown AbortError
- ai/kimi.ts: drop unused z import
- api/dm/ai/generate.ts: annotate best-effort counter increment catch
- api/dm/ai/history.ts: drop unused sql import

Test files (trivial fixes; ~70 mock-related `as any` errors remain as
pre-existing tech debt across api test mocks):
- translation-client: drop unused MockBroadcastChannel _name param and
  unused getCachedTranslation destructure
- open5e-types: eslint-disable for the intentional type-test aliases
- {notes,initiative}-api: remove unused mockUser2 placeholder
- {notes,initiative,translate,history}-api: let mockDb -> const mockDb
- ai-generate-api: wrap deliberate `var` mock decls in eslint-disable
  with comment explaining vi.mock hoisting requirement

Verification: tsc 0 errors, vitest 339/339, lint 81 -> 70 errors
(production code fully clean; remaining errors are test-mock as-any
patterns from Waves 5-6).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-15 23:33:57 +03:00
emilandClaude Opus 4.7 701398f627 feat(dm-dashboard): Wave 7 — FREE→PRO data import flow (Task 22)
Adds an opt-in modal that prompts a PRO user with local data to migrate
their localStorage notes and sessionStorage initiative into the cloud.

- src/lib/client/import.ts: pure helpers (hasLocalData, shouldShowImport)
- src/components/dm/ImportModal.astro: modal UI + import orchestration
  with conflict handling for existing "DM Notes" record
- src/i18n/dm-translations.ts: 14 new keys for the import flow
- Wired into both EN and RU dm/index.astro
- tests/import-flow.test.ts: 18 unit tests on the helpers

Local storage is preserved as backup; only a "dm-import-asked" flag is
set after the user makes a decision (import or skip).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-15 23:33:39 +03:00
emil 09e947f430 feat(dm-dashboard): Wave 6 — NPC form, result card, history panel, notes/initiative DB migration, translation service 2026-05-15 23:09:06 +03:00
emil 75f2dc6972 feat(dm-dashboard): Wave 5 — NPC generation API, history API, quota/tier badges, translate button 2026-05-15 22:45:26 +03:00
emil 2921e45237 feat(dm-dashboard): Wave 4 — rate limiting, translation API, AI tab navigation 2026-05-15 22:26:41 +03:00
emil 5aec915034 feat(dm-dashboard): Wave 3 — Boosty verification, notes/initiative API routes 2026-05-15 22:00:19 +03:00
emil 0bbaa6e3d8 feat(dm-dashboard): Wave 2 — Drizzle migration, DmSidebar fix, CORS config 2026-05-15 21:48:09 +03:00
emil 25f43ec4e2 feat(dm-dashboard): Wave 1 — auth fixes, DB schema, Open5e interfaces, AI clients
- Fix auth anti-patterns (JWT, OAuth, logout, middleware, url.origin)
- Extend DB schema with tier, npcs, counters, translations, notes, initiative
- Expand Open5e TypeScript interfaces (Monster, Spell) to full V2
- Build OpenRouter API client for FREE tier
- Build Kimi API client for PRO tier
- Add comprehensive tests for all modules
2026-05-15 21:28:42 +03:00
emilandClaude Opus 4.7 1f09270329 fix(dm): polish hardcoded strings, typo, and aria-labels
- Fix typo "combatантов" -> "участников боя" in dm-translations
- Replace hardcoded English "Result", "Rolls:", "Invalid notation"
  in DiceRoller with translation keys
- Translate English aria-labels ("Quick dice selectors", "Roll history",
  "Combatants") to Russian
- Move InitiativeTracker hardcoded strings ("Итого", "Инициатива",
  "Порядок хода", help text) into the translation file

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-15 18:31:21 +03:00
emilandClaude Opus 4.7 c01c34d3e0 style(dm): widen desktop dashboard at xl/2xl breakpoints
Bump container max-width and right context column at xl/2xl so the
reference and notes panels get noticeably more room on larger monitors.
Mobile and lg layouts are unchanged.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-15 18:23:35 +03:00
emilandSisyphus 659b042de5 fix(lint): resolve explicit any and empty catch in DM test scripts
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-15 18:10:36 +03:00
emilandSisyphus 8eafd504b2 fix(lint): resolve explicit any and useless assignment in DM components
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-15 18:10:28 +03:00
emilandSisyphus 3388d70d96 chore(lint): add debug-open5e.mjs to ESLint ignores
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-15 18:10:19 +03:00
emilandSisyphus fddc40e967 docs: note boulder tracking sync issue in DM dashboard redesign
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-15 18:02:00 +03:00
emil 1c38df7e74 style(dm): redesign dashboard with purple-gold theme
Redesign DM Dashboard (/dm/, /ru/dm/) with purple-gold theme (#534AB7 + #c8a84b):

Design System:
- Update dm-theme.css with 49 purple-gold tokens (transitions, shadows, hover states)
- Create DmTabs.astro with ARIA, keyboard nav, URL hash sync, localStorage persistence
- Create DmSidebar.astro for desktop navigation
- Redesign DmHeader with gold accent
- Update DmButton, DmCard, DmInput primitives
- Expand i18n to 47 strings

Layout:
- Redesign DmLayout with 3-column dashboard grid (sidebar|main|context)
- Update /dm/index.astro and /ru/dm/index.astro with named slots

Feature Components:
- DiceRoller: empty state, ARIA live, color-coded history, storage key fallback
- InitiativeTracker: HP editing, active highlighting, storage key fallback
- Open5eReference: skeletons, debounce, error retry, gold tabs
- NotesPanel: cross-tab sync, copy-to-clipboard, auto-save
- AuthPanel: VK/Yandex brand colors, pill buttons

Tests:
- Add 50 Playwright E2E tests (smoke, dice, initiative, reference, notes, data-migration)

Fixes:
- Document Open5e client.ts URL bugfix
- Add backward-compatible storage key fallback for dice/initiative
- Preserve legacy sessionStorage data on load
2026-05-15 17:49:32 +03:00
emil 11da171bfb docs: add AGENTS.md knowledge base (root + generators + dm) 2026-05-15 04:35:00 +03:00
emil 592fa4cc3e chore(vk): remove debug logging from callback 2026-05-15 04:21:34 +03:00
emil 5815b52c4e fix(vk): add client_id to user_info request, use query params for device_id 2026-05-15 04:18:19 +03:00
emil 57d56c7f29 debug(vk): log full callback URL and all query params 2026-05-15 04:15:07 +03:00
emil 258f062115 fix(lint): remove unused refreshToken variable 2026-05-15 04:11:07 +03:00
emil fbc011f55e fix(vk): use id.vk.ru domain, handle payload param, add device_id to token exchange 2026-05-15 04:09:02 +03:00
emil 5b6247ff7c style(auth): remove debug border from AuthPanel, clean up console.log 2026-05-15 03:55:51 +03:00
emil 6f2d9aff5d fix(auth): make DM pages server-rendered so middleware can set Astro.locals.user
Pages /dm/ and /ru/dm/ were static (prerendered), so middleware
never ran and Astro.locals.user was always null. Adding
prerender = false allows middleware to authenticate the user
before page render.
2026-05-15 03:50:41 +03:00
emil 38a778f3de debug(auth): add visible debug border and user state to AuthPanel 2026-05-15 03:40:01 +03:00
emil 08161dcdd1 fix(auth): unify cookie name to auth_token for OAuth and middleware compatibility
- Change COOKIE_NAME from 'session' to 'auth_token' in oauth.ts
- Update logout.ts to use Headers.append() and SameSite=Lax
- Fixes mismatch where callback set 'session' but middleware read 'auth_token'
2026-05-15 02:22:19 +03:00
emil de81e54ae1 fix(oauth): use SameSite=Lax for session cookie to survive cross-site redirect 2026-05-15 02:14:21 +03:00
emil b762e2eb66 fix(health): check DB connectivity in health endpoint 2026-05-15 02:05:40 +03:00
emil b71902b686 fix(oauth): use separate Set-Cookie headers for multiple cookies
Browser cannot parse multiple cookies from a single Set-Cookie header
joined by comma (RFC 6265). Use Headers.append() to send each cookie
in its own Set-Cookie header. Fixes state/verifier cookie mismatch.
2026-05-15 01:59:56 +03:00
emil da6208de50 fix(oauth): fix VK token URL (.ru -> .com), add error logging to callbacks 2026-05-15 01:50:45 +03:00
emil da0ee46bc0 fix(dm): scope DM theme CSS to .dm-theme class to override BaseLayout purple accent 2026-05-15 01:46:01 +03:00
emil aaf596a7bc fix(dm): OAuth redirect_uri, orange buttons, denser layout, hide lang switcher
- Fix OAuth redirect_uri mismatch in VK/Yandex callbacks (use PUBLIC_APP_URL)
- Make quick dice buttons and anchor nav links orange by default
- Densify DM Dashboard layout (smaller gaps, tighter spacing)
- Hide LanguageSwitcher/Blog/About/Privacy links on DM pages
- Increase notes textarea height for better usability
2026-05-15 01:42:09 +03:00
emil 5273518908 fix: SameSite=Lax for OAuth cookies to allow cross-site redirects 2026-05-15 01:28:24 +03:00
emil 2b57cba3bb fix: use process.env for runtime OAuth secrets 2026-05-15 01:25:27 +03:00
emil 4a13ba3272 fix deploy: copy dist directory instead of contents 2026-05-15 01:01:55 +03:00
emil d24f978d7e trigger deploy 2026-05-15 00:54:45 +03:00
emil 1a092e05c1 feat(dm): add dashboard with oauth, dice, initiative, open5e, notes 2026-05-14 23:48:36 +03:00
emil 7efe188a9a feat(ads): restore custom AdBanner with Admitad link 2026-05-14 16:21:50 +03:00
emil 87b35ba067 fix(ads): remove Admitad and Chitai-gorod banners completely 2026-05-14 15:55:37 +03:00
emil dfca40bb34 fix(lint): remove unused Props and eslint-disable directive 2026-05-14 15:38:04 +03:00
emil b22a68bf12 feat(ads): replace custom banner with Admitad leaderboard 2026-05-14 15:33:48 +03:00
emil 375455aac9 fix(ads): remove Yandex branding from banner, use neutral accent styling 2026-05-14 15:11:50 +03:00
emil 7c4241fd0e feat(ads): remove Yandex ads, move banner above generators 2026-05-14 15:07:17 +03:00
emil ae809cd119 feat(ads): replace Yandex Market with Admitad Читай-город affiliate link 2026-05-14 15:00:54 +03:00
emil f6f1ca05c9 feat(seo): add Admitad verification file 2026-05-14 14:48:07 +03:00
emil 2225044795 feat(seo): add Admitad verification meta tag 2026-05-14 14:43:06 +03:00
emil f3993a8315 feat(ads): replace YandexRTB placeholder with AdBanner referral banners 2026-05-14 14:10:22 +03:00
emil 11e7c9578a feat(ads): add Yandex Market referral link to banner rotation 2026-05-14 14:05:49 +03:00
emil 3917934d45 fix(blog): register @tailwindcss/typography for prose styling 2026-05-14 13:24:43 +03:00
emil 36a714d7e2 content(blog): add real articles to all 16 posts 2026-05-14 13:12:39 +03:00
emil db09f493e9 fix(blog): add generator links, sync dates to 2026-05-14
- Add back-to-generators link on EN and RU blog index pages
- Update all 16 blog post dates to 2026-05-14 with staggered times
- Sync EN and RU post dates so matching topics have identical timestamps
2026-05-14 10:32:53 +03:00
emil ea77828094 fix(blog): remove test-post.md, fix blog index slug links
- Delete extra test-post.md (scope creep from F4)
- Fix EN blog index: strip 'en/' prefix from post.id in links
- Fix RU blog index: strip 'ru/' prefix from post.id in links
- Fix schema URLs in both blog indices to use clean slugs
2026-05-14 03:28:08 +03:00
emil 25fd24d236 feat(blog+categories): Wave 3-5 — blog infrastructure, categories, content, navigation
- Install @astrojs/rss and setup content collections (blog + generators)
- Create BlogLayout with BlogPosting schema, article OG tags, prev/next nav
- Create blog post pages EN/RU ([slug].astro) with dynamic routing
- Create blog index pages EN/RU with post cards and filtering
- Create RSS feeds EN/RU with auto-discovery links
- Create generator category pages EN/RU (5 categories each)
- Add category navigation pills to homepage
- Create Breadcrumbs, RelatedGenerators, RelatedPosts components
- Add breadcrumbs to GeneratorLayout and BlogLayout
- Wire RelatedGenerators into GeneratorLayout
- Wire RelatedPosts into blog post pages
- Create 8 EN + 8 RU blog post templates with placeholder content
- Add blog link to LanguageSwitcher navigation
- Add category and blog translation keys
2026-05-14 03:19:06 +03:00
emil 23cf7058f0 feat(schema): Wave 2 — add generator categories, Organization schema, 404 pages
- Add category field to generatorSchema (required, 5 values)
- Assign categories to all 28 generators
- Add Organization JSON-LD schema to BaseLayout
- Create custom 404 pages (EN + RU)
- Add not-found and blog translation keys
2026-05-14 02:48:05 +03:00
emil 43ea9bc8f3 fix(seo): Wave 1 — fix critical SEO gaps
- Fix duplicate faq/ruFaq keys in dice.json, list.json, colors.json
- Add <main> landmarks to about/privacy pages (EN + RU)
- Move YandexRTB after <main> on homepage
- Add x-default hreflang to BaseLayout
- Configure sitemap i18n with hreflang alternates
- Add Open Graph + Twitter Card meta tags
- Create default OG image (1200x630)
2026-05-14 02:39:56 +03:00
emil 75038780d5 fix(lint): fix pre-existing unused vars in 5 generators
- EmojiGenerator: remove unused clickToCopyLabel, CopyFeedback import, forEach i
- FontPairGenerator: prefer-const loadedFonts, remove unused headingFamily, bodyFamily, copyIconSvg
- HashGenerator: remove unused copyIcon, checkIcon, copyLabel
- Magic8BallGenerator: remove unused getTypeLabel
- TeamsGenerator: remove unused teamPrefix, errAddTwo, errTeamsMin, errTeamsMax, errMorePlayers
2026-05-13 21:29:57 +03:00
emil 8a0e2b5e0f fix(lint): remove unused vars in refactored generators
- Fix 11 lint errors introduced during refactoring
- All remaining 16 errors are pre-existing in unrefactored files
2026-05-13 20:59:01 +03:00
emil 0081510914 Wave 6: refactor generators batch 4 to crypto random
- refactor(generators): migrate Teams, Time, Uuid, Weighted to crypto random
- refactor(generators): migrate WheelSpinner, YesNo to crypto random

All 28 generators now use cryptographically secure random via src/lib/client/random.ts
2026-05-13 20:41:27 +03:00
emil 8e0485c215 Wave 5: refactor generators batch 3 to crypto random
- refactor(generators): migrate Meal, Names, Number, Palette to crypto random
- refactor(generators): migrate Password, Rps, Shuffler to crypto random
2026-05-13 20:34:16 +03:00
emil 49269f209d Wave 4: refactor generators batch 2 to crypto random
- refactor(generators): migrate Gradient, Hash, Letter, List to crypto random
- refactor(generators): migrate Lorem, Lottery, Magic8Ball to crypto random
2026-05-13 20:25:39 +03:00
emil 3d94c99ff9 Wave 3: refactor generators batch 1 to crypto random
- refactor(generators): migrate Card, Coin, Color, Country to crypto random
- refactor(generators): migrate Date, Emoji, FontPair to crypto random
- refactor(dice): deduplicate DiceGenerator by importing from dice-engine.ts
2026-05-13 20:16:40 +03:00
emil e24a8b744d Wave 2: shared components + dice engine crypto migration
- feat(components): add ResultBox and CopyButton shared components
- refactor(dice): migrate dice engine to cryptographically secure random
2026-05-13 19:42:28 +03:00
emil 515c4a6168 Wave 1: add deps, crypto random utility, i18n helper, privacy fix, CI gates
- fix(deps): add zod and typescript to package.json
- feat(lib): add cryptographically secure random utility with TDD
- feat(lib): add client i18n helper with TDD
- fix(i18n): update privacy policy for crypto accuracy
- ci(deploy): add test and lint gates, fix SSH security
2026-05-13 19:34:42 +03:00
emil 2c400abb0a fix(magic8ball): enlarge triangle, shrink text further for long answers 2026-05-13 00:50:55 +03:00
emil 074adaa5e4 fix(magic8ball): lower text further and widen triangle for long answers 2026-05-13 00:45:27 +03:00
emil 40bf7dbacf fix(magic8ball): shrink text and widen triangle viewport for long answers
- Reduce font size: 8px mobile / 10px desktop
- Tighten padding and max-width for earlier line breaks
- Lower text with mt-5 for wider triangle area
- Widen triangle clip-path: 50% 12%, 94% 92%, 6% 92%
2026-05-13 00:43:18 +03:00
emil 87e11655f6 fix(magic8ball): shift answer text down with mt-3 instead of pt-2
- Replace padding-top with margin-top so text actually shifts down
  inside the flex-centered triangle viewport
2026-05-13 00:35:50 +03:00
emil 1b02227cdd Merge remote main: resolve conflicts, keep local icons and gitignore 2026-05-13 00:31:07 +03:00
emil 8f11c5af43 feat: add favorites panel to meal generator, remove redundant Another one button
- Add clickable favorites counter with expandable panel
- Render saved recipes list with remove button
- Remove duplicate 'Another one' button (Generate already covers it)
- Sync favorite button state when removing from panel
2026-05-13 00:25:09 +03:00
emil28092005 a1cc10d0a2 feat: add 4 new generators (Palette, Lorem, Country, Weighted)
- Color Palette: 7 harmony modes (Random, Analogous, Complementary,
  Triadic, Monochromatic, Split-Complementary, Tetradic), 3-8 colors,
  individual swatch copy + copy all
- Lorem Ipsum: 1-20 paragraphs, Short/Medium/Long length,
  optional classic start, full a11y support
- Random Country: 40 countries, region filter, flag emoji + capital
  + population, bilingual data
- Weighted Random: dynamic item-weight rows, validation,
  single pick + N-trials with statistics bars

All generators include EN/RU i18n, SEO metadata, FAQ, and follow
project patterns.
2026-05-12 16:22:26 +03:00
emil 02f9ada193 feat: add 6 new generators + Privacy Policy
New generators:
- Yes or No — random Yes/No/Maybe with confidence bar
- Random Name — male/female/any names (EN + RU databases)
- Team Splitter — split list into random teams
- Random Letter — A-Z / А-Я alphabet picker
- Random Date — date between two dates
- Rock Paper Scissors — play against random bot

Also added:
- Privacy Policy page (/privacy, /ru/privacy)
- FAQ blocks on all 16 generators (3 Q&A each, EN+RU)
- About page (/about, /ru/about)
- Improved Yandex RTB ad placement with min-height placeholder
- Navigation: About + Privacy links in LanguageSwitcher

Build: 38 pages, all passing
2026-05-11 23:34:31 +03:00
emil 6f8264a0fb fix: add PNG favicon for search engine snippets 2026-05-11 22:26:17 +03:00
emil cbc0a3b545 feat: prepare site for Yandex Ads (RSYA) moderation
- Add About page (/about, /ru/about) with mission, history, contacts
- Add FaqBlock component for generator FAQ sections
- Add FAQ to all 10 generators (3 Q&A each, EN+RU)
- Expand howTo/whenTo content on numbers, password, coin generators
- Add About link to LanguageSwitcher navigation
- Improve Yandex RTB ad block: min-height placeholder, no layout shift
- Extend generator schema with optional faq/ruFaq fields
- Update translations.ts with about and faq i18n keys
2026-05-11 21:59:37 +03:00
emil 7a29e3c192 feat: integrate Yandex RTB ad block R-A-19259130-1 2026-05-11 20:52:27 +03:00
emil 7e36ec52ec fix(wheel): default items separated by newlines for correct parsing 2026-05-11 01:40:14 +03:00
emil e089dd07de style: visual polish — accent tokens, starfield, fixed card/output heights, focus rings, spatial fixes 2026-05-11 01:36:58 +03:00
emil 8de77203aa feat: add JSON-LD structured data
- BaseLayout: WebPage schema on every page + canonical URL
- Homepages: WebSite schema
- GeneratorLayout: SoftwareApplication + BreadcrumbList schemas
- Add <slot name='head'> to BaseLayout for child-injected head content
2026-05-10 21:19:39 +03:00
emil 8421441664 chore: add ESLint + Prettier, fix all lint errors
- Install eslint, typescript-eslint, eslint-plugin-astro, prettier,
  prettier-plugin-astro, globals
- Add flat eslint.config.mjs with recommended rules for JS, TS, Astro
- Add .prettierrc with astro plugin
- Add lint, lint:fix, format, format:check scripts
- Fix prefer-const, no-unused-vars, no-useless-assignment across codebase
- Fix AdBanner.astro JSX fragment for Prettier compatibility
- Add eslint-disable around Yandex/Top.Mail.Ru inline scripts
- Auto-format entire codebase with Prettier
2026-05-10 21:17:34 +03:00
emil bb62d7310e test: add Vitest + 30 dice-engine tests
- Install vitest and @vitest/coverage-v8
- Add test scripts to package.json
- Create vitest.config.ts
- Add 30 comprehensive tests for parseDiceNotation, rollDice,
  rollAdvantage, buildNotation, isAdvancedNotation
- Fix isAdvancedNotation regex to detect ro (reroll once)
2026-05-10 21:12:08 +03:00
emil 3750cd2cc6 feat: add PWA support with offline caching
- Generate 192x192, 512x512 and Apple touch icons from favicon.svg
- Add web app manifest (manifest.json)
- Add service worker with network-first for HTML, cache-first for assets
- Register SW in BaseLayout
- Add theme-color meta and apple-touch-icon link
2026-05-10 21:06:19 +03:00
emil 56ab3f6952 refactor: extract shared client utilities
- Add src/lib/client/clipboard.ts with CopyFeedback class
- Add src/lib/client/validation.ts with createErrorDisplay and isInteger
- Add src/lib/client/animations.ts with popElement
- Refactor NumberGenerator, LotteryGenerator, PasswordGenerator,
  ListGenerator, CardGenerator, CoinGenerator, UuidGenerator,
  WheelSpinner to use shared utilities
- Leave DiceGenerator and ColorGenerator untouched (complex patterns)
2026-05-10 20:56:33 +03:00
emil f1ba82735b refactor: i18n routing, shared GeneratorLayout, analytics component, content collections
- Add Astro native i18n routing (prefixDefaultLocale: false)
- Introduce GeneratorLayout to deduplicate all generator pages
- Enrich generator data with pageTitle, ruPageTitle, howTo/whenTo arrays
- Make EN and RU generator pages identical using @/ path aliases
- Extract analytics (Yandex Metrika + Top.Mail.Ru) into Analytics.astro
- Move tracker IDs to src/data/config.ts, inject via define:vars
- Migrate generator metadata to Content Collections (src/content/generators/*.json)
- Add Zod schema with .strict() validation in src/lib/generator-schema.ts
- Update CLAUDE.md with new conventions
2026-05-10 20:42:05 +03:00
emil 195c2deb54 Merge branch 'main' of github.com:emil28092005/Randify.pro 2026-05-10 18:10:10 +03:00
emil 8f562d4319 feat: add VK (Top.Mail.Ru) pixel tracking 2026-05-10 18:09:35 +03:00
Emil Shanaty ef2aeb5345 feat(dice): full notation engine with kh/kl/dh/dl, exploding dice, reroll, presets, roll history, adv/disadv toggle
- Inline dice engine: XdY±Z, keep/drop (4d6dl1), exploding (3d6!), penetrating (!p), reroll (r1, ro1, r<3)
- Notation input with real-time validation and bidirectional sync with controls
- Advantage/Disadvantage toggle with smart disable when count>1 or keep/drop present
- 6 presets: Attack, Damage, Fireball, Sneak Attack, Stat Roll (4d6dl1), Savage Strike (2d6!)
- Roll history with dropped strikethrough, explosion chains, reroll arrows
- Updated SEO title/description and When-to-use blocks (EN+RU)
2026-05-10 01:23:50 +03:00
emilandClaude Sonnet 4.6 2168b88407 Reorder generators: dice 2nd, wheel 3rd (priority trio)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 19:19:54 +03:00
emilandClaude Sonnet 4.6 7c5f67eab8 Upgrade Dice Generator: notation input, advantage/disadvantage, history, D&D presets
Adds XdY+Z notation parsing with bidirectional sync, 3-way advantage/disadvantage
toggle, last-10 roll history in sessionStorage, and D&D quick presets (Attack,
Damage, Fireball, Sneak Attack, Stat Roll). Updates SEO meta and SeoBlock content
in both EN and RU pages.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 17:18:16 +03:00
emilandClaude Sonnet 4.6 b3069f5374 Update CLAUDE.md to reflect current bilingual architecture
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 17:00:13 +03:00
emilandClaude Sonnet 4.6 be6a0d712f Fix Russian generator pages: use individual pages instead of dynamic route
The dynamic [slug].astro imported all 10 generator components, causing Astro
to bundle all their scripts into every Russian page. Scripts then crashed
trying to find DOM elements that don't exist on the page.

Each Russian generator page now imports only its own component, matching
the English page structure.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 16:57:11 +03:00
emilandClaude Sonnet 4.6 65e42e7d44 Add Russian language version of the site
- New i18n system: src/i18n/translations.ts with en/ru translation objects
- LanguageSwitcher component (fixed top-right EN/RU toggle)
- BaseLayout updated with lang prop, hreflang links, auto-redirect for Russian browsers
- All 10 generator components updated to use T.* translations
- GeneratorCard updated to show ruTitle/ruDescription and link to /ru/ routes
- SeoBlock updated with translated section headers
- New /ru/ home page and /ru/generators/[slug] dynamic route (all 10 generators)
- Russian SEO content (howTo/whenTo) for all generator pages
- English pages explicitly pass lang="en"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 16:49:57 +03:00
emilandClaude Sonnet 4.6 76d9233faa feat: remove Update wheel button, wheel updates automatically on input
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 16:10:20 +03:00
emilandClaude Sonnet 4.6 6e540fdb12 feat: wheel auto-updates on textarea input with 300ms debounce
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 16:08:41 +03:00
emilandClaude Sonnet 4.6 e72312f23f fix: confetti uses position:fixed to prevent page layout expansion
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 16:06:37 +03:00
emilandClaude Sonnet 4.6 3863eb214e feat: add Spin the Wheel generator
Canvas-based wheel with 8-color palette, ease-out cubic spin animation,
deterministic winner calculation, confetti burst on result, copy to clipboard.
Supports 2–24 custom items with live textarea update.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 16:03:30 +03:00
emilandClaude Sonnet 4.6 f0ed8304b6 feat: add Yandex Metrika counter to all pages
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 15:30:52 +03:00
emilandClaude Sonnet 4.6 2ba1b53ba8 feat: randomise ad banner between Yandex app and Yandex Browser offers
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 15:10:51 +03:00
emilandClaude Sonnet 4.6 70eac59eb8 fix: move SEO text blocks below ad banner on generator pages
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 15:04:44 +03:00
emilandClaude Sonnet 4.6 57f1a8d705 seo: add How to use / When to use text blocks to all generator pages
Each page now has a subtle text section with keyword-rich content
for search engine indexing, without disrupting the tool UX.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 15:03:28 +03:00