mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
feat: add a Keep it free sponsor button to the top nav (#427)
Adds a prominent Keep it free sponsor button to the top nav, linking to https://github.com/sponsors/snapotter-hq. Solid orange pill on desktop (left of the avatar), orange heart icon on mobile. Opens in a new tab with rel=noopener noreferrer, so no referrer or user data leaks, and it adds no passive network activity (offline-mode compatible). Fires an opt-in, property-less sponsor_clicked analytics event. Adds sidebar.sponsor and a11y.sponsorLink across all 21 locales. Claude-Session: https://claude.ai/code/session_01DnYLLA5z4Uf1GDeEPENVgr
This commit is contained in:
@@ -1,8 +1,10 @@
|
|||||||
|
import { ANALYTICS_EVENTS } from "@snapotter/shared";
|
||||||
import {
|
import {
|
||||||
ChevronLeft,
|
ChevronLeft,
|
||||||
ChevronRight,
|
ChevronRight,
|
||||||
FolderOpen,
|
FolderOpen,
|
||||||
Globe,
|
Globe,
|
||||||
|
Heart,
|
||||||
HelpCircle,
|
HelpCircle,
|
||||||
LayoutGrid,
|
LayoutGrid,
|
||||||
MessageSquare,
|
MessageSquare,
|
||||||
@@ -128,6 +130,8 @@ export function TopNav({
|
|||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<SponsorButton isDark={isDark} compact />
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={onHelpClick}
|
onClick={onHelpClick}
|
||||||
@@ -280,12 +284,58 @@ export function TopNav({
|
|||||||
<HelpCircle className="h-4 w-4" />
|
<HelpCircle className="h-4 w-4" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<SponsorButton isDark={isDark} />
|
||||||
|
|
||||||
{!isMobile && <AvatarDropdown onSettingsClick={onSettingsClick} variant={variant} />}
|
{!isMobile && <AvatarDropdown onSettingsClick={onSettingsClick} variant={variant} />}
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SPONSOR_URL = "https://github.com/sponsors/snapotter-hq";
|
||||||
|
|
||||||
|
function SponsorButton({ isDark, compact = false }: { isDark: boolean; compact?: boolean }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
import("@/lib/analytics").then(({ track }) => {
|
||||||
|
track(ANALYTICS_EVENTS.SPONSOR_CLICKED);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (compact) {
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
href={SPONSOR_URL}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
onClick={handleClick}
|
||||||
|
aria-label={t.a11y.sponsorLink}
|
||||||
|
title={t.a11y.sponsorLink}
|
||||||
|
className={cn(
|
||||||
|
"p-1.5 rounded-md transition-colors text-primary",
|
||||||
|
isDark ? "hover:bg-[#333]" : "hover:bg-muted",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Heart className="h-4 w-4 fill-current" />
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
href={SPONSOR_URL}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
onClick={handleClick}
|
||||||
|
className="ms-1 inline-flex items-center gap-1.5 rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-[#1a1814] transition-opacity hover:opacity-90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1"
|
||||||
|
>
|
||||||
|
<Heart className="h-3.5 w-3.5 fill-current" aria-hidden="true" />
|
||||||
|
{t.sidebar.sponsor}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function ThemeToggle({ isDark }: { isDark: boolean }) {
|
function ThemeToggle({ isDark }: { isDark: boolean }) {
|
||||||
const { resolvedTheme, toggleTheme } = useTheme();
|
const { resolvedTheme, toggleTheme } = useTheme();
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export const ANALYTICS_EVENTS = {
|
|||||||
AI_BUNDLE_PROMPTED: "ai_bundle_prompted",
|
AI_BUNDLE_PROMPTED: "ai_bundle_prompted",
|
||||||
BATCH_PROCESSED: "batch_processed",
|
BATCH_PROCESSED: "batch_processed",
|
||||||
FEEDBACK_SUBMITTED: "feedback_submitted",
|
FEEDBACK_SUBMITTED: "feedback_submitted",
|
||||||
|
SPONSOR_CLICKED: "sponsor_clicked",
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export type AnalyticsEvent = (typeof ANALYTICS_EVENTS)[keyof typeof ANALYTICS_EVENTS];
|
export type AnalyticsEvent = (typeof ANALYTICS_EVENTS)[keyof typeof ANALYTICS_EVENTS];
|
||||||
|
|||||||
@@ -3819,6 +3819,7 @@ export const ar: TranslationKeys = {
|
|||||||
connectionError: "خطأ في الاتصال",
|
connectionError: "خطأ في الاتصال",
|
||||||
},
|
},
|
||||||
sidebar: {
|
sidebar: {
|
||||||
|
sponsor: "أبقِه مجانيًا",
|
||||||
tools: "الأدوات",
|
tools: "الأدوات",
|
||||||
grid: "شبكة",
|
grid: "شبكة",
|
||||||
automate: "أتمتة",
|
automate: "أتمتة",
|
||||||
@@ -3838,6 +3839,7 @@ export const ar: TranslationKeys = {
|
|||||||
mobileNavSettings: "الإعدادات",
|
mobileNavSettings: "الإعدادات",
|
||||||
},
|
},
|
||||||
a11y: {
|
a11y: {
|
||||||
|
sponsorLink: "ادعم SnapOtter على GitHub Sponsors",
|
||||||
skipToContent: "تخطي إلى المحتوى الرئيسي",
|
skipToContent: "تخطي إلى المحتوى الرئيسي",
|
||||||
openSidebar: "فتح الشريط الجانبي",
|
openSidebar: "فتح الشريط الجانبي",
|
||||||
closeSidebar: "إغلاق الشريط الجانبي",
|
closeSidebar: "إغلاق الشريط الجانبي",
|
||||||
|
|||||||
@@ -3866,6 +3866,7 @@ export const de: TranslationKeys = {
|
|||||||
connectionError: "Verbindungsfehler",
|
connectionError: "Verbindungsfehler",
|
||||||
},
|
},
|
||||||
sidebar: {
|
sidebar: {
|
||||||
|
sponsor: "Kostenlos halten",
|
||||||
tools: "Werkzeuge",
|
tools: "Werkzeuge",
|
||||||
grid: "Raster",
|
grid: "Raster",
|
||||||
automate: "Automatisieren",
|
automate: "Automatisieren",
|
||||||
@@ -3885,6 +3886,7 @@ export const de: TranslationKeys = {
|
|||||||
mobileNavSettings: "Einstellungen",
|
mobileNavSettings: "Einstellungen",
|
||||||
},
|
},
|
||||||
a11y: {
|
a11y: {
|
||||||
|
sponsorLink: "SnapOtter auf GitHub Sponsors unterstützen",
|
||||||
skipToContent: "Zum Hauptinhalt springen",
|
skipToContent: "Zum Hauptinhalt springen",
|
||||||
openSidebar: "Seitenleiste öffnen",
|
openSidebar: "Seitenleiste öffnen",
|
||||||
closeSidebar: "Seitenleiste schließen",
|
closeSidebar: "Seitenleiste schließen",
|
||||||
|
|||||||
@@ -3795,6 +3795,7 @@ export const en = {
|
|||||||
automate: "Automate",
|
automate: "Automate",
|
||||||
editor: "Editor",
|
editor: "Editor",
|
||||||
files: "Files",
|
files: "Files",
|
||||||
|
sponsor: "Keep it free",
|
||||||
help: "Help",
|
help: "Help",
|
||||||
settings: "Settings",
|
settings: "Settings",
|
||||||
},
|
},
|
||||||
@@ -3870,6 +3871,7 @@ export const en = {
|
|||||||
helpDialog: "Help",
|
helpDialog: "Help",
|
||||||
navigationMenu: "Navigation",
|
navigationMenu: "Navigation",
|
||||||
homeLink: "SnapOtter home",
|
homeLink: "SnapOtter home",
|
||||||
|
sponsorLink: "Support SnapOtter on GitHub Sponsors",
|
||||||
},
|
},
|
||||||
pipelineTemplates: {
|
pipelineTemplates: {
|
||||||
"web-optimize": {
|
"web-optimize": {
|
||||||
|
|||||||
@@ -3844,6 +3844,7 @@ export const es: TranslationKeys = {
|
|||||||
connectionError: "Error de conexión",
|
connectionError: "Error de conexión",
|
||||||
},
|
},
|
||||||
sidebar: {
|
sidebar: {
|
||||||
|
sponsor: "Mantenlo gratis",
|
||||||
tools: "Herramientas",
|
tools: "Herramientas",
|
||||||
grid: "Cuadrícula",
|
grid: "Cuadrícula",
|
||||||
automate: "Automatizar",
|
automate: "Automatizar",
|
||||||
@@ -3863,6 +3864,7 @@ export const es: TranslationKeys = {
|
|||||||
mobileNavSettings: "Configuración",
|
mobileNavSettings: "Configuración",
|
||||||
},
|
},
|
||||||
a11y: {
|
a11y: {
|
||||||
|
sponsorLink: "Apoya a SnapOtter en GitHub Sponsors",
|
||||||
skipToContent: "Ir al contenido principal",
|
skipToContent: "Ir al contenido principal",
|
||||||
openSidebar: "Abrir barra lateral",
|
openSidebar: "Abrir barra lateral",
|
||||||
closeSidebar: "Cerrar barra lateral",
|
closeSidebar: "Cerrar barra lateral",
|
||||||
|
|||||||
@@ -3868,6 +3868,7 @@ export const fr: TranslationKeys = {
|
|||||||
connectionError: "Erreur de connexion",
|
connectionError: "Erreur de connexion",
|
||||||
},
|
},
|
||||||
sidebar: {
|
sidebar: {
|
||||||
|
sponsor: "Gardons-le gratuit",
|
||||||
tools: "Outils",
|
tools: "Outils",
|
||||||
grid: "Grille",
|
grid: "Grille",
|
||||||
automate: "Automatiser",
|
automate: "Automatiser",
|
||||||
@@ -3887,6 +3888,7 @@ export const fr: TranslationKeys = {
|
|||||||
mobileNavSettings: "Paramètres",
|
mobileNavSettings: "Paramètres",
|
||||||
},
|
},
|
||||||
a11y: {
|
a11y: {
|
||||||
|
sponsorLink: "Soutenir SnapOtter sur GitHub Sponsors",
|
||||||
skipToContent: "Aller au contenu principal",
|
skipToContent: "Aller au contenu principal",
|
||||||
openSidebar: "Ouvrir le panneau latéral",
|
openSidebar: "Ouvrir le panneau latéral",
|
||||||
closeSidebar: "Fermer le panneau latéral",
|
closeSidebar: "Fermer le panneau latéral",
|
||||||
|
|||||||
@@ -3649,6 +3649,7 @@ export const hi: TranslationKeys = {
|
|||||||
connectionError: "कनेक्शन त्रुटि",
|
connectionError: "कनेक्शन त्रुटि",
|
||||||
},
|
},
|
||||||
sidebar: {
|
sidebar: {
|
||||||
|
sponsor: "इसे मुफ़्त रखें",
|
||||||
tools: "टूल्स",
|
tools: "टूल्स",
|
||||||
grid: "ग्रिड",
|
grid: "ग्रिड",
|
||||||
automate: "ऑटोमेट",
|
automate: "ऑटोमेट",
|
||||||
@@ -3668,6 +3669,7 @@ export const hi: TranslationKeys = {
|
|||||||
mobileNavSettings: "सेटिंग्स",
|
mobileNavSettings: "सेटिंग्स",
|
||||||
},
|
},
|
||||||
a11y: {
|
a11y: {
|
||||||
|
sponsorLink: "GitHub Sponsors पर SnapOtter का समर्थन करें",
|
||||||
skipToContent: "मुख्य सामग्री पर जाएं",
|
skipToContent: "मुख्य सामग्री पर जाएं",
|
||||||
openSidebar: "साइडबार खोलें",
|
openSidebar: "साइडबार खोलें",
|
||||||
closeSidebar: "साइडबार बंद करें",
|
closeSidebar: "साइडबार बंद करें",
|
||||||
|
|||||||
@@ -3844,6 +3844,7 @@ export const id: TranslationKeys = {
|
|||||||
connectionError: "Kesalahan koneksi",
|
connectionError: "Kesalahan koneksi",
|
||||||
},
|
},
|
||||||
sidebar: {
|
sidebar: {
|
||||||
|
sponsor: "Jaga tetap gratis",
|
||||||
tools: "Alat",
|
tools: "Alat",
|
||||||
grid: "Kisi",
|
grid: "Kisi",
|
||||||
automate: "Otomasi",
|
automate: "Otomasi",
|
||||||
@@ -3863,6 +3864,7 @@ export const id: TranslationKeys = {
|
|||||||
mobileNavSettings: "Pengaturan",
|
mobileNavSettings: "Pengaturan",
|
||||||
},
|
},
|
||||||
a11y: {
|
a11y: {
|
||||||
|
sponsorLink: "Dukung SnapOtter di GitHub Sponsors",
|
||||||
skipToContent: "Lewati ke konten utama",
|
skipToContent: "Lewati ke konten utama",
|
||||||
openSidebar: "Buka bilah sisi",
|
openSidebar: "Buka bilah sisi",
|
||||||
closeSidebar: "Tutup bilah sisi",
|
closeSidebar: "Tutup bilah sisi",
|
||||||
|
|||||||
@@ -3859,6 +3859,7 @@ export const it: TranslationKeys = {
|
|||||||
connectionError: "Errore di connessione",
|
connectionError: "Errore di connessione",
|
||||||
},
|
},
|
||||||
sidebar: {
|
sidebar: {
|
||||||
|
sponsor: "Mantienilo gratis",
|
||||||
tools: "Strumenti",
|
tools: "Strumenti",
|
||||||
grid: "Griglia",
|
grid: "Griglia",
|
||||||
automate: "Automatizza",
|
automate: "Automatizza",
|
||||||
@@ -3878,6 +3879,7 @@ export const it: TranslationKeys = {
|
|||||||
mobileNavSettings: "Impostazioni",
|
mobileNavSettings: "Impostazioni",
|
||||||
},
|
},
|
||||||
a11y: {
|
a11y: {
|
||||||
|
sponsorLink: "Sostieni SnapOtter su GitHub Sponsors",
|
||||||
skipToContent: "Vai al contenuto principale",
|
skipToContent: "Vai al contenuto principale",
|
||||||
openSidebar: "Apri barra laterale",
|
openSidebar: "Apri barra laterale",
|
||||||
closeSidebar: "Chiudi barra laterale",
|
closeSidebar: "Chiudi barra laterale",
|
||||||
|
|||||||
@@ -3795,6 +3795,7 @@ export const ja: TranslationKeys = {
|
|||||||
connectionError: "接続エラー",
|
connectionError: "接続エラー",
|
||||||
},
|
},
|
||||||
sidebar: {
|
sidebar: {
|
||||||
|
sponsor: "無料を守る",
|
||||||
tools: "ツール",
|
tools: "ツール",
|
||||||
grid: "グリッド",
|
grid: "グリッド",
|
||||||
automate: "自動化",
|
automate: "自動化",
|
||||||
@@ -3814,6 +3815,7 @@ export const ja: TranslationKeys = {
|
|||||||
mobileNavSettings: "設定",
|
mobileNavSettings: "設定",
|
||||||
},
|
},
|
||||||
a11y: {
|
a11y: {
|
||||||
|
sponsorLink: "GitHub Sponsors で SnapOtter を支援する",
|
||||||
skipToContent: "メインコンテンツへスキップ",
|
skipToContent: "メインコンテンツへスキップ",
|
||||||
openSidebar: "サイドバーを開く",
|
openSidebar: "サイドバーを開く",
|
||||||
closeSidebar: "サイドバーを閉じる",
|
closeSidebar: "サイドバーを閉じる",
|
||||||
|
|||||||
@@ -3776,6 +3776,7 @@ export const ko: TranslationKeys = {
|
|||||||
connectionError: "연결 오류",
|
connectionError: "연결 오류",
|
||||||
},
|
},
|
||||||
sidebar: {
|
sidebar: {
|
||||||
|
sponsor: "무료로 유지하기",
|
||||||
tools: "도구",
|
tools: "도구",
|
||||||
grid: "그리드",
|
grid: "그리드",
|
||||||
automate: "자동화",
|
automate: "자동화",
|
||||||
@@ -3795,6 +3796,7 @@ export const ko: TranslationKeys = {
|
|||||||
mobileNavSettings: "설정",
|
mobileNavSettings: "설정",
|
||||||
},
|
},
|
||||||
a11y: {
|
a11y: {
|
||||||
|
sponsorLink: "GitHub Sponsors에서 SnapOtter 후원하기",
|
||||||
skipToContent: "본문으로 건너뛰기",
|
skipToContent: "본문으로 건너뛰기",
|
||||||
openSidebar: "사이드바 열기",
|
openSidebar: "사이드바 열기",
|
||||||
closeSidebar: "사이드바 닫기",
|
closeSidebar: "사이드바 닫기",
|
||||||
|
|||||||
@@ -3853,6 +3853,7 @@ export const nl: TranslationKeys = {
|
|||||||
connectionError: "Verbindingsfout",
|
connectionError: "Verbindingsfout",
|
||||||
},
|
},
|
||||||
sidebar: {
|
sidebar: {
|
||||||
|
sponsor: "Houd het gratis",
|
||||||
tools: "Tools",
|
tools: "Tools",
|
||||||
grid: "Raster",
|
grid: "Raster",
|
||||||
automate: "Automatiseren",
|
automate: "Automatiseren",
|
||||||
@@ -3872,6 +3873,7 @@ export const nl: TranslationKeys = {
|
|||||||
mobileNavSettings: "Instellingen",
|
mobileNavSettings: "Instellingen",
|
||||||
},
|
},
|
||||||
a11y: {
|
a11y: {
|
||||||
|
sponsorLink: "Steun SnapOtter op GitHub Sponsors",
|
||||||
skipToContent: "Ga naar hoofdinhoud",
|
skipToContent: "Ga naar hoofdinhoud",
|
||||||
openSidebar: "Zijbalk openen",
|
openSidebar: "Zijbalk openen",
|
||||||
closeSidebar: "Zijbalk sluiten",
|
closeSidebar: "Zijbalk sluiten",
|
||||||
|
|||||||
@@ -3859,6 +3859,7 @@ export const pl: TranslationKeys = {
|
|||||||
connectionError: "Błąd połączenia",
|
connectionError: "Błąd połączenia",
|
||||||
},
|
},
|
||||||
sidebar: {
|
sidebar: {
|
||||||
|
sponsor: "Zachowaj darmowość",
|
||||||
tools: "Narzędzia",
|
tools: "Narzędzia",
|
||||||
grid: "Siatka",
|
grid: "Siatka",
|
||||||
automate: "Automatyzacja",
|
automate: "Automatyzacja",
|
||||||
@@ -3878,6 +3879,7 @@ export const pl: TranslationKeys = {
|
|||||||
mobileNavSettings: "Ustawienia",
|
mobileNavSettings: "Ustawienia",
|
||||||
},
|
},
|
||||||
a11y: {
|
a11y: {
|
||||||
|
sponsorLink: "Wesprzyj SnapOtter na GitHub Sponsors",
|
||||||
skipToContent: "Przejdź do treści",
|
skipToContent: "Przejdź do treści",
|
||||||
openSidebar: "Otwórz panel boczny",
|
openSidebar: "Otwórz panel boczny",
|
||||||
closeSidebar: "Zamknij panel boczny",
|
closeSidebar: "Zamknij panel boczny",
|
||||||
|
|||||||
@@ -3851,6 +3851,7 @@ export const ptBR: TranslationKeys = {
|
|||||||
connectionError: "Erro de conexão",
|
connectionError: "Erro de conexão",
|
||||||
},
|
},
|
||||||
sidebar: {
|
sidebar: {
|
||||||
|
sponsor: "Mantenha gratuito",
|
||||||
tools: "Ferramentas",
|
tools: "Ferramentas",
|
||||||
grid: "Grade",
|
grid: "Grade",
|
||||||
automate: "Automatizar",
|
automate: "Automatizar",
|
||||||
@@ -3870,6 +3871,7 @@ export const ptBR: TranslationKeys = {
|
|||||||
mobileNavSettings: "Configurações",
|
mobileNavSettings: "Configurações",
|
||||||
},
|
},
|
||||||
a11y: {
|
a11y: {
|
||||||
|
sponsorLink: "Apoie o SnapOtter no GitHub Sponsors",
|
||||||
skipToContent: "Pular para o conteúdo principal",
|
skipToContent: "Pular para o conteúdo principal",
|
||||||
openSidebar: "Abrir barra lateral",
|
openSidebar: "Abrir barra lateral",
|
||||||
closeSidebar: "Fechar barra lateral",
|
closeSidebar: "Fechar barra lateral",
|
||||||
|
|||||||
@@ -3847,6 +3847,7 @@ export const ru: TranslationKeys = {
|
|||||||
connectionError: "Ошибка подключения",
|
connectionError: "Ошибка подключения",
|
||||||
},
|
},
|
||||||
sidebar: {
|
sidebar: {
|
||||||
|
sponsor: "Пусть будет бесплатным",
|
||||||
tools: "Инструменты",
|
tools: "Инструменты",
|
||||||
grid: "Сетка",
|
grid: "Сетка",
|
||||||
automate: "Автоматизация",
|
automate: "Автоматизация",
|
||||||
@@ -3866,6 +3867,7 @@ export const ru: TranslationKeys = {
|
|||||||
mobileNavSettings: "Настройки",
|
mobileNavSettings: "Настройки",
|
||||||
},
|
},
|
||||||
a11y: {
|
a11y: {
|
||||||
|
sponsorLink: "Поддержать SnapOtter на GitHub Sponsors",
|
||||||
skipToContent: "Перейти к основному содержимому",
|
skipToContent: "Перейти к основному содержимому",
|
||||||
openSidebar: "Открыть боковую панель",
|
openSidebar: "Открыть боковую панель",
|
||||||
closeSidebar: "Закрыть боковую панель",
|
closeSidebar: "Закрыть боковую панель",
|
||||||
|
|||||||
@@ -3840,6 +3840,7 @@ export const sv: TranslationKeys = {
|
|||||||
connectionError: "Anslutningsfel",
|
connectionError: "Anslutningsfel",
|
||||||
},
|
},
|
||||||
sidebar: {
|
sidebar: {
|
||||||
|
sponsor: "Håll det gratis",
|
||||||
tools: "Verktyg",
|
tools: "Verktyg",
|
||||||
grid: "Rutnät",
|
grid: "Rutnät",
|
||||||
automate: "Automatisera",
|
automate: "Automatisera",
|
||||||
@@ -3859,6 +3860,7 @@ export const sv: TranslationKeys = {
|
|||||||
mobileNavSettings: "Inställningar",
|
mobileNavSettings: "Inställningar",
|
||||||
},
|
},
|
||||||
a11y: {
|
a11y: {
|
||||||
|
sponsorLink: "Stöd SnapOtter på GitHub Sponsors",
|
||||||
skipToContent: "Hoppa till huvudinnehåll",
|
skipToContent: "Hoppa till huvudinnehåll",
|
||||||
openSidebar: "Öppna sidopanel",
|
openSidebar: "Öppna sidopanel",
|
||||||
closeSidebar: "Stäng sidopanel",
|
closeSidebar: "Stäng sidopanel",
|
||||||
|
|||||||
@@ -3799,6 +3799,7 @@ export const th: TranslationKeys = {
|
|||||||
connectionError: "ข้อผิดพลาดในการเชื่อมต่อ",
|
connectionError: "ข้อผิดพลาดในการเชื่อมต่อ",
|
||||||
},
|
},
|
||||||
sidebar: {
|
sidebar: {
|
||||||
|
sponsor: "ให้ใช้ฟรีต่อไป",
|
||||||
tools: "เครื่องมือ",
|
tools: "เครื่องมือ",
|
||||||
grid: "กริด",
|
grid: "กริด",
|
||||||
automate: "อัตโนมัติ",
|
automate: "อัตโนมัติ",
|
||||||
@@ -3818,6 +3819,7 @@ export const th: TranslationKeys = {
|
|||||||
mobileNavSettings: "ตั้งค่า",
|
mobileNavSettings: "ตั้งค่า",
|
||||||
},
|
},
|
||||||
a11y: {
|
a11y: {
|
||||||
|
sponsorLink: "สนับสนุน SnapOtter บน GitHub Sponsors",
|
||||||
skipToContent: "ข้ามไปยังเนื้อหาหลัก",
|
skipToContent: "ข้ามไปยังเนื้อหาหลัก",
|
||||||
openSidebar: "เปิดแถบด้านข้าง",
|
openSidebar: "เปิดแถบด้านข้าง",
|
||||||
closeSidebar: "ปิดแถบด้านข้าง",
|
closeSidebar: "ปิดแถบด้านข้าง",
|
||||||
|
|||||||
@@ -3851,6 +3851,7 @@ export const tr: TranslationKeys = {
|
|||||||
connectionError: "Bağlantı hatası",
|
connectionError: "Bağlantı hatası",
|
||||||
},
|
},
|
||||||
sidebar: {
|
sidebar: {
|
||||||
|
sponsor: "Ücretsiz kalsın",
|
||||||
tools: "Araçlar",
|
tools: "Araçlar",
|
||||||
grid: "Izgara",
|
grid: "Izgara",
|
||||||
automate: "Otomatikleştir",
|
automate: "Otomatikleştir",
|
||||||
@@ -3870,6 +3871,7 @@ export const tr: TranslationKeys = {
|
|||||||
mobileNavSettings: "Ayarlar",
|
mobileNavSettings: "Ayarlar",
|
||||||
},
|
},
|
||||||
a11y: {
|
a11y: {
|
||||||
|
sponsorLink: "SnapOtter'ı GitHub Sponsors'ta destekleyin",
|
||||||
skipToContent: "Ana içeriğe geç",
|
skipToContent: "Ana içeriğe geç",
|
||||||
openSidebar: "Kenar çubuğunu aç",
|
openSidebar: "Kenar çubuğunu aç",
|
||||||
closeSidebar: "Kenar çubuğunu kapat",
|
closeSidebar: "Kenar çubuğunu kapat",
|
||||||
|
|||||||
@@ -3848,6 +3848,7 @@ export const uk: TranslationKeys = {
|
|||||||
connectionError: "Помилка з'єднання",
|
connectionError: "Помилка з'єднання",
|
||||||
},
|
},
|
||||||
sidebar: {
|
sidebar: {
|
||||||
|
sponsor: "Нехай буде безкоштовним",
|
||||||
tools: "Інструменти",
|
tools: "Інструменти",
|
||||||
grid: "Сітка",
|
grid: "Сітка",
|
||||||
automate: "Автоматизація",
|
automate: "Автоматизація",
|
||||||
@@ -3867,6 +3868,7 @@ export const uk: TranslationKeys = {
|
|||||||
mobileNavSettings: "Налаштування",
|
mobileNavSettings: "Налаштування",
|
||||||
},
|
},
|
||||||
a11y: {
|
a11y: {
|
||||||
|
sponsorLink: "Підтримати SnapOtter на GitHub Sponsors",
|
||||||
skipToContent: "Перейти до основного вмісту",
|
skipToContent: "Перейти до основного вмісту",
|
||||||
openSidebar: "Відкрити бічну панель",
|
openSidebar: "Відкрити бічну панель",
|
||||||
closeSidebar: "Закрити бічну панель",
|
closeSidebar: "Закрити бічну панель",
|
||||||
|
|||||||
@@ -3840,6 +3840,7 @@ export const vi: TranslationKeys = {
|
|||||||
connectionError: "Lỗi kết nối",
|
connectionError: "Lỗi kết nối",
|
||||||
},
|
},
|
||||||
sidebar: {
|
sidebar: {
|
||||||
|
sponsor: "Giữ nó miễn phí",
|
||||||
tools: "Công cụ",
|
tools: "Công cụ",
|
||||||
grid: "Lưới",
|
grid: "Lưới",
|
||||||
automate: "Tự động hóa",
|
automate: "Tự động hóa",
|
||||||
@@ -3859,6 +3860,7 @@ export const vi: TranslationKeys = {
|
|||||||
mobileNavSettings: "Cài đặt",
|
mobileNavSettings: "Cài đặt",
|
||||||
},
|
},
|
||||||
a11y: {
|
a11y: {
|
||||||
|
sponsorLink: "Ủng hộ SnapOtter trên GitHub Sponsors",
|
||||||
skipToContent: "Chuyển đến nội dung chính",
|
skipToContent: "Chuyển đến nội dung chính",
|
||||||
openSidebar: "Mở thanh bên",
|
openSidebar: "Mở thanh bên",
|
||||||
closeSidebar: "Đóng thanh bên",
|
closeSidebar: "Đóng thanh bên",
|
||||||
|
|||||||
@@ -3583,6 +3583,7 @@ export const zhCN: TranslationKeys = {
|
|||||||
connectionError: "连接错误",
|
connectionError: "连接错误",
|
||||||
},
|
},
|
||||||
sidebar: {
|
sidebar: {
|
||||||
|
sponsor: "保持免费",
|
||||||
tools: "工具",
|
tools: "工具",
|
||||||
grid: "网格",
|
grid: "网格",
|
||||||
automate: "自动化",
|
automate: "自动化",
|
||||||
@@ -3602,6 +3603,7 @@ export const zhCN: TranslationKeys = {
|
|||||||
mobileNavSettings: "设置",
|
mobileNavSettings: "设置",
|
||||||
},
|
},
|
||||||
a11y: {
|
a11y: {
|
||||||
|
sponsorLink: "在 GitHub Sponsors 上支持 SnapOtter",
|
||||||
skipToContent: "跳到主要内容",
|
skipToContent: "跳到主要内容",
|
||||||
openSidebar: "打开侧边栏",
|
openSidebar: "打开侧边栏",
|
||||||
closeSidebar: "关闭侧边栏",
|
closeSidebar: "关闭侧边栏",
|
||||||
|
|||||||
@@ -3583,6 +3583,7 @@ export const zhTW: TranslationKeys = {
|
|||||||
connectionError: "連線錯誤",
|
connectionError: "連線錯誤",
|
||||||
},
|
},
|
||||||
sidebar: {
|
sidebar: {
|
||||||
|
sponsor: "保持免費",
|
||||||
tools: "工具",
|
tools: "工具",
|
||||||
grid: "格線",
|
grid: "格線",
|
||||||
automate: "自動化",
|
automate: "自動化",
|
||||||
@@ -3602,6 +3603,7 @@ export const zhTW: TranslationKeys = {
|
|||||||
mobileNavSettings: "設定",
|
mobileNavSettings: "設定",
|
||||||
},
|
},
|
||||||
a11y: {
|
a11y: {
|
||||||
|
sponsorLink: "在 GitHub Sponsors 上支持 SnapOtter",
|
||||||
skipToContent: "跳至主要內容",
|
skipToContent: "跳至主要內容",
|
||||||
openSidebar: "開啟側邊欄",
|
openSidebar: "開啟側邊欄",
|
||||||
closeSidebar: "關閉側邊欄",
|
closeSidebar: "關閉側邊欄",
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { expect, test } from "./helpers";
|
||||||
|
|
||||||
|
const SPONSOR_URL = "https://github.com/sponsors/snapotter-hq";
|
||||||
|
|
||||||
|
test.describe("Sponsor button", () => {
|
||||||
|
// Pin a desktop viewport so the full pill (with visible text) renders,
|
||||||
|
// not the mobile icon-only form.
|
||||||
|
test.use({ viewport: { width: 1280, height: 720 } });
|
||||||
|
|
||||||
|
test("top nav links to GitHub Sponsors with safe rel", async ({ page }) => {
|
||||||
|
await page.goto("/");
|
||||||
|
|
||||||
|
const link = page.locator(`a[href="${SPONSOR_URL}"]`);
|
||||||
|
await expect(link).toBeVisible();
|
||||||
|
await expect(link).toHaveAttribute("target", "_blank");
|
||||||
|
await expect(link).toHaveAttribute("rel", "noopener noreferrer");
|
||||||
|
await expect(link).toContainText(/keep it free/i);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -2,8 +2,8 @@ import { ANALYTICS_EVENTS } from "@snapotter/shared";
|
|||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
describe("ANALYTICS_EVENTS", () => {
|
describe("ANALYTICS_EVENTS", () => {
|
||||||
it("has exactly 13 event keys", () => {
|
it("has exactly 14 event keys", () => {
|
||||||
expect(Object.keys(ANALYTICS_EVENTS)).toHaveLength(13);
|
expect(Object.keys(ANALYTICS_EVENTS)).toHaveLength(14);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("contains the expected keys", () => {
|
it("contains the expected keys", () => {
|
||||||
@@ -20,6 +20,7 @@ describe("ANALYTICS_EVENTS", () => {
|
|||||||
expect(ANALYTICS_EVENTS).toHaveProperty("AI_BUNDLE_PROMPTED");
|
expect(ANALYTICS_EVENTS).toHaveProperty("AI_BUNDLE_PROMPTED");
|
||||||
expect(ANALYTICS_EVENTS).toHaveProperty("BATCH_PROCESSED");
|
expect(ANALYTICS_EVENTS).toHaveProperty("BATCH_PROCESSED");
|
||||||
expect(ANALYTICS_EVENTS).toHaveProperty("FEEDBACK_SUBMITTED");
|
expect(ANALYTICS_EVENTS).toHaveProperty("FEEDBACK_SUBMITTED");
|
||||||
|
expect(ANALYTICS_EVENTS).toHaveProperty("SPONSOR_CLICKED");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("all event values are strings", () => {
|
it("all event values are strings", () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user