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
Decompose monolithic Dashboard into SubscriptionCardActive,
SubscriptionCardExpired, TrialOfferCard, and StatsGrid sub-components.
Replace Subscription page progress bar with TrafficProgressBar compact.
Add i18n keys for traffic zones, stats labels, expired/trial states.
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
Dashboard is the default landing page for all authenticated users.
Loading it eagerly eliminates one extra network round-trip for the
JS chunk (+30KB / +7KB gzip to main bundle, but saves ~200-500ms
of chunk fetch + parse time on first load).
- Move Google Fonts from CSS @import (render-blocking) to <link> in
index.html with preconnect for earlier discovery
- Remove @import from globals.css — browser no longer waits for CSS
parse -> @import fetch -> font CSS parse before first paint
- Defer initLogoPreload() to requestIdleCallback so logo fetch
doesn't compete with critical resources during page load
- 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)
- Create AdminRoleEdit.tsx for /admin/roles/create and /admin/roles/:id/edit
- Create AdminPolicyEdit.tsx for /admin/policies/create and /admin/policies/:id/edit
- Remove create/edit modals from AdminRoles.tsx and AdminPolicies.tsx
- Add new lazy routes in App.tsx with proper permission gates
- Keep delete confirmation as small inline modal (appropriate for destructive action)
- Read policy role_id from top-level field instead of conditions object
- Use policy.role_name for display, remove conditions.role_id lookup
- Remove duplicate role_id injection into conditions payload on submit
- Fix PermissionGate strings: policies:* -> roles:* to match backend
- Remove redundant client-side status filtering in audit log (already
filtered server-side)
- Move RBAC translations from banSystem to admin namespace in all 4 locales
- Fix AccessPolicy type: action (string) -> actions (string[])
- Fix AuditLogEntry type to match backend: add status, request_method, request_path, resource_type, user info
- Fix AuditLogFilters: resource -> resource_type, add status filter
- Handle null details safely in audit log expanded view
- Fix policy form submission and display to use actions array
- Add global settings section (CHANNEL_IS_REQUIRED_SUB, trial/paid toggles)
at top of AdminChannelSubscriptions page
- Add per-channel disable_trial_on_leave and disable_paid_on_leave toggles
in each channel card with inline Toggle controls
- Update RequiredChannel type and API request interfaces with new fields
- Add translations for globalSettings and perChannel in en/ru/fa/zh locales