Remove opacity from beamDash keyframes — opacity on SVG paths triggers
full-page repaints. Animate only stroke-dashoffset (compositor-friendly).
Reduce animated paths from 50 to 17 (every 3rd). Isolate container with
contain:strict + will-change:transform for own compositor layer.
ThemeTab: throttle applyThemeColors via requestAnimationFrame (1x/frame),
debounce queryClient.setQueryData 150ms. Fixes browser lag when dragging
color sliders (~60 calls/sec × 50+ CSS var writes).
BackgroundBeams: replace 20 framer-motion linearGradient JS loops with
pure CSS stroke-dashoffset animation on all 50 original Aceternity paths.
Single shared gradient, GPU-composited keyframes, zero per-frame JS.
- Add glassTheme utility with theme-aware color tokens
- Replace hardcoded rgba(255,255,255,...) with dynamic values
- Replace text-white with text-dark-50 for auto theme remapping
- Restyle subscription page block to match dashboard glassmorphic design
- Light mode: white semi-opaque cards with subtle shadows
- Dark mode: unchanged visual appearance
- Rewrite TrafficProgressBar with multi-segment gradient fill, flex-based
zone tints, shimmer + highlight overlays, radial glow at fill edge
- Rewrite SubscriptionCardActive with zone header, big percentage on right,
connect device card with device dots, tariff/days-left two-column row,
sparkline placeholder, traffic refresh controls
- Rewrite SubscriptionCardExpired with red glow, grid pattern, gradient
renew button, outline tariffs button
- Rewrite TrialOfferCard with animated glow background, grid pattern,
icon glow animation, price tag with old price, gradient CTA buttons
- Rewrite StatsGrid with 2x2 card layout, icon+label+chevron header,
big value numbers, zone-colored balance/earnings cards
- Update Sparkline with color prop and last-point dot indicator
- Update useAnimatedNumber to use easeOutExpo matching prototype
- Add formatTraffic utility for MB/GB/TB unit formatting
- Add mainHex to trafficZone for inline style support
- Update animations to match prototype timing
- Add new i18n keys for redesigned components
SubscriptionCardActive with animated zone dot, traffic bar, connect button.
SubscriptionCardExpired with red theme and renewal actions.
TrialOfferCard with stats grid and free/paid activation.
StatsGrid 2x2 with font-display values and font-mono labels.
Pure CSS approach: @property --border-angle + conic-gradient on border-box.
No extra elements, no overflow issues, no JS animation.
Smooth rotation via CSS Houdini custom property animation.
Replace framer-motion gradient interpolation (buggy in TG WebView) with
pure CSS conic-gradient + transform:rotate() keyframe animation.
Transform animations are GPU-accelerated and reliable everywhere.
- Use accent CSS vars for gradient (matches app theme dynamically)
- Increase gradient spot size for visible animation
- Darker inner bg (dark-900) for better contrast
- Increase blur to 3px for glow effect
- Add prefetchBackground() to registry — triggers dynamic import
without creating React component
- Call prefetch at module init in BackgroundRenderer using cached
config from localStorage, before React renders
- Background JS chunk starts downloading immediately instead of
waiting for Suspense to trigger lazy() import
- Use 300% oversized CSS grid container instead of fixed-size flex cells
- Grid with 1fr units auto-fills the container, skew+scale covers viewport
- Simplified to flat cell array, removed SVG crosses for cleaner look
- Use original Aceternity positioning: translate(-40%,-60%) with
-top-1/4 left-1/4 to cover viewport after skew transform
- Switch from CSS grid to flex layout matching original structure
- Add cross (+) SVG markers at grid intersections
- Use pastel color palette from original, animate opacity per cell
- Increase default rows to 20 for better coverage
- Delete google-gemini-effect.tsx and noise-background.tsx
- Remove from types, registry, and backend whitelist
- Rewrite aurora: use animate-aurora directly on element instead of
invisible pseudo-element with mix-blend-difference
- Aurora: remove invert filter (broken on dark bg), use dark gradient directly
- Beams: use animated linearGradient positions instead of pathLength animation
- Beams Collision: add collision detection, explosion particles, AnimatePresence
- Gemini Effect: rewrite as SVG motion.path with animated pathLength (was canvas)
- Noise: change mixBlendMode from overlay to screen (overlay invisible on dark bg)
- Admin panel: added Edit button on channel cards with inline edit form
(title, link, sort order) using existing PATCH API
- Subscription screen: filter out already-subscribed channels so users
only see what they still need to subscribe to
- Updated channelIdHint to remove @username reference (numeric only)
- Added i18n keys: edit, editing, form.save (en/ru/fa/zh)
- Channel subscription blocking screen with per-channel status display
- Safe URL handling with protocol validation (no open redirect)
- Race-condition-safe subscription check with useRef guard
- Admin channel management page with create, toggle, delete
- Error handlers on all admin mutations with haptic feedback
- Proper type guard for channel subscription error detection
- Accessibility: aria-labels on icon-only buttons
- Localization: en, ru, zh, fa translations
Replace all bare useAuthStore(), useBlockingStore(), and
useSuccessNotification() calls with individual field selectors.
This prevents components from re-rendering when unrelated store
fields change.
- 21 useAuthStore usages across 20 files: individual selectors
for 1-2 fields, useShallow for 3+ fields
- 5 useBlockingStore usages: individual selectors
- 2 useSuccessNotification usages: individual selectors
- Remove mix-blend-mode: overlay from body::before noise texture
(was forcing fullscreen GPU compositing on every paint)
- Disable noise texture on mobile via display:none media query
- Replace backdrop-filter animation in backdropFadeIn/Out keyframes
with opacity-only animation (backdrop-filter transition is expensive)
- Replace toast progress bar width animation with scaleX (GPU-composited)
- Remove redundant will-change: transform from .glass and .wave-blob
- Fix isWebglAvailable() leaking a full WebGL context on every check
by replacing OGL Renderer with raw canvas.getContext() + loseContext()
- Add visibilitychange listener to fully stop rAF loop when tab hidden
and restart on visibility restore (was running 24/7 in background)
- Properly destroy WebGL context on unmount via WEBGL_lose_context
extension and gl.deleteProgram() (was only nulling JS refs)
- Reduce target FPS from 20 to 10 (slow gradient, visually identical)
Mirrors campaign.ts localStorage pattern to capture ?ref= from URL and pass
referral_code to backend during Telegram Widget, OAuth, email login, and
Mini App authentication. Fixes redirect loop when email auth is disabled.
- Add campaign.ts utility (capture from URL, localStorage with TTL, validation)
- Pass campaign_slug in all auth API methods (telegram, widget, email, oauth)
- Consume slug in auth store login methods, set pendingCampaignBonus state
- Add CampaignBonusNotifier component (toast on bonus, mounted in AppShell)
- Show web_link alongside bot deep_link in AdminCampaignStats
- Add CampaignBonusInfo type with union bonus_type
- Fix VerifyEmail: ref guard against StrictMode double-fire, setTimeout cleanup
- Fix AdminCampaignStats: setTimeout refs with cleanup, i18n-aware locale
- Wrap all localStorage access in try/catch (Safari private browsing safety)
- Add campaignBonus translations (ru/en/fa/zh)
- Branding cache moved from localStorage to sessionStorage so it clears
when the mini-app is closed, preventing stale names after admin updates
- Added initialDataUpdatedAt: 0 to all branding queries so fresh data is
always fetched on page refresh while showing cached data instantly
- One-time migration moves existing localStorage value to sessionStorage
- Aurora component now checks WebGL availability upfront, skipping all
hooks and subscriptions when WebGL is not supported
- Add flex-wrap to color picker chips so they wrap on narrow cards
- Add min-w-0/truncate to header text, shrink-0 to badge+toggle group
- Reduce chip padding for better fit in 2-column grid
- Add overflow-hidden to card container as safety net
- Extract normalizeConfig() for consistent response normalization
- Add onError handlers to mutations with useNotify
- Add window.confirm before destructive reset action
- Add resetConfirm translation key to all locales
- Add Toggle per section card to show/hide button in main menu
- Add collapsible locale label inputs (ru, en, ua, zh, fa) per section
- Normalize server response for backward compatibility with older API
- Update all 4 locale files with new translation keys
Adds a 'default' style option that removes color from buttons, showing
Telegram's native default style. Updates type, UI chip, preview, and
translations for all 4 locales.
Visual editor in admin settings for configuring Telegram button colors
and custom emoji IDs per menu section. Includes API client, draft state
management, save/reset, and translations for ru/en/fa/zh locales.
OGL renderer.setSize() sets both the internal buffer and CSS dimensions,
so the 10% resolution canvas was only covering 10% of the screen.
Now explicitly reset canvas.style.width/height to 100% after setSize()
so the small buffer is upscaled by the browser to full viewport.
- Render WebGL canvas at 10% viewport resolution (~20K px vs 2M px)
- Replace backdrop-filter: blur(80px) with filter: blur(20px) on canvas
(backdrop-filter composites ALL layers underneath — 10-20x more expensive)
- Disable antialiasing (useless when output is blurred)
- Lower target FPS from 30 to 20 (imperceptible for ambient background)
- Add CSS contain: strict to isolate layout/paint recalculations
- Remove separate blur overlay div (one element instead of two)
Reported: GTX 1660S showed 30-55% GPU load with cabinet open.
Expected: ~1-5% GPU after this change.
Logo images were exposing the backend API URL in the DOM via <img src>.
Now preloadLogo() fetches the image as a blob and serves it through
URL.createObjectURL(), so only blob: URLs appear in the page source.
Blob is invalidated on logo upload/delete.
Replace generic placeholder icons with designs based on actual favicon
assets from payment provider websites. Updated icons for Platega,
Tribute, WATA, CloudPayments, Kassa AI, YooKassa, CryptoBot, Heleket,
MulenPay, PAL24, and Freekassa with correct brand colors and marks.
savedColorsRef was unconditionally updated in the serverColors sync
effect, including when queryClient.setQueryData was called from
updateDraftColor for live preview. This made hasUnsavedChanges always
false, hiding the save/cancel buttons. Move savedColorsRef update
inside the sync condition so it only updates when no local edits exist.
- Add OAuthProvider type and extend User.auth_type union
- Add OAuth API methods (providers, authorize, callback)
- Add loginWithOAuth to auth store
- Create OAuthCallback page with state validation
- Create OAuthProviderIcon component with brand SVGs
- Add OAuth buttons to Login page between Telegram and Email
- Add OAuth callback route to App.tsx
- Add translations for ru, en, zh, fa
Save and Cancel buttons were hidden inside the collapsible "Custom Colors"
section, so manual color changes were lost on navigation. Moved them to
the top level so they are always visible when there are unsaved changes.