From 9d423456af96dc49bbe21c07b1c14088a10e5e93 Mon Sep 17 00:00:00 2001 From: loki5512344 Date: Tue, 29 Sep 2026 17:22:02 +0200 Subject: [PATCH] feat(frontend): landing redesign, mock/fake-data cleanup, Safari fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Landing: - Rewrote all landing copy (ru/en): removed the tracked-caps eyebrow chrome pattern everywhere, active-voice player-facing text - ThemeStrip: full-bleed live-recolored ClickGui window instead of a boxed swatch panel; HudPlayground: full-size real HUD screen - ShowcaseTeaser: dropped the fake "example" fallback cards, shows an honest empty state when there aren't 3 real configs yet - Fixed VoxelScene parallax leaking outside the hero into the HUD section Data accuracy: the module list/counts advertised InvisESP, BlockESP and FakeLag — all three were removed from the mod earlier this session as cheat modules. Corrected the catalogue and the 88/52 module-count claims to the real 86/50. Design QOL pass: removed the same eyebrow-label chrome from the site footer; replaced plausible-looking fake share codes (howItWorks demo slots, the chat command typing demo) with an explicit placeholder string so they read as illustration, not fabricated real data. Perf: promoted .lv-lower-bg onto its own compositor layer (will-change/translateZ) — a full-height, multi-gradient, masked, continuously-animated background layer was repainting on every scroll tick in Safari. Claude-Session: https://claude.ai/code/session_01F1M1Jic1wTSn4igUENynmZ --- .../src/features/clickgui/parts/modules.ts | 6 +- frontend/src/features/landing/CommandHero.tsx | 62 ++--- frontend/src/features/landing/HowItWorks.tsx | 43 ++-- .../features/landing/effects/VoxelScene.tsx | 12 +- frontend/src/features/landing/i18n/en.ts | 89 +++---- frontend/src/features/landing/i18n/ru.ts | 91 +++---- .../landing/sections/explore/FaqDownload.tsx | 67 +++-- .../sections/explore/HudPlayground.tsx | 242 +++++------------- .../landing/sections/explore/ModuleWall.tsx | 50 ++-- .../sections/explore/ShowcaseTeaser.tsx | 59 ++--- .../sections/explore/hud/WidgetContent.tsx | 79 ++++++ .../landing/sections/explore/hud/layout.ts | 60 +++++ .../features/landing/sections/hero/Hero.tsx | 4 +- .../landing/sections/hero/ThemeStrip.tsx | 187 +++++++++----- .../src/features/landing/styles/landing.css | 55 +--- .../src/features/landing/styles/sections.css | 119 +++++++++ .../landing/tests/interactions.test.tsx | 103 ++++++++ .../features/landing/tests/landing.test.tsx | 22 +- .../features/landing/tests/sections.test.tsx | 14 +- frontend/src/shared/layout/Footer.tsx | 4 +- 20 files changed, 830 insertions(+), 538 deletions(-) create mode 100644 frontend/src/features/landing/sections/explore/hud/WidgetContent.tsx create mode 100644 frontend/src/features/landing/sections/explore/hud/layout.ts create mode 100644 frontend/src/features/landing/styles/sections.css create mode 100644 frontend/src/features/landing/tests/interactions.test.tsx diff --git a/frontend/src/features/clickgui/parts/modules.ts b/frontend/src/features/clickgui/parts/modules.ts index 80ea25c..10cbb8c 100644 --- a/frontend/src/features/clickgui/parts/modules.ts +++ b/frontend/src/features/clickgui/parts/modules.ts @@ -26,7 +26,7 @@ export const CATEGORIES: Category[] = [ id: 'player', name: 'Player', modules: list( - ['Auto Accept', 'Auto Leave', 'Auto Respawn', 'AutoTool', 'ClickPearl', 'Death Coords', 'FakeLag', 'Lock Slot', 'NoDelay', + ['Auto Accept', 'Auto Leave', 'Auto Respawn', 'AutoTool', 'ClickPearl', 'Death Coords', 'Lock Slot', 'NoDelay', 'NoInteract', 'Tape Mouse', 'Third Person', 'Use Tracker'], ['Auto Accept'], ), @@ -35,9 +35,9 @@ export const CATEGORIES: Category[] = [ id: 'visuals', name: 'Visuals', modules: list( - ['AlwaysVisibleHand', 'AmbientParticles', 'Animations', 'AspectRatio', 'AtmoDawnFog', 'BackSword', 'BlockESP', 'BlockHighlight', + ['AlwaysVisibleHand', 'AmbientParticles', 'Animations', 'AspectRatio', 'AtmoDawnFog', 'BackSword', 'BlockHighlight', 'CameraClip', 'Chams', 'ChinaHat', 'Crosshair', 'CrystalHoles', 'DropESP', 'EndCrystal', 'ESP', 'FovControl', 'FPS Optimizer', - 'Freecam', 'FullBright', 'Gps', 'Hitboxes', 'HitBubbles', 'HitEffect', 'InvisESP', 'Item Holder', 'Item Physic', 'JumpCircles', + 'Freecam', 'FullBright', 'Gps', 'Hitboxes', 'HitBubbles', 'HitEffect', 'Item Holder', 'Item Physic', 'JumpCircles', 'KillEffect', 'KineticLyrics', 'Light', 'Lightning Target ESP', 'MotionBlur', 'NameTags', 'NoRender', 'PostFX', 'Rain', 'ReimaginedVisual', 'ShaderSky', 'Shulker Preview', 'SoundESP', 'TazikHat', 'TotemFX', 'Trails', 'Trinket', 'Trollface Mask', 'ViewModel', 'Wet World', 'Wings', 'WorldParticles', 'WorldTweaks', 'Zoom'], diff --git a/frontend/src/features/landing/CommandHero.tsx b/frontend/src/features/landing/CommandHero.tsx index 723249f..7370beb 100644 --- a/frontend/src/features/landing/CommandHero.tsx +++ b/frontend/src/features/landing/CommandHero.tsx @@ -1,37 +1,39 @@ -import { useEffect, useState } from 'react' +import { useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { prefersReducedMotion } from '@shared/motion/reducedMotion' import { ButtonLink } from '@shared/ui/Button' import { useSession } from '../auth/session' +import { useInView } from './effects/useInView' +import '@features/landing/styles/sections.css' -const COMMAND = '%config load 7KQ3M9XA' +// A placeholder code, not a plausible real one — this types out a UI demo, not live data. +const COMMAND = '%config load ••••••••' const TYPE_DELAY_MS = 60 - +/** Cloud configs, told through the one thing a player actually does: a command in the game chat. */ export function CommandHero() { const { t } = useTranslation('landing') const { status } = useSession() return ( -
- {t('cloud.eyebrow')} -

