Polish landing copy, footer links, and buy button behavior.
Rewrite feature descriptions in narrative style, open buy links in a new tab, update support email, rename key action to Choose key, and remove privacy policy from the footer. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -9,7 +9,6 @@ const FOOTER_LINK_KEYS = [
|
|||||||
"faq",
|
"faq",
|
||||||
"enterKey",
|
"enterKey",
|
||||||
"support",
|
"support",
|
||||||
"privacy",
|
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
const FOOTER_HREFS: Record<(typeof FOOTER_LINK_KEYS)[number], string> = {
|
const FOOTER_HREFS: Record<(typeof FOOTER_LINK_KEYS)[number], string> = {
|
||||||
@@ -18,7 +17,6 @@ const FOOTER_HREFS: Record<(typeof FOOTER_LINK_KEYS)[number], string> = {
|
|||||||
faq: SITE_LINKS.faq,
|
faq: SITE_LINKS.faq,
|
||||||
enterKey: SITE_LINKS.enterKey,
|
enterKey: SITE_LINKS.enterKey,
|
||||||
support: SITE_LINKS.support,
|
support: SITE_LINKS.support,
|
||||||
privacy: SITE_LINKS.privacy,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export function Footer() {
|
export function Footer() {
|
||||||
@@ -30,7 +28,14 @@ export function Footer() {
|
|||||||
<p className={styles.copy}>© TTRPGPlayer</p>
|
<p className={styles.copy}>© TTRPGPlayer</p>
|
||||||
<nav className={styles.links} aria-label={copy.footer.aria}>
|
<nav className={styles.links} aria-label={copy.footer.aria}>
|
||||||
{FOOTER_LINK_KEYS.map((key) => (
|
{FOOTER_LINK_KEYS.map((key) => (
|
||||||
<a key={key} className={styles.link} href={FOOTER_HREFS[key]}>
|
<a
|
||||||
|
key={key}
|
||||||
|
className={styles.link}
|
||||||
|
href={FOOTER_HREFS[key]}
|
||||||
|
{...(key === "buy"
|
||||||
|
? { target: "_blank", rel: "noopener noreferrer" }
|
||||||
|
: undefined)}
|
||||||
|
>
|
||||||
{copy.footer.links[key]}
|
{copy.footer.links[key]}
|
||||||
</a>
|
</a>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ export function Header() {
|
|||||||
href={SITE_LINKS.buy}
|
href={SITE_LINKS.buy}
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
className={styles.actionBtn}
|
className={styles.actionBtn}
|
||||||
|
newTab
|
||||||
>
|
>
|
||||||
{copy.cta.buy}
|
{copy.cta.buy}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export function CtaBanner() {
|
|||||||
<h2 className={styles.title}>{copy.ctaBanner.title}</h2>
|
<h2 className={styles.title}>{copy.ctaBanner.title}</h2>
|
||||||
<div className={styles.actions}>
|
<div className={styles.actions}>
|
||||||
<Button href={SITE_LINKS.download}>{copy.cta.download}</Button>
|
<Button href={SITE_LINKS.download}>{copy.cta.download}</Button>
|
||||||
<Button href={SITE_LINKS.buy} variant="secondary">
|
<Button href={SITE_LINKS.buy} variant="secondary" newTab>
|
||||||
{copy.cta.buyLicense}
|
{copy.cta.buyLicense}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export function Hero() {
|
|||||||
|
|
||||||
<div className={styles.cta}>
|
<div className={styles.cta}>
|
||||||
<Button href={SITE_LINKS.download}>{copy.cta.downloadFree}</Button>
|
<Button href={SITE_LINKS.download}>{copy.cta.downloadFree}</Button>
|
||||||
<Button href={SITE_LINKS.buy} variant="secondary">
|
<Button href={SITE_LINKS.buy} variant="secondary" newTab>
|
||||||
{copy.cta.buyLicense}
|
{copy.cta.buyLicense}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ export function Pricing() {
|
|||||||
<Button
|
<Button
|
||||||
href={SITE_LINKS.buy}
|
href={SITE_LINKS.buy}
|
||||||
variant={recommended ? "gold" : "secondary"}
|
variant={recommended ? "gold" : "secondary"}
|
||||||
|
newTab
|
||||||
>
|
>
|
||||||
{copy.cta.buy}
|
{copy.cta.buy}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ type ButtonProps = {
|
|||||||
className?: string;
|
className?: string;
|
||||||
onClick?: () => void;
|
onClick?: () => void;
|
||||||
type?: "button" | "submit";
|
type?: "button" | "submit";
|
||||||
|
newTab?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
function join(...parts: (string | undefined)[]): string {
|
function join(...parts: (string | undefined)[]): string {
|
||||||
@@ -24,12 +25,19 @@ export function Button({
|
|||||||
className,
|
className,
|
||||||
onClick,
|
onClick,
|
||||||
type = "button",
|
type = "button",
|
||||||
|
newTab = false,
|
||||||
}: ButtonProps) {
|
}: ButtonProps) {
|
||||||
const cls = join(styles.button, styles[variant], className);
|
const cls = join(styles.button, styles[variant], className);
|
||||||
|
|
||||||
if (href) {
|
if (href) {
|
||||||
return (
|
return (
|
||||||
<a className={cls} href={href}>
|
<a
|
||||||
|
className={cls}
|
||||||
|
href={href}
|
||||||
|
{...(newTab
|
||||||
|
? { target: "_blank", rel: "noopener noreferrer" }
|
||||||
|
: undefined)}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
</a>
|
</a>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ export const SITE_LINKS = {
|
|||||||
download: "#platforms",
|
download: "#platforms",
|
||||||
buy: "https://donate.stream/ttrpgplayer",
|
buy: "https://donate.stream/ttrpgplayer",
|
||||||
enterKey: "#pricing",
|
enterKey: "#pricing",
|
||||||
support: "mailto:support@ttrpgplayer.example",
|
support: "mailto:fontosh.ivan@gmail.com",
|
||||||
privacy: "#",
|
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
/** Same feed as electron-builder `publish.url` in dnd_player/package.json */
|
/** Same feed as electron-builder `publish.url` in dnd_player/package.json */
|
||||||
|
|||||||
+22
-24
@@ -118,26 +118,26 @@ const ru = {
|
|||||||
editor: {
|
editor: {
|
||||||
title: "Редактор кампаний",
|
title: "Редактор кампаний",
|
||||||
description:
|
description:
|
||||||
"Граф сцен и переходов, ветвления, превью медиа, музыка сцены и кампании, импорт и экспорт .ttrpg.zip, поиск и управление медиа.",
|
"Планируйте кампанию как граф сцен: связывайте локации, закладывайте развилки сюжета, прикрепляйте карты и музыку — и собирайте материалы партии до первого броска костей.",
|
||||||
tags: "Граф сцен · переходы · медиа",
|
tags: "Граф сцен · ветвления · медиа",
|
||||||
},
|
},
|
||||||
control: {
|
control: {
|
||||||
title: "Пульт управления",
|
title: "Пульт управления",
|
||||||
description:
|
description:
|
||||||
"Переключение сцен одной кнопкой, предпросмотр экрана игроков, сюжетная линия, авто- и ручное управление музыкой, запуск и остановка демонстрации.",
|
"Ведите сессию из одного окна: переключайте сцены, следите за экраном игроков, держите ход сюжета под рукой и управляйте музыкой, пока стол ждёт вашего следующего хода.",
|
||||||
tags: "Переключить · музыка · сюжетная линия",
|
tags: "Сцены · музыка · сюжетная линия",
|
||||||
},
|
},
|
||||||
presentation: {
|
presentation: {
|
||||||
title: "Экран презентации",
|
title: "Экран презентации",
|
||||||
description:
|
description:
|
||||||
"Отдельное окно для второго монитора, проектора или ТВ. Только карта или видео сцены, синхронные эффекты и оверлеи, поддержка поворота 0°/90°/180°/270°.",
|
"Вынесите карту и атмосферу на второй монитор, проектор или ТВ — игроки видят только задуманное для сцены, без панелей редактора, с поворотом экрана под ваш стол.",
|
||||||
tags: "Карта · видео · второй монитор",
|
tags: "Второй экран · карта · атмосфера",
|
||||||
},
|
},
|
||||||
atmosphere: {
|
atmosphere: {
|
||||||
title: "Атмосфера за столом",
|
title: "Атмосфера за столом",
|
||||||
description:
|
description:
|
||||||
"Туман, дождь, огонь, вода, молния, луч света, заморозка, облако яда, эффекты действий, кисть мастера, ластик, очистка эффектов и музыка сцены плюс фон кампании.",
|
"Оживите карту погодой и светом, дорисуйте от руки то, что видят игроки, и меняйте настроение сцены музыкой — от тихой таверны до грозы над полем боя.",
|
||||||
tags: "Туман · дождь · огонь · кисть",
|
tags: "Погода · свет · музыка сцены",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -173,7 +173,7 @@ const ru = {
|
|||||||
pricing: {
|
pricing: {
|
||||||
title: "Выберите ключ мастера",
|
title: "Выберите ключ мастера",
|
||||||
subtitle:
|
subtitle:
|
||||||
"Активируйте продуктовый ключ в приложении: Настройки → Указать ключ",
|
"Активируйте продуктовый ключ в приложении: Настройки → Выбрать ключ",
|
||||||
recommendedBadge: "⭐ Рекомендуем",
|
recommendedBadge: "⭐ Рекомендуем",
|
||||||
tiers: {
|
tiers: {
|
||||||
basic: {
|
basic: {
|
||||||
@@ -225,7 +225,7 @@ const ru = {
|
|||||||
activate: {
|
activate: {
|
||||||
question: "Как активировать лицензию?",
|
question: "Как активировать лицензию?",
|
||||||
answer:
|
answer:
|
||||||
"В приложении откройте Настройки → Указать ключ и вставьте ключ формата TTRPG-….",
|
"В приложении откройте Настройки → Выбрать ключ и вставьте ключ формата TTRPG-….",
|
||||||
},
|
},
|
||||||
os: {
|
os: {
|
||||||
question: "На каких ОС работает приложение?",
|
question: "На каких ОС работает приложение?",
|
||||||
@@ -243,9 +243,8 @@ const ru = {
|
|||||||
download: "Скачать",
|
download: "Скачать",
|
||||||
buy: "Купить",
|
buy: "Купить",
|
||||||
faq: "FAQ",
|
faq: "FAQ",
|
||||||
enterKey: "Указать ключ",
|
enterKey: "Выбрать ключ",
|
||||||
support: "Поддержка",
|
support: "Поддержка",
|
||||||
privacy: "Политика конфиденциальности",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
localeSwitcher: {
|
localeSwitcher: {
|
||||||
@@ -373,26 +372,26 @@ const en: SiteCopy = {
|
|||||||
editor: {
|
editor: {
|
||||||
title: "Campaign editor",
|
title: "Campaign editor",
|
||||||
description:
|
description:
|
||||||
"Scene graph and transitions, branching, media previews, scene and campaign audio, import and export of .ttrpg.zip, search and media management.",
|
"Plan your campaign as a scene graph: link locations, lay out story branches, attach maps and music — and gather session materials before the first roll.",
|
||||||
tags: "Scene graph · transitions · media",
|
tags: "Scene graph · branching · media",
|
||||||
},
|
},
|
||||||
control: {
|
control: {
|
||||||
title: "Control panel",
|
title: "Control panel",
|
||||||
description:
|
description:
|
||||||
"One-click scene switching, player screen preview, story timeline, automatic and manual music control, start and stop presentation.",
|
"Run the session from one window: switch scenes, watch the player screen, keep the story beat at hand and control music while the table waits for your next move.",
|
||||||
tags: "Switch · music · story line",
|
tags: "Scenes · music · story line",
|
||||||
},
|
},
|
||||||
presentation: {
|
presentation: {
|
||||||
title: "Presentation screen",
|
title: "Presentation screen",
|
||||||
description:
|
description:
|
||||||
"Separate window for a second monitor, projector or TV. Scene map or video only, synced effects and overlays, rotation 0°/90°/180°/270°.",
|
"Send the map and atmosphere to a second monitor, projector or TV — players see only what the scene calls for, with no editor panels and rotation to fit your table setup.",
|
||||||
tags: "Map · video · second monitor",
|
tags: "Second screen · map · atmosphere",
|
||||||
},
|
},
|
||||||
atmosphere: {
|
atmosphere: {
|
||||||
title: "Atmosphere at the table",
|
title: "Atmosphere at the table",
|
||||||
description:
|
description:
|
||||||
"Fog, rain, fire, water, lightning, sunbeam, freeze, poison cloud, action effects, GM brush, eraser, clear effects, scene music plus campaign ambience.",
|
"Bring the map to life with weather and light, sketch what players see by hand, and shift the mood with scene music — from a quiet tavern to a storm over the battlefield.",
|
||||||
tags: "Fog · rain · fire · brush",
|
tags: "Weather · light · scene music",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -427,7 +426,7 @@ const en: SiteCopy = {
|
|||||||
},
|
},
|
||||||
pricing: {
|
pricing: {
|
||||||
title: "Choose your GM key",
|
title: "Choose your GM key",
|
||||||
subtitle: "Activate your product key in the app: Settings → Enter key",
|
subtitle: "Activate your product key in the app: Settings → Choose key",
|
||||||
recommendedBadge: "⭐ Recommended",
|
recommendedBadge: "⭐ Recommended",
|
||||||
tiers: {
|
tiers: {
|
||||||
basic: {
|
basic: {
|
||||||
@@ -479,7 +478,7 @@ const en: SiteCopy = {
|
|||||||
activate: {
|
activate: {
|
||||||
question: "How do I activate a license?",
|
question: "How do I activate a license?",
|
||||||
answer:
|
answer:
|
||||||
"In the app, open Settings → Enter key and paste a key in the TTRPG-… format.",
|
"In the app, open Settings → Choose key and paste a key in the TTRPG-… format.",
|
||||||
},
|
},
|
||||||
os: {
|
os: {
|
||||||
question: "Which operating systems are supported?",
|
question: "Which operating systems are supported?",
|
||||||
@@ -496,9 +495,8 @@ const en: SiteCopy = {
|
|||||||
download: "Download",
|
download: "Download",
|
||||||
buy: "Buy",
|
buy: "Buy",
|
||||||
faq: "FAQ",
|
faq: "FAQ",
|
||||||
enterKey: "Enter key",
|
enterKey: "Choose key",
|
||||||
support: "Support",
|
support: "Support",
|
||||||
privacy: "Privacy policy",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
localeSwitcher: {
|
localeSwitcher: {
|
||||||
|
|||||||
Reference in New Issue
Block a user