- {t('cloud.headlinePart1')} - {' '}
- {t('cloud.headlinePart2')} -

-

{t('cloud.subtitle')}

-
- {status === 'authenticated' ? ( - {t('cloud.ctaAuthenticated')} - ) : ( - {t('cloud.ctaAnonymous')} - )} - - {t('cloud.ctaShowcase')} - -
+
+
+

{t('cloud.title')}

+
+

{t('cloud.subtitle')}

+
+ {status === 'authenticated' ? ( + {t('cloud.ctaAuthenticated')} + ) : ( + {t('cloud.ctaAnonymous')} + )} + + {t('cloud.ctaShowcase')} + +
+
+
) } @@ -40,28 +42,30 @@ function ChatBar() { const { t } = useTranslation('landing') const [typed, setTyped] = useState(() => (prefersReducedMotion() ? COMMAND.length : 0)) const done = typed >= COMMAND.length + // Typing starts once the chat is on screen, so the player actually sees the command go in. + const ref = useRef(null) + const inView = useInView(ref, { once: true, rootMargin: '-80px' }) useEffect(() => { - if (done) return + if (done || !inView) return const t = setInterval(() => setTyped((n) => Math.min(n + 1, COMMAND.length)), TYPE_DELAY_MS) return () => clearInterval(t) - }, [done]) + }, [done, inView]) return (
- {done ? ( - - ) : null} +
) } diff --git a/frontend/src/features/landing/HowItWorks.tsx b/frontend/src/features/landing/HowItWorks.tsx index c2ab9d4..a18fcba 100644 --- a/frontend/src/features/landing/HowItWorks.tsx +++ b/frontend/src/features/landing/HowItWorks.tsx @@ -1,45 +1,56 @@ import { useTranslation } from 'react-i18next' import { Link } from 'react-router' +import '@features/landing/styles/sections.css' export function HowItWorks() { const { t } = useTranslation('landing') + // Placeholder-style codes (not plausible real share codes) — this is an illustration of + // the slot/code concept, not a real config, so it shouldn't look like fabricated data. + const placeholderCode = t('howItWorks.slotCodePlaceholder') const slots = [ - { name: t('howItWorks.slotNamePvp'), code: '7KQ3M9XA' }, - { name: t('howItWorks.slotNameStream'), code: 'R2WD8HNC' }, - { name: t('howItWorks.slotNameLazy'), code: 'L5TB4QZE' }, + { name: t('howItWorks.slotNamePvp'), code: placeholderCode }, + { name: t('howItWorks.slotNameStream'), code: placeholderCode }, + { name: t('howItWorks.slotNameLazy'), code: placeholderCode }, { name: null, code: null }, ] const linkSteps = [t('howItWorks.linkStep1'), t('howItWorks.linkStep2'), t('howItWorks.linkStep3')] return (
-
+

{t('howItWorks.slotsTitle')}

-

{t('howItWorks.slotsDesc')}

+

{t('howItWorks.slotsDesc')}

-
    + {/* The four slots drawn as a Minecraft hotbar; the first one is the "selected" slot. */} +
      {slots.map((slot, i) => ( -
    • +
    • {slot.name ? ( <> - {slot.name} - {slot.code} + {slot.name} + {slot.code} ) : ( - {t('howItWorks.emptySlot')} + {t('howItWorks.emptySlot')} )}
    • ))}
-
+

{t('howItWorks.linkTitle')}

-
    - {linkSteps.map((step) => ( -
  1. - {step} +
      + {linkSteps.map((step, i) => ( +
    1. + + {step}
    2. ))}
    @@ -47,7 +58,7 @@ export function HowItWorks() {

    {t('howItWorks.showcaseText')}{' '} - + {t('howItWorks.showcaseLink')}

    diff --git a/frontend/src/features/landing/effects/VoxelScene.tsx b/frontend/src/features/landing/effects/VoxelScene.tsx index 406d6d5..5330fa9 100644 --- a/frontend/src/features/landing/effects/VoxelScene.tsx +++ b/frontend/src/features/landing/effects/VoxelScene.tsx @@ -130,8 +130,12 @@ function drawSky(ctx: CanvasRenderingContext2D, w: number, h: number) { } } -/** Procedural blocky dusk landscape with layered scroll parallax. Purely decorative. */ -export function VoxelScene({ accent, className = '' }: { accent: string; className?: string }) { +/** + * Procedural blocky dusk landscape with layered scroll parallax. Purely decorative. + * The parallax is tuned for the top of the page (it follows window.scrollY); scenes further down + * pass `parallax={false}`, or the layers slide out of their frame by the time it is on screen. + */ +export function VoxelScene({ accent, className = '', parallax = true }: { accent: string; className?: string; parallax?: boolean }) { const rootRef = useRef(null) const canvases = useRef<(HTMLCanvasElement | null)[]>([]) @@ -160,7 +164,7 @@ export function VoxelScene({ accent, className = '' }: { accent: string; classNa }, [accent]) useEffect(() => { - if (prefersReducedMotion()) return + if (!parallax || prefersReducedMotion()) return let frame = 0 const lags = [0.5, ...LAYERS.map((l) => l.lag)] const onScroll = () => { @@ -178,7 +182,7 @@ export function VoxelScene({ accent, className = '' }: { accent: string; classNa window.removeEventListener('scroll', onScroll) cancelAnimationFrame(frame) } - }, []) + }, [parallax]) return (