feat: redesign pricing section with beta notice and i18n pricing
- 4-card layout: Free / Monthly / Quarterly / Lifetime License - zh shows RMB ¥, en shows $ with localized prices - Monthly ¥19.9 (edu ¥12.9 / $1.99), Quarterly ¥49.9 (edu ¥29.9 / $7.99) - Diagonal corner ribbons (green "限时免费") on Monthly & Quarterly - Desktop card renamed to "永久授权 / Lifetime License" with Coming Soon ribbon - Desktop tag badge distinguishes it as a native offline app - Beta notice banner: all plans free during beta, no payment required - CTA unified to "免费体验 / Try Free" linking to /app - Cards use flex-column + align-items:stretch for consistent equal height Made-with: Cursor
This commit is contained in:
BIN
public/demo/preview-chinese.png
Normal file
BIN
public/demo/preview-chinese.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.3 MiB |
BIN
public/demo/preview-english.png
Normal file
BIN
public/demo/preview-english.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.7 MiB |
BIN
public/demo/preview-formula.png
Normal file
BIN
public/demo/preview-formula.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
@@ -70,144 +70,287 @@ export default function AuthModal({ onClose, mandatory = false }: AuthModalProps
|
||||
await beginGoogleOAuth();
|
||||
};
|
||||
|
||||
const s = {
|
||||
overlay: {
|
||||
position: 'fixed' as const,
|
||||
inset: 0,
|
||||
background: 'rgba(31,26,23,0.45)',
|
||||
backdropFilter: 'blur(4px)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
zIndex: 1000,
|
||||
padding: '16px',
|
||||
},
|
||||
card: {
|
||||
background: '#FFFDF9',
|
||||
borderRadius: '24px',
|
||||
boxShadow: '0 24px 64px rgba(198,134,85,0.18)',
|
||||
maxWidth: '420px',
|
||||
width: '100%',
|
||||
padding: '32px',
|
||||
fontFamily: "'DM Sans', sans-serif",
|
||||
border: '1px solid #F1E6D8',
|
||||
},
|
||||
header: {
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
marginBottom: '24px',
|
||||
},
|
||||
title: {
|
||||
fontSize: '22px',
|
||||
fontWeight: 700,
|
||||
color: '#1F1A17',
|
||||
fontFamily: "'Lora', serif",
|
||||
},
|
||||
closeBtn: {
|
||||
width: '32px',
|
||||
height: '32px',
|
||||
borderRadius: '50%',
|
||||
border: '1px solid #F1E6D8',
|
||||
background: 'transparent',
|
||||
cursor: 'pointer',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
color: '#AA9685',
|
||||
transition: 'all 0.15s',
|
||||
},
|
||||
tabs: {
|
||||
display: 'grid',
|
||||
gridTemplateColumns: '1fr 1fr',
|
||||
gap: '4px',
|
||||
background: '#F5DDD0',
|
||||
borderRadius: '14px',
|
||||
padding: '4px',
|
||||
marginBottom: '24px',
|
||||
},
|
||||
tabActive: {
|
||||
padding: '8px 12px',
|
||||
borderRadius: '11px',
|
||||
border: 'none',
|
||||
background: '#FFFDF9',
|
||||
color: '#C8622A',
|
||||
fontWeight: 600,
|
||||
fontSize: '14px',
|
||||
cursor: 'pointer',
|
||||
fontFamily: "'DM Sans', sans-serif",
|
||||
boxShadow: '0 1px 4px rgba(200,98,42,0.12)',
|
||||
transition: 'all 0.15s',
|
||||
},
|
||||
tabInactive: {
|
||||
padding: '8px 12px',
|
||||
borderRadius: '11px',
|
||||
border: 'none',
|
||||
background: 'transparent',
|
||||
color: '#6F6257',
|
||||
fontWeight: 500,
|
||||
fontSize: '14px',
|
||||
cursor: 'pointer',
|
||||
fontFamily: "'DM Sans', sans-serif",
|
||||
transition: 'all 0.15s',
|
||||
},
|
||||
fieldGroup: {
|
||||
marginBottom: '16px',
|
||||
},
|
||||
label: {
|
||||
display: 'block',
|
||||
fontSize: '13px',
|
||||
fontWeight: 600,
|
||||
color: '#6F6257',
|
||||
marginBottom: '6px',
|
||||
letterSpacing: '0.02em',
|
||||
},
|
||||
input: {
|
||||
width: '100%',
|
||||
padding: '10px 14px',
|
||||
border: '1.5px solid #F1E6D8',
|
||||
borderRadius: '12px',
|
||||
fontSize: '15px',
|
||||
color: '#1F1A17',
|
||||
background: '#FFFFFF',
|
||||
outline: 'none',
|
||||
fontFamily: "'DM Sans', sans-serif",
|
||||
transition: 'border-color 0.15s',
|
||||
},
|
||||
inputError: {
|
||||
border: '1.5px solid #d97a6a',
|
||||
},
|
||||
fieldError: {
|
||||
marginTop: '4px',
|
||||
fontSize: '12px',
|
||||
color: '#c0503c',
|
||||
},
|
||||
fieldHint: {
|
||||
marginTop: '4px',
|
||||
fontSize: '12px',
|
||||
color: '#AA9685',
|
||||
},
|
||||
errorBox: {
|
||||
padding: '10px 14px',
|
||||
background: '#fff0ed',
|
||||
border: '1px solid #f5c0b0',
|
||||
color: '#c0503c',
|
||||
borderRadius: '12px',
|
||||
fontSize: '13px',
|
||||
fontWeight: 500,
|
||||
marginBottom: '16px',
|
||||
},
|
||||
submitBtn: {
|
||||
width: '100%',
|
||||
height: '48px',
|
||||
background: '#C8622A',
|
||||
color: 'white',
|
||||
border: 'none',
|
||||
borderRadius: '14px',
|
||||
fontSize: '15px',
|
||||
fontWeight: 600,
|
||||
fontFamily: "'DM Sans', sans-serif",
|
||||
cursor: 'pointer',
|
||||
boxShadow: '0 2px 12px rgba(200,98,42,0.28)',
|
||||
transition: 'all 0.2s',
|
||||
marginBottom: '16px',
|
||||
},
|
||||
divider: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '12px',
|
||||
marginBottom: '16px',
|
||||
},
|
||||
dividerLine: {
|
||||
flex: 1,
|
||||
height: '1px',
|
||||
background: '#F1E6D8',
|
||||
},
|
||||
dividerText: {
|
||||
fontSize: '12px',
|
||||
color: '#AA9685',
|
||||
letterSpacing: '0.05em',
|
||||
},
|
||||
googleBtn: {
|
||||
width: '100%',
|
||||
height: '48px',
|
||||
border: '1.5px solid #F1E6D8',
|
||||
borderRadius: '14px',
|
||||
background: '#FFFFFF',
|
||||
color: '#1F1A17',
|
||||
fontSize: '15px',
|
||||
fontWeight: 500,
|
||||
fontFamily: "'DM Sans', sans-serif",
|
||||
cursor: 'pointer',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
gap: '10px',
|
||||
transition: 'all 0.2s',
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4">
|
||||
<div className="bg-white rounded-xl shadow-xl max-w-md w-full p-6">
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
<h2 className="text-2xl font-bold text-gray-900">
|
||||
<div style={s.overlay}>
|
||||
<div style={s.card}>
|
||||
<div style={s.header}>
|
||||
<h2 style={s.title}>
|
||||
{mode === 'signup' ? t.auth.signUpTitle : t.auth.signInTitle}
|
||||
</h2>
|
||||
{!mandatory && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
className="p-2 hover:bg-gray-100 rounded-lg transition-colors"
|
||||
style={s.closeBtn}
|
||||
aria-label="close"
|
||||
disabled={isBusy}
|
||||
>
|
||||
<X size={20} />
|
||||
<X size={15} />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-2 rounded-lg bg-gray-100 p-1 mb-4">
|
||||
<div style={s.tabs}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setMode('signin');
|
||||
setFieldErrors({});
|
||||
setLocalError('');
|
||||
}}
|
||||
onClick={() => { setMode('signin'); setFieldErrors({}); setLocalError(''); }}
|
||||
aria-pressed={mode === 'signin'}
|
||||
disabled={isBusy}
|
||||
className={`rounded-md px-3 py-2 text-sm font-medium transition-colors ${
|
||||
mode === 'signin' ? 'bg-white text-gray-900 shadow-sm' : 'text-gray-600 hover:text-gray-900'
|
||||
}`}
|
||||
style={mode === 'signin' ? s.tabActive : s.tabInactive}
|
||||
>
|
||||
{t.auth.signIn}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setMode('signup');
|
||||
setFieldErrors({});
|
||||
setLocalError('');
|
||||
}}
|
||||
onClick={() => { setMode('signup'); setFieldErrors({}); setLocalError(''); }}
|
||||
aria-pressed={mode === 'signup'}
|
||||
disabled={isBusy}
|
||||
className={`rounded-md px-3 py-2 text-sm font-medium transition-colors ${
|
||||
mode === 'signup' ? 'bg-white text-gray-900 shadow-sm' : 'text-gray-600 hover:text-gray-900'
|
||||
}`}
|
||||
style={mode === 'signup' ? s.tabActive : s.tabInactive}
|
||||
>
|
||||
{t.auth.signUp}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit} noValidate className="space-y-4">
|
||||
<div>
|
||||
<label htmlFor="auth-email" className="block text-sm font-medium text-gray-700 mb-1">
|
||||
{t.auth.email}
|
||||
</label>
|
||||
<form onSubmit={handleSubmit} noValidate>
|
||||
<div style={s.fieldGroup}>
|
||||
<label htmlFor="auth-email" style={s.label}>{t.auth.email}</label>
|
||||
<input
|
||||
id="auth-email"
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={(e) => {
|
||||
setEmail(e.target.value);
|
||||
if (fieldErrors.email) {
|
||||
setFieldErrors((prev) => ({ ...prev, email: undefined }));
|
||||
}
|
||||
if (fieldErrors.email) setFieldErrors((prev) => ({ ...prev, email: undefined }));
|
||||
}}
|
||||
className={`w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent ${
|
||||
fieldErrors.email ? 'border-red-400' : 'border-gray-300'
|
||||
}`}
|
||||
style={fieldErrors.email ? { ...s.input, ...s.inputError } : s.input}
|
||||
placeholder="your@email.com"
|
||||
required
|
||||
disabled={isBusy}
|
||||
/>
|
||||
{fieldErrors.email && <p className="mt-1 text-xs text-red-600">{fieldErrors.email}</p>}
|
||||
{mode === 'signup' && (
|
||||
<p className="mt-1 text-xs text-gray-500">{t.auth.emailHint}</p>
|
||||
)}
|
||||
{fieldErrors.email && <p style={s.fieldError}>{fieldErrors.email}</p>}
|
||||
{mode === 'signup' && <p style={s.fieldHint}>{t.auth.emailHint}</p>}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label htmlFor="auth-password" className="block text-sm font-medium text-gray-700 mb-1">
|
||||
{t.auth.password}
|
||||
</label>
|
||||
<div style={s.fieldGroup}>
|
||||
<label htmlFor="auth-password" style={s.label}>{t.auth.password}</label>
|
||||
<input
|
||||
id="auth-password"
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={(e) => {
|
||||
setPassword(e.target.value);
|
||||
if (fieldErrors.password) {
|
||||
setFieldErrors((prev) => ({ ...prev, password: undefined }));
|
||||
}
|
||||
if (fieldErrors.password) setFieldErrors((prev) => ({ ...prev, password: undefined }));
|
||||
}}
|
||||
className={`w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent ${
|
||||
fieldErrors.password ? 'border-red-400' : 'border-gray-300'
|
||||
}`}
|
||||
style={fieldErrors.password ? { ...s.input, ...s.inputError } : s.input}
|
||||
placeholder="••••••••"
|
||||
required
|
||||
minLength={6}
|
||||
disabled={isBusy}
|
||||
/>
|
||||
{fieldErrors.password && <p className="mt-1 text-xs text-red-600">{fieldErrors.password}</p>}
|
||||
{mode === 'signup' && (
|
||||
<p className="mt-1 text-xs text-gray-500">{t.auth.passwordHint}</p>
|
||||
)}
|
||||
{fieldErrors.password && <p style={s.fieldError}>{fieldErrors.password}</p>}
|
||||
{mode === 'signup' && <p style={s.fieldHint}>{t.auth.passwordHint}</p>}
|
||||
</div>
|
||||
|
||||
{mode === 'signup' && (
|
||||
<div>
|
||||
<label htmlFor="auth-password-confirm" className="block text-sm font-medium text-gray-700 mb-1">
|
||||
{t.auth.confirmPassword}
|
||||
</label>
|
||||
<div style={s.fieldGroup}>
|
||||
<label htmlFor="auth-password-confirm" style={s.label}>{t.auth.confirmPassword}</label>
|
||||
<input
|
||||
id="auth-password-confirm"
|
||||
type="password"
|
||||
value={confirmPassword}
|
||||
onChange={(e) => {
|
||||
setConfirmPassword(e.target.value);
|
||||
if (fieldErrors.confirmPassword) {
|
||||
setFieldErrors((prev) => ({ ...prev, confirmPassword: undefined }));
|
||||
}
|
||||
if (fieldErrors.confirmPassword) setFieldErrors((prev) => ({ ...prev, confirmPassword: undefined }));
|
||||
}}
|
||||
className={`w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent ${
|
||||
fieldErrors.confirmPassword ? 'border-red-400' : 'border-gray-300'
|
||||
}`}
|
||||
style={fieldErrors.confirmPassword ? { ...s.input, ...s.inputError } : s.input}
|
||||
placeholder="••••••••"
|
||||
required
|
||||
minLength={6}
|
||||
disabled={isBusy}
|
||||
/>
|
||||
{fieldErrors.confirmPassword && <p className="mt-1 text-xs text-red-600">{fieldErrors.confirmPassword}</p>}
|
||||
{fieldErrors.confirmPassword && <p style={s.fieldError}>{fieldErrors.confirmPassword}</p>}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{(localError || authError) && (
|
||||
<div className="p-3 bg-red-100 border border-red-300 text-red-700 rounded-lg text-sm font-medium">
|
||||
<div style={s.errorBox}>
|
||||
{t.auth.error}: {localError || authError}
|
||||
</div>
|
||||
)}
|
||||
@@ -215,31 +358,28 @@ export default function AuthModal({ onClose, mandatory = false }: AuthModalProps
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isBusy}
|
||||
className="w-full py-3 px-4 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors font-medium disabled:opacity-80 disabled:cursor-wait"
|
||||
style={{ ...s.submitBtn, opacity: isBusy ? 0.75 : 1, cursor: isBusy ? 'wait' : 'pointer' }}
|
||||
>
|
||||
{submitText}
|
||||
</button>
|
||||
|
||||
<div className="relative py-1">
|
||||
<div className="absolute inset-0 flex items-center" aria-hidden="true">
|
||||
<div className="w-full border-t border-gray-200" />
|
||||
</div>
|
||||
<div className="relative flex justify-center text-xs">
|
||||
<span className="bg-white px-2 text-gray-400">OR</span>
|
||||
</div>
|
||||
<div style={s.divider}>
|
||||
<div style={s.dividerLine} />
|
||||
<span style={s.dividerText}>OR</span>
|
||||
<div style={s.dividerLine} />
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleGoogleOAuth}
|
||||
disabled={isBusy}
|
||||
className="w-full py-3 px-4 border border-gray-300 text-gray-800 rounded-lg hover:bg-gray-50 transition-colors font-medium disabled:opacity-80 disabled:cursor-wait inline-flex items-center justify-center gap-2"
|
||||
style={{ ...s.googleBtn, opacity: isBusy ? 0.75 : 1, cursor: isBusy ? 'wait' : 'pointer' }}
|
||||
>
|
||||
<img
|
||||
src="https://upload.wikimedia.org/wikipedia/commons/7/7e/Gmail_icon_%282020%29.svg"
|
||||
alt=""
|
||||
aria-hidden="true"
|
||||
className="w-[18px] h-[18px]"
|
||||
style={{ width: '18px', height: '18px' }}
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
/>
|
||||
|
||||
@@ -1,29 +1,43 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useLanguage } from '../../contexts/LanguageContext';
|
||||
|
||||
const LATEX_LINES = [
|
||||
'<span class="code-kw">\\frac</span>{-b \\pm <span class="code-kw">\\sqrt</span>{b² - 4ac}}{2a}',
|
||||
'<span class="code-kw">\\int</span>_0^1 x^2\\,dx',
|
||||
'<span class="code-kw">\\sum</span>_{i=<span class="code-num">1</span>}^n \\frac{1}{i^2}',
|
||||
const SLIDES = [
|
||||
{
|
||||
preview: '/demo/preview-formula.png',
|
||||
previewAlt: 'Quadratic formula',
|
||||
latex: '<span class="code-kw" style="color: black;">x = \\frac{- b \\pm \\sqrt{b^{2} - 4 a c}}{2 a}</span>',
|
||||
},
|
||||
{
|
||||
preview: '/demo/preview-chinese.png',
|
||||
previewAlt: 'Chinese text with integral formula',
|
||||
latex: '设函数 $f(x)$ 在 $[a,b]$ 上连续,则<br><span class="code-kw" style="color: black;">\\int_a^b f(x)\\dx</span>',
|
||||
},
|
||||
{
|
||||
preview: '/demo/preview-english.png',
|
||||
previewAlt: 'English text with Maxwell equations',
|
||||
latex: 'According to Maxwell\'s equations, the curl of the electric field is:<br><span class="code-kw" style="color: black;">\\nabla \\times \\mathbf{E} = - \\frac{\\partial \\mathbf{B}}{\\partial t}</span>',
|
||||
},
|
||||
];
|
||||
|
||||
export default function HeroSection() {
|
||||
const { language } = useLanguage();
|
||||
const codeRef = useRef<HTMLDivElement>(null);
|
||||
const [slideIdx, setSlideIdx] = useState(0);
|
||||
|
||||
// Typing cycling effect
|
||||
useEffect(() => {
|
||||
let idx = 0;
|
||||
const interval = setInterval(() => {
|
||||
idx = (idx + 1) % LATEX_LINES.length;
|
||||
if (codeRef.current) {
|
||||
codeRef.current.innerHTML = LATEX_LINES[idx] + '<span class="cursor-blink"></span>';
|
||||
}
|
||||
setSlideIdx((i) => (i + 1) % SLIDES.length);
|
||||
}, 3500);
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (codeRef.current) {
|
||||
codeRef.current.innerHTML = SLIDES[slideIdx].latex + '<span class="cursor-blink"></span>';
|
||||
}
|
||||
}, [slideIdx]);
|
||||
|
||||
return (
|
||||
<section className="hero">
|
||||
<div className="container">
|
||||
@@ -96,22 +110,20 @@ export default function HeroSection() {
|
||||
</div>
|
||||
</div>
|
||||
<div className="window-body">
|
||||
<div className="upload-zone">
|
||||
<div className="upload-icon-wrap">
|
||||
<svg viewBox="0 0 24 24">
|
||||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
|
||||
<polyline points="17 8 12 3 7 8" />
|
||||
<line x1="12" y1="3" x2="12" y2="15" />
|
||||
</svg>
|
||||
</div>
|
||||
<div className="upload-text">
|
||||
{language === 'zh' ? '点击、拖拽或粘贴文件开始解析' : 'Click, drag or paste a file to start'}
|
||||
</div>
|
||||
<div className="upload-sub">
|
||||
{language === 'zh' ? '支持 PNG · JPG · PDF · 手写截图' : 'PNG · JPG · PDF · Handwriting'}
|
||||
</div>
|
||||
|
||||
{/* Upload zone — shows the uploaded document preview */}
|
||||
<div className="upload-zone upload-zone-preview">
|
||||
{SLIDES.map((slide, i) => (
|
||||
<img
|
||||
key={slide.preview}
|
||||
src={slide.preview}
|
||||
alt={slide.previewAlt}
|
||||
className={`upload-preview-img${i === slideIdx ? ' upload-preview-active' : ''}`}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Output zone — LaTeX result */}
|
||||
<div className="output-zone">
|
||||
<div className="output-header">
|
||||
<span className="output-label">LaTeX Output</span>
|
||||
@@ -122,7 +134,7 @@ export default function HeroSection() {
|
||||
ref={codeRef}
|
||||
// eslint-disable-next-line react/no-danger
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: LATEX_LINES[0] + '<span class="cursor-blink"></span>',
|
||||
__html: SLIDES[0].latex + '<span class="cursor-blink"></span>',
|
||||
}}
|
||||
/>
|
||||
<div className="output-actions">
|
||||
@@ -143,6 +155,18 @@ export default function HeroSection() {
|
||||
<div className="fmt-tag">Word</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Slide indicator dots */}
|
||||
<div className="window-slide-dots">
|
||||
{SLIDES.map((_, i) => (
|
||||
<button
|
||||
key={i}
|
||||
className={`window-dot-btn${i === slideIdx ? ' window-dot-active' : ''}`}
|
||||
onClick={() => setSlideIdx(i)}
|
||||
aria-label={`Demo ${i + 1}`}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -14,68 +14,117 @@ export default function PricingSection() {
|
||||
{zh ? '选择适合你的方案' : 'Choose the plan that fits your workflow'}
|
||||
</h2>
|
||||
<p className="section-desc">
|
||||
{zh ? '从免费试用到永久桌面版,按需选择,无需绑定订阅。' : 'From free trial to lifetime desktop — pick what fits, no subscription lock-in.'}
|
||||
{zh
|
||||
? '从免费试用到永久授权,按需选择,无需绑定订阅。'
|
||||
: 'From free trial to lifetime license — pick what fits, no subscription lock-in.'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Beta notice */}
|
||||
<div className="pricing-beta-notice reveal">
|
||||
<span className="pricing-beta-icon">🎉</span>
|
||||
<span>
|
||||
{zh
|
||||
? '内测期间全部方案免费开放 · 无需绑定支付方式,注册即可解锁全部功能'
|
||||
: 'Free during beta · No payment required — sign up and unlock all features now'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="pricing-cards">
|
||||
|
||||
{/* Free */}
|
||||
<div className="pricing-card reveal reveal-delay-1">
|
||||
<div className="plan-name">Free</div>
|
||||
<div className="plan-price">$<span style={{ fontSize: '44px', color: 'var(--text-strong)' }}>0</span></div>
|
||||
<div className="plan-price">
|
||||
{zh ? <><span>¥</span>0</> : <><span>$</span>0</>}
|
||||
</div>
|
||||
<div className="plan-period">{zh ? '永久免费' : 'Forever free'}</div>
|
||||
<div className="plan-desc">For first-time use and quick screenshots.</div>
|
||||
<div className="plan-edu-price plan-edu-price-placeholder"> </div>
|
||||
<ul className="plan-features">
|
||||
<li>{zh ? '每月 30 次识别' : '30 recognitions / month'}</li>
|
||||
<li>LaTeX {zh ? '输出' : 'output'}</li>
|
||||
<li>Web App {zh ? '访问' : 'access'}</li>
|
||||
<li>{zh ? '基础公式支持' : 'Basic formula support'}</li>
|
||||
</ul>
|
||||
<Link to="/app" className="plan-btn">{zh ? '开始使用' : 'Get Started'}</Link>
|
||||
<Link to="/app" className="plan-btn">{zh ? '免费体验' : 'Try Free'}</Link>
|
||||
</div>
|
||||
|
||||
{/* Monthly */}
|
||||
<div className="pricing-card reveal reveal-delay-2">
|
||||
<div className="plan-name">Monthly</div>
|
||||
<div className="plan-price"><span>$</span>9</div>
|
||||
<div className="plan-period">{zh ? '每月 / 随时取消' : '/month · cancel anytime'}</div>
|
||||
<div className="plan-desc">Unlimited recognition for everyday study.</div>
|
||||
<div className="plan-ribbon plan-ribbon-free">
|
||||
{zh ? '限时免费' : 'Free Now'}
|
||||
</div>
|
||||
<div className="plan-name">{zh ? '月度会员' : 'Monthly'}</div>
|
||||
<div className="plan-price">
|
||||
{zh
|
||||
? <><span>RMB ¥</span>19.9</>
|
||||
: <><span>$</span>2.99</>}
|
||||
</div>
|
||||
<div className="plan-edu-price">
|
||||
{zh ? '教育优惠 ¥12.9 / 月' : 'Edu discount $1.99 / mo'}
|
||||
</div>
|
||||
<ul className="plan-features">
|
||||
<li>{zh ? '无限次识别' : 'Unlimited recognitions'}</li>
|
||||
<li>LaTeX + Markdown</li>
|
||||
<li>{zh ? 'Word 原生公式' : 'Native Word equations'}</li>
|
||||
<li>{zh ? '优先处理队列' : 'Priority queue'}</li>
|
||||
<li>{zh ? '每月 1,000 次请求' : '1,000 requests / month'}</li>
|
||||
<li>{zh ? '优先处理队列' : 'Priority processing'}</li>
|
||||
<li>{zh ? '插件使用权限' : 'Plugin access'}</li>
|
||||
</ul>
|
||||
<Link to="/app" className="plan-btn">{zh ? '开始使用' : 'Get Started'}</Link>
|
||||
<Link to="/app" className="plan-btn">{zh ? '免费体验' : 'Try Free'}</Link>
|
||||
</div>
|
||||
|
||||
<div className="pricing-card reveal reveal-delay-3">
|
||||
<div className="plan-name">Quarterly</div>
|
||||
<div className="plan-price"><span>$</span>24</div>
|
||||
<div className="plan-period">{zh ? '每季 · 省 $3/月' : '/quarter · save $3/mo'}</div>
|
||||
<div className="plan-desc">Best value for semester-long heavy usage.</div>
|
||||
{/* Quarterly — featured */}
|
||||
<div className="pricing-card featured reveal reveal-delay-3">
|
||||
<div className="featured-badge">{zh ? '最优惠' : 'Best Value'}</div>
|
||||
<div className="plan-ribbon plan-ribbon-free">
|
||||
{zh ? '限时免费' : 'Free Now'}
|
||||
</div>
|
||||
<div className="plan-name">{zh ? '季度会员' : 'Quarterly'}</div>
|
||||
<div className="plan-price">
|
||||
{zh
|
||||
? <><span>RMB ¥</span>49.9</>
|
||||
: <><span>$</span>9.9</>}
|
||||
</div>
|
||||
<div className="plan-edu-price">
|
||||
{zh ? '教育优惠 ¥29.9 / 季' : 'Edu discount $7.99 / quarter'}
|
||||
</div>
|
||||
<ul className="plan-features">
|
||||
<li>{zh ? '无限次识别' : 'Unlimited recognitions'}</li>
|
||||
<li>{zh ? '全格式输出' : 'All output formats'}</li>
|
||||
<li>{zh ? '批量 PDF 提取' : 'Batch PDF extraction'}</li>
|
||||
<li>{zh ? '每月 3,000 积分' : '3,000 credits / month'}</li>
|
||||
<li>{zh ? '优先处理队列' : 'Priority processing'}</li>
|
||||
<li>{zh ? '插件使用权限' : 'Plugin access'}</li>
|
||||
<li>API {zh ? '访问(Beta)' : 'access (Beta)'}</li>
|
||||
</ul>
|
||||
<Link to="/app" className="plan-btn">{zh ? '开始使用' : 'Get Started'}</Link>
|
||||
<Link to="/app" className="plan-btn featured-btn">{zh ? '免费体验' : 'Try Free'}</Link>
|
||||
</div>
|
||||
|
||||
<div className="pricing-card featured reveal">
|
||||
<div className="featured-badge">{zh ? '永久版' : 'Lifetime'}</div>
|
||||
<div className="plan-name">Desktop</div>
|
||||
<div className="plan-price"><span>$</span>79</div>
|
||||
<div className="plan-period">{zh ? '一次购买 · 终身使用' : 'one-time · lifetime access'}</div>
|
||||
<div className="plan-desc">Lifetime access for offline and sensitive files.</div>
|
||||
{/* Lifetime License — coming soon */}
|
||||
<div className="pricing-card pricing-card-desktop reveal">
|
||||
<div className="plan-ribbon plan-ribbon-soon">Coming Soon</div>
|
||||
<div className="plan-desktop-tag">
|
||||
<span>🖥</span>
|
||||
{zh ? '桌面应用 · 离线运行' : 'Desktop App · Offline'}
|
||||
</div>
|
||||
<div className="plan-name">{zh ? '永久授权' : 'Lifetime License'}</div>
|
||||
<div className="plan-price">
|
||||
{zh
|
||||
? <><span>RMB ¥</span>149</>
|
||||
: <><span>$</span>29.9</>}
|
||||
</div>
|
||||
<div className="plan-period">
|
||||
{zh ? '一次购买 · 终身使用' : 'one-time · lifetime access'}
|
||||
</div>
|
||||
<div className="plan-edu-price">
|
||||
{zh ? '教育优惠 ¥99' : 'Edu discount $24.9'}
|
||||
</div>
|
||||
<ul className="plan-features">
|
||||
<li>{zh ? '完全离线运行' : 'Fully offline'}</li>
|
||||
<li>{zh ? '无限次识别' : 'Unlimited recognitions'}</li>
|
||||
<li>{zh ? '批量 PDF 处理' : 'Batch PDF processing'}</li>
|
||||
<li>{zh ? '本地隐私保护' : 'Local privacy'}</li>
|
||||
<li>{zh ? '终身免费更新' : 'Lifetime free updates'}</li>
|
||||
</ul>
|
||||
<Link to="/app" className="plan-btn featured-btn">{zh ? '购买桌面版' : 'Buy Desktop'}</Link>
|
||||
<button className="plan-btn plan-btn-desktop-disabled" disabled>
|
||||
{zh ? '敬请期待' : 'Coming Soon'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -10,12 +10,12 @@ export default function ProductSuiteSection() {
|
||||
<div className="section-header reveal">
|
||||
<div className="eyebrow">Product Matrix</div>
|
||||
<h2 className="section-title">
|
||||
{language === 'zh' ? '覆盖所有公式工作流' : 'Built for every formula workflow'}
|
||||
{language === 'zh' ? '三端灵活,覆盖所有使用场景' : 'Three ways to use, every workflow covered'}
|
||||
</h2>
|
||||
<p className="section-desc">
|
||||
{language === 'zh'
|
||||
? '从浏览器即取即用,到扩展一键复制,再到桌面端离线处理——一个工具,覆盖学生写作业、研究者整理文献、工程师记录推导的全部场景。'
|
||||
: 'From instant browser use to one-click extension copy to offline desktop — one tool for students, researchers, and engineers.'}
|
||||
? '浏览器即开即用,扩展随手一键,桌面端离线无忧——学生轻松完成作业,教师高效备课制件,研究者快速整理文献,总有一种方式最适合你。'
|
||||
: 'Browser-ready, extension-powered, desktop offline — for students finishing homework, teachers preparing materials, and researchers organizing papers.'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -30,8 +30,8 @@ export default function ProductSuiteSection() {
|
||||
<div className="card-title">Web App</div>
|
||||
<div className="card-desc">
|
||||
{language === 'zh'
|
||||
? '浏览器内即时识别,无需安装。上传截图或手写图片,秒级输出 LaTeX。'
|
||||
: 'Instant recognition in browser, no install needed. Upload screenshot or handwriting, get LaTeX in seconds.'}
|
||||
? '无需安装,打开浏览器即可使用。上传截图或手写图片,秒级输出 LaTeX——学生写作业、教师课堂即时识别都毫不费力。'
|
||||
: 'No install needed. Upload a screenshot or handwriting, get LaTeX in seconds — perfect for students and teachers alike.'}
|
||||
</div>
|
||||
<Link to="/app" className="card-link">
|
||||
{language === 'zh' ? '浏览器即时识别 →' : 'Instant formula recognition in browser →'}
|
||||
@@ -49,8 +49,8 @@ export default function ProductSuiteSection() {
|
||||
<div className="card-title">Extension</div>
|
||||
<div className="card-desc">
|
||||
{language === 'zh'
|
||||
? '浏览器扩展,一键将 ChatGPT、Claude 输出的公式复制为 Word 原生数学公式。'
|
||||
: 'Browser extension — one-click copy of formulas from ChatGPT or Claude as native Word equations.'}
|
||||
? '一键将 ChatGPT、Claude 输出的公式转为 Word 原生数学公式。教师备课制件、学生写报告,不再手动排版。'
|
||||
: 'One-click copy of formulas from ChatGPT or Claude as native Word equations — ideal for teachers making slides and students writing reports.'}
|
||||
</div>
|
||||
<a href="#" className="card-link">
|
||||
{language === 'zh' ? '从 LLM 复制公式到 Word →' : 'Copy formulas from LLMs to Word →'}
|
||||
@@ -67,8 +67,8 @@ export default function ProductSuiteSection() {
|
||||
<div className="card-title">Desktop</div>
|
||||
<div className="card-desc">
|
||||
{language === 'zh'
|
||||
? '桌面端离线处理,适合论文批量提取与隐私保护场景,一次购买终身使用。'
|
||||
: 'Offline desktop app for batch PDF extraction and privacy-sensitive work. One-time purchase.'}
|
||||
? '本地离线运行,数据不出机。研究者批量提取论文公式、保护敏感数据的首选。一次购买,终身使用。'
|
||||
: 'Fully offline, data stays local. The go-to for researchers extracting formulas from papers at scale. One-time purchase.'}
|
||||
</div>
|
||||
<a href="#pricing" className="card-link">
|
||||
{language === 'zh' ? '查看桌面版定价 →' : 'See Desktop pricing →'}
|
||||
|
||||
@@ -46,19 +46,19 @@ export default function Footer() {
|
||||
<div>
|
||||
<div className="footer-col-title">{zh ? '公司' : 'Company'}</div>
|
||||
<ul className="footer-links">
|
||||
<li><a href="#">{zh ? '关于我们' : 'About'}</a></li>
|
||||
<li><a href="#pricing">{zh ? '价格' : 'Pricing'}</a></li>
|
||||
<li><Link to="/about">{zh ? '关于我们' : 'About'}</Link></li>
|
||||
<li><Link to="/#pricing">{zh ? '价格' : 'Pricing'}</Link></li>
|
||||
<li><Link to="/blog">{zh ? '博客' : 'Blog'}</Link></li>
|
||||
<li><a href="#">{zh ? '联系我们' : 'Contact'}</a></li>
|
||||
<li><Link to="/contact">{zh ? '联系我们' : 'Contact'}</Link></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className="footer-col-title">{zh ? '法律' : 'Legal'}</div>
|
||||
<ul className="footer-links">
|
||||
<li><a href="#">{zh ? '服务条款' : 'Terms of Service'}</a></li>
|
||||
<li><a href="#">{zh ? '隐私政策' : 'Privacy Policy'}</a></li>
|
||||
<li><a href="#">{zh ? 'Cookie 政策' : 'Cookie Policy'}</a></li>
|
||||
<li><Link to="/terms">{zh ? '服务条款' : 'Terms of Service'}</Link></li>
|
||||
<li><Link to="/privacy">{zh ? '隐私政策' : 'Privacy Policy'}</Link></li>
|
||||
<li><Link to="/cookies">{zh ? 'Cookie 政策' : 'Cookie Policy'}</Link></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useState, useEffect, useRef } from 'react';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import { useLanguage } from '../../contexts/LanguageContext';
|
||||
import { useAuth } from '../../contexts/AuthContext';
|
||||
import AuthModal from '../AuthModal';
|
||||
|
||||
export default function MarketingNavbar() {
|
||||
const { language, setLanguage } = useLanguage();
|
||||
@@ -11,6 +12,7 @@ export default function MarketingNavbar() {
|
||||
const [scrolled, setScrolled] = useState(false);
|
||||
const [activeSection, setActiveSection] = useState('');
|
||||
const [userMenuOpen, setUserMenuOpen] = useState(false);
|
||||
const [showAuthModal, setShowAuthModal] = useState(false);
|
||||
const userMenuRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// Scroll: sticky style + active nav section
|
||||
@@ -52,90 +54,94 @@ export default function MarketingNavbar() {
|
||||
: [];
|
||||
|
||||
return (
|
||||
<nav style={{ opacity: scrolled ? 1 : undefined }}>
|
||||
<div className="nav-inner">
|
||||
<Link to="/" className="nav-logo">
|
||||
<div className="logo-icon">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="2">
|
||||
<path d="M4 6h16M4 10h10M4 14h12M4 18h8" />
|
||||
</svg>
|
||||
</div>
|
||||
<span style={{ fontFamily: "'Lora', serif" }}>TexPixel</span>
|
||||
</Link>
|
||||
<>
|
||||
<nav style={{ opacity: scrolled ? 1 : undefined }}>
|
||||
<div className="nav-inner">
|
||||
<Link to="/" className="nav-logo">
|
||||
<div className="logo-icon">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="2">
|
||||
<path d="M4 6h16M4 10h10M4 14h12M4 18h8" />
|
||||
</svg>
|
||||
</div>
|
||||
<span style={{ fontFamily: "'Lora', serif" }}>TexPixel</span>
|
||||
</Link>
|
||||
|
||||
<ul className="nav-links">
|
||||
{navLinks.map((link) => (
|
||||
<li key={link.href}>
|
||||
<Link
|
||||
to={link.href}
|
||||
className={location.pathname === link.href ? 'active' : ''}
|
||||
>
|
||||
{link.label}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
{anchorLinks.map((link) => (
|
||||
<li key={link.href}>
|
||||
<a
|
||||
href={link.href}
|
||||
className={activeSection === link.href.slice(1) ? 'active' : ''}
|
||||
>
|
||||
{link.label}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<ul className="nav-links">
|
||||
{navLinks.map((link) => (
|
||||
<li key={link.href}>
|
||||
<Link
|
||||
to={link.href}
|
||||
className={location.pathname === link.href ? 'active' : ''}
|
||||
>
|
||||
{link.label}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
{anchorLinks.map((link) => (
|
||||
<li key={link.href}>
|
||||
<a
|
||||
href={link.href}
|
||||
className={activeSection === link.href.slice(1) ? 'active' : ''}
|
||||
>
|
||||
{link.label}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<div className="nav-right">
|
||||
<button
|
||||
className="lang-switch"
|
||||
onClick={() => setLanguage(language === 'zh' ? 'en' : 'zh')}
|
||||
>
|
||||
{language === 'zh' ? 'EN' : '中文'}
|
||||
</button>
|
||||
<div className="nav-right">
|
||||
<button
|
||||
className="lang-switch"
|
||||
onClick={() => setLanguage(language === 'zh' ? 'en' : 'zh')}
|
||||
>
|
||||
{language === 'zh' ? 'EN' : '中文'}
|
||||
</button>
|
||||
|
||||
{user ? (
|
||||
<div className="nav-user" ref={userMenuRef} style={{ position: 'relative' }}>
|
||||
<div
|
||||
className="nav-avatar"
|
||||
onClick={() => setUserMenuOpen((o) => !o)}
|
||||
style={{ cursor: 'pointer' }}
|
||||
>
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8">
|
||||
<circle cx="12" cy="8" r="4" />
|
||||
<path d="M4 20c0-4 3.6-7 8-7s8 3 8 7" />
|
||||
</svg>
|
||||
</div>
|
||||
{userMenuOpen && (
|
||||
<div className="nav-user-menu" style={{ display: 'block' }}>
|
||||
<div className="nav-menu-divider" />
|
||||
<Link to="/app" className="nav-menu-item" onClick={() => setUserMenuOpen(false)}>
|
||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8">
|
||||
<rect x="2" y="3" width="20" height="14" rx="2" />
|
||||
<path d="M8 21h8M12 17v4" />
|
||||
</svg>
|
||||
{language === 'zh' ? '启动应用' : 'Launch App'}
|
||||
</Link>
|
||||
<div className="nav-menu-divider" />
|
||||
<button
|
||||
className="nav-menu-item nav-menu-logout"
|
||||
onClick={() => { signOut(); setUserMenuOpen(false); }}
|
||||
style={{ background: 'none', border: 'none', width: '100%', textAlign: 'left', cursor: 'pointer' }}
|
||||
>
|
||||
{language === 'zh' ? '退出登录' : 'Sign Out'}
|
||||
</button>
|
||||
{user ? (
|
||||
<div className="nav-user" ref={userMenuRef} style={{ position: 'relative' }}>
|
||||
<div
|
||||
className="nav-avatar"
|
||||
onClick={() => setUserMenuOpen((o) => !o)}
|
||||
style={{ cursor: 'pointer' }}
|
||||
>
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8">
|
||||
<circle cx="12" cy="8" r="4" />
|
||||
<path d="M4 20c0-4 3.6-7 8-7s8 3 8 7" />
|
||||
</svg>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="nav-login-btn">
|
||||
<Link to="/app" className="btn-cta" style={{ display: 'inline-block', lineHeight: '52px', padding: '0 24px', textDecoration: 'none' }}>
|
||||
{language === 'zh' ? '免费试用' : 'Try Free'}
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
{userMenuOpen && (
|
||||
<div className="nav-user-menu" style={{ display: 'block' }}>
|
||||
<div className="nav-menu-divider" />
|
||||
<Link to="/app" className="nav-menu-item" onClick={() => setUserMenuOpen(false)}>
|
||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8">
|
||||
<rect x="2" y="3" width="20" height="14" rx="2" />
|
||||
<path d="M8 21h8M12 17v4" />
|
||||
</svg>
|
||||
{language === 'zh' ? '启动应用' : 'Launch App'}
|
||||
</Link>
|
||||
<div className="nav-menu-divider" />
|
||||
<button
|
||||
className="nav-menu-item nav-menu-logout"
|
||||
onClick={() => { signOut(); setUserMenuOpen(false); }}
|
||||
style={{ background: 'none', border: 'none', width: '100%', textAlign: 'left', cursor: 'pointer' }}
|
||||
>
|
||||
{language === 'zh' ? '退出登录' : 'Sign Out'}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="nav-login-btn">
|
||||
<button className="btn-cta" onClick={() => setShowAuthModal(true)}>
|
||||
{language === 'zh' ? '登录' : 'Login'}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</nav>
|
||||
|
||||
{showAuthModal && <AuthModal onClose={() => setShowAuthModal(false)} />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
168
src/pages/AboutPage.tsx
Normal file
168
src/pages/AboutPage.tsx
Normal file
@@ -0,0 +1,168 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useLanguage } from '../contexts/LanguageContext';
|
||||
import SEOHead from '../components/seo/SEOHead';
|
||||
|
||||
export default function AboutPage() {
|
||||
const { language } = useLanguage();
|
||||
const zh = language === 'zh';
|
||||
|
||||
return (
|
||||
<>
|
||||
<SEOHead
|
||||
title={zh ? '关于我们 — TexPixel' : 'About — TexPixel'}
|
||||
description={zh
|
||||
? 'TexPixel 致力于让每位学生和研究者都能轻松将数学公式转换为 LaTeX。'
|
||||
: 'TexPixel is on a mission to make math typesetting effortless for every student and researcher.'}
|
||||
path="/about"
|
||||
/>
|
||||
|
||||
<div className="docs-detail">
|
||||
<Link to="/" className="docs-back-link">
|
||||
<svg viewBox="0 0 24 24"><path d="M15 18l-6-6 6-6" /></svg>
|
||||
{zh ? '返回首页' : 'Back to home'}
|
||||
</Link>
|
||||
|
||||
<div className="docs-article-header">
|
||||
<div className="docs-article-tags">
|
||||
<span className="docs-tag">{zh ? '公司' : 'Company'}</span>
|
||||
</div>
|
||||
<h1 className="docs-article-h1">{zh ? '关于 TexPixel' : 'About TexPixel'}</h1>
|
||||
<div className="docs-meta-row">
|
||||
<span>{zh ? '让数学排版触手可及' : 'Making math typesetting effortless'}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="docs-prose">
|
||||
{zh ? <AboutZh /> : <AboutEn />}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function AboutEn() {
|
||||
return (
|
||||
<>
|
||||
<h2>Our Mission</h2>
|
||||
<p>
|
||||
TexPixel exists to eliminate the most tedious part of academic writing: transcribing handwritten or printed
|
||||
formulas into LaTeX. We believe that students, researchers, and educators should spend their time thinking
|
||||
about ideas — not fighting with syntax.
|
||||
</p>
|
||||
<p>
|
||||
A photograph of a chalkboard, a scan of a textbook, a snapshot of your own handwriting — TexPixel turns any
|
||||
of these into clean, copy-paste-ready LaTeX, Markdown, or Word equations in under a second.
|
||||
</p>
|
||||
|
||||
<h2>What We Build</h2>
|
||||
<p>
|
||||
Our core product is an AI-powered document recognition engine trained specifically on mathematical notation.
|
||||
Unlike general-purpose OCR tools, TexPixel understands the structure of formulas — fractions, integrals,
|
||||
summations, matrices, and multi-line expressions — and produces output that actually works the first time.
|
||||
</p>
|
||||
<p>We offer:</p>
|
||||
<ul>
|
||||
<li><strong>Web App</strong> — instant recognition in the browser, no installation required.</li>
|
||||
<li><strong>API (Beta)</strong> — integrate formula recognition directly into your tools and workflows.</li>
|
||||
<li><strong>Desktop App</strong> — fully offline processing for privacy-sensitive documents.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Who Uses TexPixel</h2>
|
||||
<p>
|
||||
Our users range from undergraduate students digitizing lecture notes to researchers processing thousands of
|
||||
equations from scanned papers. TexPixel is used in over 50 countries, with particular strength in China,
|
||||
the United States, Germany, Japan, and India.
|
||||
</p>
|
||||
|
||||
<h2>Our Principles</h2>
|
||||
<ul>
|
||||
<li><strong>Speed over friction.</strong> Every extra click is one too many. We optimize for the fastest possible path from image to output.</li>
|
||||
<li><strong>Accuracy where it matters.</strong> A wrong minus sign or missing exponent can invalidate an entire equation. We hold our models to a high bar.</li>
|
||||
<li><strong>Privacy by design.</strong> Your documents belong to you. We do not use uploaded content to train models without your explicit consent.</li>
|
||||
<li><strong>Accessible pricing.</strong> Students should not have to pay enterprise prices. Our free tier is generous, and our paid plans are priced for individuals.</li>
|
||||
</ul>
|
||||
|
||||
<h2>The Team</h2>
|
||||
<p>
|
||||
TexPixel is a small, focused team of engineers and researchers who care deeply about tools that make
|
||||
scientific writing easier. We are based in China, with contributors around the world.
|
||||
</p>
|
||||
<p>
|
||||
We're always looking for people who share our obsession with accuracy and clean user interfaces.
|
||||
If that sounds like you, reach out at <a href="mailto:hello@texpixel.com">hello@texpixel.com</a>.
|
||||
</p>
|
||||
|
||||
<h2>Get in Touch</h2>
|
||||
<p>
|
||||
For general inquiries: <a href="mailto:hello@texpixel.com">hello@texpixel.com</a><br />
|
||||
For support: <a href="mailto:support@texpixel.com">support@texpixel.com</a><br />
|
||||
For legal and privacy matters: <a href="mailto:legal@texpixel.com">legal@texpixel.com</a>
|
||||
</p>
|
||||
<p>
|
||||
<Link to="/contact">Send us a message →</Link>
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function AboutZh() {
|
||||
return (
|
||||
<>
|
||||
<h2>我们的使命</h2>
|
||||
<p>
|
||||
TexPixel 的存在,是为了消除学术写作中最繁琐的一环:将手写或印刷的公式转录为 LaTeX。
|
||||
我们相信,学生、研究者和教育工作者的时间应该用来思考问题,而不是与语法格式较劲。
|
||||
</p>
|
||||
<p>
|
||||
一张黑板照片、一份教材扫描件、一页你自己的手写笔记——TexPixel 能在一秒内将它们转换为
|
||||
可直接复制使用的 LaTeX、Markdown 或 Word 公式。
|
||||
</p>
|
||||
|
||||
<h2>我们做什么</h2>
|
||||
<p>
|
||||
我们的核心产品是一个专为数学符号训练的 AI 文档识别引擎。与通用 OCR 工具不同,
|
||||
TexPixel 能理解公式的结构——分数、积分、求和、矩阵、多行表达式——并生成一次就能用的输出。
|
||||
</p>
|
||||
<p>我们提供:</p>
|
||||
<ul>
|
||||
<li><strong>Web 应用</strong>——在浏览器中即时识别,无需安装。</li>
|
||||
<li><strong>API(Beta)</strong>——将公式识别直接集成到您的工具和工作流程中。</li>
|
||||
<li><strong>桌面版</strong>——完全离线处理,适合隐私敏感的文档场景。</li>
|
||||
</ul>
|
||||
|
||||
<h2>谁在使用 TexPixel</h2>
|
||||
<p>
|
||||
我们的用户从正在数字化课堂笔记的本科生,到处理大量扫描论文公式的科研人员,不一而足。
|
||||
TexPixel 已在全球 50 多个国家被广泛使用,在中国、美国、德国、日本和印度尤为普及。
|
||||
</p>
|
||||
|
||||
<h2>我们的原则</h2>
|
||||
<ul>
|
||||
<li><strong>速度优先,减少摩擦。</strong>每一次多余的点击都是累赘。我们追求从图片到输出的最短路径。</li>
|
||||
<li><strong>精度至上。</strong>一个错误的负号或漏掉的指数,可能让整个方程失效。我们对模型保持高标准。</li>
|
||||
<li><strong>隐私设计。</strong>您的文档属于您自己。未经您明确同意,我们不会使用上传内容训练模型。</li>
|
||||
<li><strong>亲民定价。</strong>学生不应为企业级服务付费。我们的免费额度足够慷慨,付费套餐也专为个人用户定价。</li>
|
||||
</ul>
|
||||
|
||||
<h2>我们的团队</h2>
|
||||
<p>
|
||||
TexPixel 是一支小而精的工程师与研究者团队,深度专注于让科学写作更便捷的工具。
|
||||
我们总部位于中国,贡献者遍布全球。
|
||||
</p>
|
||||
<p>
|
||||
我们始终欢迎同样痴迷于精准度与简洁交互设计的伙伴加入。
|
||||
如果这说的是你,请联系 <a href="mailto:hello@texpixel.com">hello@texpixel.com</a>。
|
||||
</p>
|
||||
|
||||
<h2>联系我们</h2>
|
||||
<p>
|
||||
综合咨询:<a href="mailto:hello@texpixel.com">hello@texpixel.com</a><br />
|
||||
技术支持:<a href="mailto:support@texpixel.com">support@texpixel.com</a><br />
|
||||
法律与隐私:<a href="mailto:legal@texpixel.com">legal@texpixel.com</a>
|
||||
</p>
|
||||
<p>
|
||||
<Link to="/contact">发送消息 →</Link>
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
185
src/pages/ContactPage.tsx
Normal file
185
src/pages/ContactPage.tsx
Normal file
@@ -0,0 +1,185 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useLanguage } from '../contexts/LanguageContext';
|
||||
import SEOHead from '../components/seo/SEOHead';
|
||||
|
||||
export default function ContactPage() {
|
||||
const { language } = useLanguage();
|
||||
const zh = language === 'zh';
|
||||
|
||||
return (
|
||||
<>
|
||||
<SEOHead
|
||||
title={zh ? '联系我们 — TexPixel' : 'Contact — TexPixel'}
|
||||
description={zh
|
||||
? '通过邮件联系 TexPixel 团队,获取支持、商务合作或反馈建议。'
|
||||
: 'Get in touch with the TexPixel team for support, partnerships, or feedback.'}
|
||||
path="/contact"
|
||||
/>
|
||||
|
||||
<div className="docs-detail">
|
||||
<Link to="/" className="docs-back-link">
|
||||
<svg viewBox="0 0 24 24"><path d="M15 18l-6-6 6-6" /></svg>
|
||||
{zh ? '返回首页' : 'Back to home'}
|
||||
</Link>
|
||||
|
||||
<div className="docs-article-header">
|
||||
<div className="docs-article-tags">
|
||||
<span className="docs-tag">{zh ? '公司' : 'Company'}</span>
|
||||
</div>
|
||||
<h1 className="docs-article-h1">{zh ? '联系我们' : 'Contact Us'}</h1>
|
||||
<div className="docs-meta-row">
|
||||
<span>{zh ? '我们通常在 1 个工作日内回复' : 'We typically reply within 1 business day'}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="docs-prose">
|
||||
{zh ? <ContactZh /> : <ContactEn />}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function ContactEn() {
|
||||
return (
|
||||
<>
|
||||
<p>
|
||||
Have a question, a bug to report, or an idea to share? We'd love to hear from you.
|
||||
Choose the right channel below and we'll get back to you as quickly as we can.
|
||||
</p>
|
||||
|
||||
<h2>Support</h2>
|
||||
<p>
|
||||
For issues with recognition quality, account problems, credit purchases, or any technical questions about
|
||||
the product:
|
||||
</p>
|
||||
<p>
|
||||
<a href="mailto:support@texpixel.com">support@texpixel.com</a>
|
||||
</p>
|
||||
<p>
|
||||
When writing in, please include:
|
||||
</p>
|
||||
<ul>
|
||||
<li>A brief description of the issue</li>
|
||||
<li>The task ID (shown in your history panel) if the issue is recognition-related</li>
|
||||
<li>Your browser and operating system (for web app issues)</li>
|
||||
</ul>
|
||||
|
||||
<h2>Billing & Credits</h2>
|
||||
<p>
|
||||
For questions about charges, credit balance discrepancies, or refund requests:
|
||||
</p>
|
||||
<p>
|
||||
<a href="mailto:billing@texpixel.com">billing@texpixel.com</a>
|
||||
</p>
|
||||
<p>
|
||||
Please include your account email and a description of the issue. For potential billing errors,
|
||||
include the transaction date and amount.
|
||||
</p>
|
||||
|
||||
<h2>Partnerships & API Access</h2>
|
||||
<p>
|
||||
Interested in integrating TexPixel into your platform, LMS, or research pipeline? Looking for volume
|
||||
pricing or a custom agreement?
|
||||
</p>
|
||||
<p>
|
||||
<a href="mailto:partnerships@texpixel.com">partnerships@texpixel.com</a>
|
||||
</p>
|
||||
|
||||
<h2>Legal & Privacy</h2>
|
||||
<p>
|
||||
For privacy requests (data access, deletion, correction), legal inquiries, or DMCA notices:
|
||||
</p>
|
||||
<p>
|
||||
<a href="mailto:legal@texpixel.com">legal@texpixel.com</a>
|
||||
</p>
|
||||
|
||||
<h2>General Inquiries</h2>
|
||||
<p>
|
||||
For everything else — press, feedback, or just saying hello:
|
||||
</p>
|
||||
<p>
|
||||
<a href="mailto:hello@texpixel.com">hello@texpixel.com</a>
|
||||
</p>
|
||||
|
||||
<h2>Response Times</h2>
|
||||
<p>
|
||||
We aim to respond to all inquiries within 1 business day (China Standard Time, UTC+8).
|
||||
During periods of high volume, support requests may take up to 3 business days.
|
||||
</p>
|
||||
<p>
|
||||
For self-service help, check our <Link to="/docs">documentation</Link> — most common questions are
|
||||
answered there.
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function ContactZh() {
|
||||
return (
|
||||
<>
|
||||
<p>
|
||||
有问题想反馈、发现了 Bug,或者有好的建议?我们很乐意听到您的声音。
|
||||
请根据需求选择对应的联系方式,我们会尽快回复。
|
||||
</p>
|
||||
|
||||
<h2>技术支持</h2>
|
||||
<p>
|
||||
如遇识别质量问题、账户故障、积分购买疑问或其他产品技术问题:
|
||||
</p>
|
||||
<p>
|
||||
<a href="mailto:support@texpixel.com">support@texpixel.com</a>
|
||||
</p>
|
||||
<p>来信时请提供以下信息,以便我们更快处理:</p>
|
||||
<ul>
|
||||
<li>问题的简要描述</li>
|
||||
<li>任务 ID(在历史记录面板中可见),如问题与识别结果相关</li>
|
||||
<li>浏览器名称和操作系统(针对 Web 应用问题)</li>
|
||||
</ul>
|
||||
|
||||
<h2>账单与积分</h2>
|
||||
<p>
|
||||
如有扣款疑问、积分余额异常或退款申请:
|
||||
</p>
|
||||
<p>
|
||||
<a href="mailto:billing@texpixel.com">billing@texpixel.com</a>
|
||||
</p>
|
||||
<p>
|
||||
请在邮件中注明您的账户邮箱及问题描述。若涉及账单错误,请提供交易日期和金额。
|
||||
</p>
|
||||
|
||||
<h2>合作与 API 接入</h2>
|
||||
<p>
|
||||
希望将 TexPixel 集成到您的平台、学习管理系统或科研流程中?寻求批量定价或定制合作?
|
||||
</p>
|
||||
<p>
|
||||
<a href="mailto:partnerships@texpixel.com">partnerships@texpixel.com</a>
|
||||
</p>
|
||||
|
||||
<h2>法律与隐私</h2>
|
||||
<p>
|
||||
隐私权利请求(数据访问、删除、更正)、法律咨询或 DMCA 版权通知:
|
||||
</p>
|
||||
<p>
|
||||
<a href="mailto:legal@texpixel.com">legal@texpixel.com</a>
|
||||
</p>
|
||||
|
||||
<h2>综合咨询</h2>
|
||||
<p>
|
||||
其他一切事宜——媒体采访、产品反馈,或者只是想打个招呼:
|
||||
</p>
|
||||
<p>
|
||||
<a href="mailto:hello@texpixel.com">hello@texpixel.com</a>
|
||||
</p>
|
||||
|
||||
<h2>响应时间</h2>
|
||||
<p>
|
||||
我们争取在 1 个工作日内回复所有咨询(中国标准时间 UTC+8)。
|
||||
高峰期内,支持请求最多可能需要 3 个工作日。
|
||||
</p>
|
||||
<p>
|
||||
如需自助解答,请查阅我们的<Link to="/docs">文档</Link>,大多数常见问题都有详细解答。
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
304
src/pages/CookiePolicyPage.tsx
Normal file
304
src/pages/CookiePolicyPage.tsx
Normal file
@@ -0,0 +1,304 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useLanguage } from '../contexts/LanguageContext';
|
||||
import SEOHead from '../components/seo/SEOHead';
|
||||
|
||||
const EFFECTIVE_DATE_EN = 'March 26, 2026';
|
||||
const EFFECTIVE_DATE_ZH = '2026年3月26日';
|
||||
|
||||
export default function CookiePolicyPage() {
|
||||
const { language } = useLanguage();
|
||||
const zh = language === 'zh';
|
||||
|
||||
return (
|
||||
<>
|
||||
<SEOHead
|
||||
title={zh ? 'Cookie 政策 — TexPixel' : 'Cookie Policy — TexPixel'}
|
||||
description={zh
|
||||
? 'TexPixel 如何使用 Cookie 及本地存储技术。'
|
||||
: 'How TexPixel uses cookies and local storage technologies.'}
|
||||
path="/cookies"
|
||||
/>
|
||||
|
||||
<div className="docs-detail">
|
||||
<Link to="/" className="docs-back-link">
|
||||
<svg viewBox="0 0 24 24"><path d="M15 18l-6-6 6-6" /></svg>
|
||||
{zh ? '返回首页' : 'Back to home'}
|
||||
</Link>
|
||||
|
||||
<div className="docs-article-header">
|
||||
<div className="docs-article-tags">
|
||||
<span className="docs-tag">Legal</span>
|
||||
</div>
|
||||
<h1 className="docs-article-h1">{zh ? 'Cookie 政策' : 'Cookie Policy'}</h1>
|
||||
<div className="docs-meta-row">
|
||||
<span>{zh ? `生效日期:${EFFECTIVE_DATE_ZH}` : `Effective: ${EFFECTIVE_DATE_EN}`}</span>
|
||||
<span className="docs-meta-sep">·</span>
|
||||
<span>TexPixel</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="docs-prose">
|
||||
{zh ? <CookieZh /> : <CookieEn />}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function CookieEn() {
|
||||
return (
|
||||
<>
|
||||
<p>
|
||||
This Cookie Policy explains how TexPixel ("we", "us", or "our") uses cookies and similar technologies on our
|
||||
website and web application. By using the Service, you consent to the use of cookies as described here.
|
||||
</p>
|
||||
|
||||
<h2>1. What Are Cookies?</h2>
|
||||
<p>
|
||||
Cookies are small text files placed on your device by websites you visit. They are widely used to make websites
|
||||
work efficiently and to provide information to site owners. Beyond traditional cookies, we also use browser
|
||||
<strong> localStorage</strong> — a similar technology that stores data in your browser without an expiration date.
|
||||
</p>
|
||||
|
||||
<h2>2. Cookies and Storage We Use</h2>
|
||||
<p>We use the following types of storage technologies:</p>
|
||||
|
||||
<h3 style={{ fontSize: '18px', fontWeight: 600, marginTop: '28px', marginBottom: '10px' }}>
|
||||
Strictly Necessary
|
||||
</h3>
|
||||
<p>
|
||||
These are essential for the Service to function. They cannot be disabled without breaking core functionality.
|
||||
</p>
|
||||
<table style={{ width: '100%', borderCollapse: 'collapse', fontSize: '14px', marginBottom: '8px' }}>
|
||||
<thead>
|
||||
<tr style={{ borderBottom: '1px solid var(--color-border)' }}>
|
||||
<th style={{ textAlign: 'left', padding: '8px 12px', fontWeight: 600 }}>Name</th>
|
||||
<th style={{ textAlign: 'left', padding: '8px 12px', fontWeight: 600 }}>Type</th>
|
||||
<th style={{ textAlign: 'left', padding: '8px 12px', fontWeight: 600 }}>Purpose</th>
|
||||
<th style={{ textAlign: 'left', padding: '8px 12px', fontWeight: 600 }}>Retention</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr style={{ borderBottom: '1px solid var(--color-border-light)' }}>
|
||||
<td style={{ padding: '8px 12px', fontFamily: 'monospace' }}>texpixel_token</td>
|
||||
<td style={{ padding: '8px 12px' }}>localStorage</td>
|
||||
<td style={{ padding: '8px 12px' }}>Stores your JWT authentication token to keep you logged in</td>
|
||||
<td style={{ padding: '8px 12px' }}>Until sign-out or token expiry</td>
|
||||
</tr>
|
||||
<tr style={{ borderBottom: '1px solid var(--color-border-light)' }}>
|
||||
<td style={{ padding: '8px 12px', fontFamily: 'monospace' }}>language</td>
|
||||
<td style={{ padding: '8px 12px' }}>localStorage</td>
|
||||
<td style={{ padding: '8px 12px' }}>Remembers your language preference (en/zh)</td>
|
||||
<td style={{ padding: '8px 12px' }}>Persistent (until cleared)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style={{ padding: '8px 12px', fontFamily: 'monospace' }}>texpixel_guest_usage_count</td>
|
||||
<td style={{ padding: '8px 12px' }}>localStorage</td>
|
||||
<td style={{ padding: '8px 12px' }}>Tracks free-tier usage count for guest (unauthenticated) users</td>
|
||||
<td style={{ padding: '8px 12px' }}>Persistent (until cleared)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h3 style={{ fontSize: '18px', fontWeight: 600, marginTop: '28px', marginBottom: '10px' }}>
|
||||
Functional
|
||||
</h3>
|
||||
<p>
|
||||
These enhance your experience but are not strictly required. Disabling them may affect some features.
|
||||
</p>
|
||||
<table style={{ width: '100%', borderCollapse: 'collapse', fontSize: '14px', marginBottom: '8px' }}>
|
||||
<thead>
|
||||
<tr style={{ borderBottom: '1px solid var(--color-border)' }}>
|
||||
<th style={{ textAlign: 'left', padding: '8px 12px', fontWeight: 600 }}>Name</th>
|
||||
<th style={{ textAlign: 'left', padding: '8px 12px', fontWeight: 600 }}>Type</th>
|
||||
<th style={{ textAlign: 'left', padding: '8px 12px', fontWeight: 600 }}>Purpose</th>
|
||||
<th style={{ textAlign: 'left', padding: '8px 12px', fontWeight: 600 }}>Retention</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style={{ padding: '8px 12px', fontFamily: 'monospace' }}>_ga, _gid</td>
|
||||
<td style={{ padding: '8px 12px' }}>Cookie</td>
|
||||
<td style={{ padding: '8px 12px' }}>Analytics (if enabled): distinguishes users and sessions for aggregate usage statistics</td>
|
||||
<td style={{ padding: '8px 12px' }}>2 years / 24 hours</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2>3. Third-Party Cookies</h2>
|
||||
<p>
|
||||
When you use <strong>Sign in with Google</strong>, Google may set its own cookies as part of the OAuth
|
||||
authentication flow. These cookies are governed by{' '}
|
||||
<a href="https://policies.google.com/privacy" target="_blank" rel="noopener noreferrer">Google's Privacy Policy</a>.
|
||||
We do not control Google's cookies and cannot disable them on Google's behalf.
|
||||
</p>
|
||||
<p>
|
||||
Our payment processor may also set session cookies during the checkout process. These are strictly necessary
|
||||
for completing your purchase and are removed after the transaction.
|
||||
</p>
|
||||
|
||||
<h2>4. What We Do Not Do</h2>
|
||||
<ul>
|
||||
<li>We do not use cookies or localStorage to track you across third-party websites.</li>
|
||||
<li>We do not sell data derived from cookies to advertisers or data brokers.</li>
|
||||
<li>We do not serve targeted advertising cookies.</li>
|
||||
</ul>
|
||||
|
||||
<h2>5. Managing Cookies & Local Storage</h2>
|
||||
<p>
|
||||
You can control and delete cookies and localStorage data through your browser settings. Note that clearing
|
||||
your authentication token (<code>texpixel_token</code>) will sign you out. Clearing the <code>language</code>{' '}
|
||||
key will reset your language preference to auto-detection.
|
||||
</p>
|
||||
<p>Instructions for managing storage in common browsers:</p>
|
||||
<ul>
|
||||
<li><strong>Chrome:</strong> Settings → Privacy and security → Clear browsing data</li>
|
||||
<li><strong>Firefox:</strong> Settings → Privacy & Security → Cookies and Site Data → Clear Data</li>
|
||||
<li><strong>Safari:</strong> Settings → Privacy → Manage Website Data</li>
|
||||
<li><strong>Edge:</strong> Settings → Privacy, search, and services → Clear browsing data</li>
|
||||
</ul>
|
||||
<p>
|
||||
To inspect or manually delete our localStorage entries, open your browser's Developer Tools → Application
|
||||
tab → Local Storage → <code>texpixel.com</code>.
|
||||
</p>
|
||||
|
||||
<h2>6. Changes to This Policy</h2>
|
||||
<p>
|
||||
We may update this Cookie Policy from time to time. The "Effective" date at the top of this page indicates
|
||||
when the policy was last revised. Continued use of the Service after changes constitutes acceptance.
|
||||
</p>
|
||||
|
||||
<h2>7. Contact Us</h2>
|
||||
<p>
|
||||
If you have questions about our use of cookies, contact us at{' '}
|
||||
<a href="mailto:privacy@texpixel.com">privacy@texpixel.com</a>.
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function CookieZh() {
|
||||
return (
|
||||
<>
|
||||
<p>
|
||||
本 Cookie 政策说明 TexPixel("我们")如何在我们的网站及 Web 应用中使用 Cookie 及类似技术。
|
||||
使用本服务即表示您同意按本政策所述使用 Cookie。
|
||||
</p>
|
||||
|
||||
<h2>1. 什么是 Cookie?</h2>
|
||||
<p>
|
||||
Cookie 是您访问网站时由网站在您设备上放置的小型文本文件。它们被广泛用于使网站高效运行,
|
||||
并向网站所有者提供信息。除传统 Cookie 外,我们还使用浏览器的
|
||||
<strong> localStorage</strong>——一种在浏览器中存储数据且无过期时间的类似技术。
|
||||
</p>
|
||||
|
||||
<h2>2. 我们使用的 Cookie 及存储项</h2>
|
||||
<p>我们使用以下几类存储技术:</p>
|
||||
|
||||
<h3 style={{ fontSize: '18px', fontWeight: 600, marginTop: '28px', marginBottom: '10px' }}>
|
||||
严格必要类
|
||||
</h3>
|
||||
<p>这些存储项对于服务的正常运行不可或缺,禁用后核心功能将无法使用。</p>
|
||||
<table style={{ width: '100%', borderCollapse: 'collapse', fontSize: '14px', marginBottom: '8px' }}>
|
||||
<thead>
|
||||
<tr style={{ borderBottom: '1px solid var(--color-border)' }}>
|
||||
<th style={{ textAlign: 'left', padding: '8px 12px', fontWeight: 600 }}>名称</th>
|
||||
<th style={{ textAlign: 'left', padding: '8px 12px', fontWeight: 600 }}>类型</th>
|
||||
<th style={{ textAlign: 'left', padding: '8px 12px', fontWeight: 600 }}>用途</th>
|
||||
<th style={{ textAlign: 'left', padding: '8px 12px', fontWeight: 600 }}>保留时间</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr style={{ borderBottom: '1px solid var(--color-border-light)' }}>
|
||||
<td style={{ padding: '8px 12px', fontFamily: 'monospace' }}>texpixel_token</td>
|
||||
<td style={{ padding: '8px 12px' }}>localStorage</td>
|
||||
<td style={{ padding: '8px 12px' }}>存储 JWT 身份验证令牌,维持登录状态</td>
|
||||
<td style={{ padding: '8px 12px' }}>退出登录或令牌过期时清除</td>
|
||||
</tr>
|
||||
<tr style={{ borderBottom: '1px solid var(--color-border-light)' }}>
|
||||
<td style={{ padding: '8px 12px', fontFamily: 'monospace' }}>language</td>
|
||||
<td style={{ padding: '8px 12px' }}>localStorage</td>
|
||||
<td style={{ padding: '8px 12px' }}>记录您的语言偏好(en/zh)</td>
|
||||
<td style={{ padding: '8px 12px' }}>持久(直至手动清除)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style={{ padding: '8px 12px', fontFamily: 'monospace' }}>texpixel_guest_usage_count</td>
|
||||
<td style={{ padding: '8px 12px' }}>localStorage</td>
|
||||
<td style={{ padding: '8px 12px' }}>记录未登录访客用户的免费使用次数</td>
|
||||
<td style={{ padding: '8px 12px' }}>持久(直至手动清除)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h3 style={{ fontSize: '18px', fontWeight: 600, marginTop: '28px', marginBottom: '10px' }}>
|
||||
功能类
|
||||
</h3>
|
||||
<p>这些存储项可改善您的使用体验,但非严格必需。禁用可能影响部分功能。</p>
|
||||
<table style={{ width: '100%', borderCollapse: 'collapse', fontSize: '14px', marginBottom: '8px' }}>
|
||||
<thead>
|
||||
<tr style={{ borderBottom: '1px solid var(--color-border)' }}>
|
||||
<th style={{ textAlign: 'left', padding: '8px 12px', fontWeight: 600 }}>名称</th>
|
||||
<th style={{ textAlign: 'left', padding: '8px 12px', fontWeight: 600 }}>类型</th>
|
||||
<th style={{ textAlign: 'left', padding: '8px 12px', fontWeight: 600 }}>用途</th>
|
||||
<th style={{ textAlign: 'left', padding: '8px 12px', fontWeight: 600 }}>保留时间</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style={{ padding: '8px 12px', fontFamily: 'monospace' }}>_ga, _gid</td>
|
||||
<td style={{ padding: '8px 12px' }}>Cookie</td>
|
||||
<td style={{ padding: '8px 12px' }}>数据分析(如已启用):区分用户与会话,用于汇总使用统计</td>
|
||||
<td style={{ padding: '8px 12px' }}>2年 / 24小时</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2>3. 第三方 Cookie</h2>
|
||||
<p>
|
||||
使用<strong>谷歌登录</strong>时,Google 可能在 OAuth 身份验证流程中设置自己的 Cookie。
|
||||
这些 Cookie 受{' '}
|
||||
<a href="https://policies.google.com/privacy" target="_blank" rel="noopener noreferrer">Google 隐私政策</a>
|
||||
约束。我们无法控制 Google 的 Cookie,也无法代表 Google 禁用它们。
|
||||
</p>
|
||||
<p>
|
||||
我们的支付处理商也可能在结账过程中设置会话 Cookie,这些 Cookie 是完成购买所必需的,交易结束后将自动删除。
|
||||
</p>
|
||||
|
||||
<h2>4. 我们不会做的事</h2>
|
||||
<ul>
|
||||
<li>我们不会使用 Cookie 或 localStorage 跨第三方网站追踪您。</li>
|
||||
<li>我们不会将 Cookie 衍生数据出售给广告商或数据经纪商。</li>
|
||||
<li>我们不会投放定向广告 Cookie。</li>
|
||||
</ul>
|
||||
|
||||
<h2>5. 管理 Cookie 与本地存储</h2>
|
||||
<p>
|
||||
您可以通过浏览器设置控制和删除 Cookie 及 localStorage 数据。请注意,清除身份验证令牌
|
||||
(<code>texpixel_token</code>)将导致您退出登录;清除 <code>language</code> 键将重置语言偏好为自动检测。
|
||||
</p>
|
||||
<p>常见浏览器管理存储的操作路径:</p>
|
||||
<ul>
|
||||
<li><strong>Chrome:</strong>设置 → 隐私设置和安全性 → 清除浏览数据</li>
|
||||
<li><strong>Firefox:</strong>设置 → 隐私与安全 → Cookie 和网站数据 → 清除数据</li>
|
||||
<li><strong>Safari:</strong>设置 → 隐私 → 管理网站数据</li>
|
||||
<li><strong>Edge:</strong>设置 → 隐私、搜索和服务 → 清除浏览数据</li>
|
||||
</ul>
|
||||
<p>
|
||||
如需手动检查或删除我们的 localStorage 条目,请打开浏览器开发者工具 → Application 选项卡 →
|
||||
Local Storage → <code>texpixel.com</code>。
|
||||
</p>
|
||||
|
||||
<h2>6. 政策变更</h2>
|
||||
<p>
|
||||
我们可能不时更新本 Cookie 政策。页面顶部的"生效日期"表示本政策最近一次修订的时间。
|
||||
变更后继续使用本服务,即视为您接受修订内容。
|
||||
</p>
|
||||
|
||||
<h2>7. 联系我们</h2>
|
||||
<p>
|
||||
如对我们使用 Cookie 的方式有任何疑问,请通过{' '}
|
||||
<a href="mailto:privacy@texpixel.com">privacy@texpixel.com</a> 联系我们。
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
283
src/pages/PrivacyPage.tsx
Normal file
283
src/pages/PrivacyPage.tsx
Normal file
@@ -0,0 +1,283 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useLanguage } from '../contexts/LanguageContext';
|
||||
import SEOHead from '../components/seo/SEOHead';
|
||||
|
||||
const EFFECTIVE_DATE_EN = 'March 26, 2026';
|
||||
const EFFECTIVE_DATE_ZH = '2026年3月26日';
|
||||
|
||||
export default function PrivacyPage() {
|
||||
const { language } = useLanguage();
|
||||
const zh = language === 'zh';
|
||||
|
||||
return (
|
||||
<>
|
||||
<SEOHead
|
||||
title={zh ? '隐私政策 — TexPixel' : 'Privacy Policy — TexPixel'}
|
||||
description={zh
|
||||
? 'TexPixel 如何收集、使用和保护您的个人信息。'
|
||||
: 'How TexPixel collects, uses, and protects your personal information.'}
|
||||
path="/privacy"
|
||||
/>
|
||||
|
||||
<div className="docs-detail">
|
||||
<Link to="/" className="docs-back-link">
|
||||
<svg viewBox="0 0 24 24"><path d="M15 18l-6-6 6-6" /></svg>
|
||||
{zh ? '返回首页' : 'Back to home'}
|
||||
</Link>
|
||||
|
||||
<div className="docs-article-header">
|
||||
<div className="docs-article-tags">
|
||||
<span className="docs-tag">Legal</span>
|
||||
</div>
|
||||
<h1 className="docs-article-h1">{zh ? '隐私政策' : 'Privacy Policy'}</h1>
|
||||
<div className="docs-meta-row">
|
||||
<span>{zh ? `生效日期:${EFFECTIVE_DATE_ZH}` : `Effective: ${EFFECTIVE_DATE_EN}`}</span>
|
||||
<span className="docs-meta-sep">·</span>
|
||||
<span>TexPixel</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="docs-prose">
|
||||
{zh ? <PrivacyZh /> : <PrivacyEn />}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function PrivacyEn() {
|
||||
return (
|
||||
<>
|
||||
<p>
|
||||
TexPixel ("we", "us", or "our") is committed to protecting your privacy. This Privacy Policy explains what
|
||||
information we collect, how we use it, and your rights regarding your data. By using the Service, you consent
|
||||
to the practices described here.
|
||||
</p>
|
||||
|
||||
<h2>1. Information We Collect</h2>
|
||||
<p><strong>Information you provide directly:</strong></p>
|
||||
<ul>
|
||||
<li><strong>Account data:</strong> email address, password (stored as a hashed value — we never see your plaintext password), and display name.</li>
|
||||
<li><strong>Google OAuth data:</strong> if you sign in with Google, we receive your Google account email, name, and profile picture URL as provided by Google.</li>
|
||||
<li><strong>Payment data:</strong> when you purchase credits, payment is processed by a third-party payment processor. We receive only a transaction ID and purchase amount — we do not store your card details.</li>
|
||||
<li><strong>User Content:</strong> documents, images, and files you upload for recognition processing.</li>
|
||||
<li><strong>Support communications:</strong> messages you send to our support team.</li>
|
||||
</ul>
|
||||
<p><strong>Information collected automatically:</strong></p>
|
||||
<ul>
|
||||
<li><strong>Usage data:</strong> pages visited, features used, recognition tasks submitted, timestamps, and error logs.</li>
|
||||
<li><strong>Device and technical data:</strong> IP address, browser type, operating system, and referring URL.</li>
|
||||
<li><strong>Cookies and local storage:</strong> authentication tokens (JWT stored in localStorage), language preference, and guest usage count. See our <Link to="/cookies">Cookie Policy</Link> for details.</li>
|
||||
</ul>
|
||||
|
||||
<h2>2. How We Use Your Information</h2>
|
||||
<ul>
|
||||
<li>Provide, operate, and improve the Service.</li>
|
||||
<li>Authenticate your identity and maintain your session.</li>
|
||||
<li>Process recognition tasks you submit.</li>
|
||||
<li>Manage your credit balance and transaction history.</li>
|
||||
<li>Send transactional emails: verification codes, payment receipts, and account security alerts.</li>
|
||||
<li>Detect and prevent fraud, abuse, and violations of our Terms of Service.</li>
|
||||
<li>Analyze aggregate usage patterns to improve the Service (using anonymized or pseudonymized data).</li>
|
||||
<li>Comply with legal obligations.</li>
|
||||
</ul>
|
||||
<p>
|
||||
We do <strong>not</strong> use your User Content to train AI or machine learning models without your explicit,
|
||||
opt-in consent. We do not sell your personal information to third parties.
|
||||
</p>
|
||||
|
||||
<h2>3. Information Sharing</h2>
|
||||
<p>We share your information only in the following circumstances:</p>
|
||||
<ul>
|
||||
<li><strong>Service providers:</strong> cloud object storage (for file storage), email delivery services (for verification codes and notifications), and payment processors. These parties process data on our behalf under data processing agreements.</li>
|
||||
<li><strong>Google:</strong> when you use Google OAuth, data is exchanged with Google in accordance with Google's Privacy Policy.</li>
|
||||
<li><strong>Legal compliance:</strong> we may disclose information if required by law, court order, or governmental authority, or to protect the rights, property, or safety of TexPixel, our users, or the public.</li>
|
||||
<li><strong>Business transfers:</strong> in the event of a merger, acquisition, or sale of assets, your information may be transferred to the successor entity, subject to the same privacy protections.</li>
|
||||
</ul>
|
||||
|
||||
<h2>4. Data Retention</h2>
|
||||
<ul>
|
||||
<li><strong>Account data:</strong> retained for as long as your account is active, plus a 30-day grace period after deletion.</li>
|
||||
<li><strong>User Content:</strong> uploaded files are retained for up to 90 days after task completion, then permanently deleted unless you save them to your account history.</li>
|
||||
<li><strong>Task history:</strong> recognition results in your account history are retained until you delete them or close your account.</li>
|
||||
<li><strong>Payment records:</strong> retained for 7 years as required by applicable financial regulations.</li>
|
||||
<li><strong>Log data:</strong> server and access logs are retained for up to 90 days.</li>
|
||||
</ul>
|
||||
|
||||
<h2>5. Data Security</h2>
|
||||
<p>
|
||||
We implement industry-standard security measures including TLS encryption for data in transit, encrypted storage
|
||||
for sensitive credentials, and access controls limiting who can access production data. However, no system is
|
||||
completely secure. We encourage you to use a strong, unique password and to contact us immediately if you suspect
|
||||
unauthorized access to your account.
|
||||
</p>
|
||||
|
||||
<h2>6. Your Rights</h2>
|
||||
<p>
|
||||
Depending on your location, you may have the following rights regarding your personal data:
|
||||
</p>
|
||||
<ul>
|
||||
<li><strong>Access:</strong> request a copy of the personal data we hold about you.</li>
|
||||
<li><strong>Correction:</strong> request correction of inaccurate or incomplete data.</li>
|
||||
<li><strong>Deletion:</strong> request deletion of your personal data ("right to be forgotten"), subject to legal retention obligations.</li>
|
||||
<li><strong>Portability:</strong> receive your data in a structured, machine-readable format.</li>
|
||||
<li><strong>Objection / Restriction:</strong> object to or request restriction of certain processing activities.</li>
|
||||
<li><strong>Withdraw Consent:</strong> where processing is based on consent, withdraw that consent at any time.</li>
|
||||
</ul>
|
||||
<p>
|
||||
To exercise any of these rights, contact us at <a href="mailto:privacy@texpixel.com">privacy@texpixel.com</a>.
|
||||
We will respond within 30 days. We may need to verify your identity before processing your request.
|
||||
</p>
|
||||
|
||||
<h2>7. Children's Privacy</h2>
|
||||
<p>
|
||||
The Service is not directed to children under 13. We do not knowingly collect personal information from children
|
||||
under 13. If we become aware that a child under 13 has provided us with personal information, we will delete it
|
||||
promptly. If you believe a child has provided us with their data, please contact us immediately.
|
||||
</p>
|
||||
|
||||
<h2>8. International Data Transfers</h2>
|
||||
<p>
|
||||
TexPixel operates primarily from servers located in China. If you access the Service from outside China,
|
||||
your data will be transferred to and processed in China. By using the Service, you consent to this transfer.
|
||||
We take appropriate safeguards to ensure your data is treated in accordance with this Privacy Policy regardless
|
||||
of where it is processed.
|
||||
</p>
|
||||
|
||||
<h2>9. Third-Party Links</h2>
|
||||
<p>
|
||||
The Service may contain links to third-party websites. This Privacy Policy does not apply to those sites.
|
||||
We encourage you to review the privacy policies of any third-party services you visit.
|
||||
</p>
|
||||
|
||||
<h2>10. Changes to This Policy</h2>
|
||||
<p>
|
||||
We may update this Privacy Policy from time to time. We will notify you of material changes via email or a
|
||||
prominent notice on the Service at least 14 days before the changes take effect. The "Effective" date at the
|
||||
top of this page indicates when the policy was last revised.
|
||||
</p>
|
||||
|
||||
<h2>11. Contact Us</h2>
|
||||
<p>
|
||||
For privacy-related questions or to exercise your rights, contact us at{' '}
|
||||
<a href="mailto:privacy@texpixel.com">privacy@texpixel.com</a>.
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function PrivacyZh() {
|
||||
return (
|
||||
<>
|
||||
<p>
|
||||
TexPixel("我们")致力于保护您的隐私。本隐私政策说明我们收集哪些信息、如何使用这些信息,
|
||||
以及您对自己数据所享有的权利。使用本服务即表示您同意本政策所述的相关做法。
|
||||
</p>
|
||||
|
||||
<h2>1. 我们收集的信息</h2>
|
||||
<p><strong>您主动提供的信息:</strong></p>
|
||||
<ul>
|
||||
<li><strong>账户数据:</strong>电子邮件地址、密码(以哈希形式存储,我们从不接触您的明文密码)及显示名称。</li>
|
||||
<li><strong>Google OAuth 数据:</strong>通过 Google 登录时,我们会获取 Google 提供的账户邮箱、姓名及头像 URL。</li>
|
||||
<li><strong>支付数据:</strong>购买积分时,支付由第三方支付处理商完成。我们仅获取交易 ID 和购买金额,不存储您的银行卡信息。</li>
|
||||
<li><strong>用户内容:</strong>您上传以供识别处理的文档、图片及文件。</li>
|
||||
<li><strong>支持沟通:</strong>您向我们客服发送的消息。</li>
|
||||
</ul>
|
||||
<p><strong>自动收集的信息:</strong></p>
|
||||
<ul>
|
||||
<li><strong>使用数据:</strong>访问的页面、使用的功能、提交的识别任务、时间戳及错误日志。</li>
|
||||
<li><strong>设备与技术数据:</strong>IP 地址、浏览器类型、操作系统及来源页面 URL。</li>
|
||||
<li><strong>Cookie 与本地存储:</strong>身份验证令牌(JWT,存储于 localStorage)、语言偏好及访客使用次数。详情请参阅我们的 <Link to="/cookies">Cookie 政策</Link>。</li>
|
||||
</ul>
|
||||
|
||||
<h2>2. 我们如何使用您的信息</h2>
|
||||
<ul>
|
||||
<li>提供、运营和改进本服务。</li>
|
||||
<li>验证您的身份并维持您的登录会话。</li>
|
||||
<li>处理您提交的识别任务。</li>
|
||||
<li>管理您的积分余额和交易记录。</li>
|
||||
<li>发送交易类邮件:验证码、支付收据及账户安全提醒。</li>
|
||||
<li>检测和防止欺诈、滥用及违反服务条款的行为。</li>
|
||||
<li>使用匿名化或假名化数据分析汇总使用模式,以改进服务。</li>
|
||||
<li>履行法律义务。</li>
|
||||
</ul>
|
||||
<p>
|
||||
未经您明确选择同意,我们<strong>不会</strong>使用您的用户内容训练 AI 或机器学习模型。
|
||||
我们不会将您的个人信息出售给第三方。
|
||||
</p>
|
||||
|
||||
<h2>3. 信息共享</h2>
|
||||
<p>仅在以下情况下,我们会共享您的信息:</p>
|
||||
<ul>
|
||||
<li><strong>服务提供商:</strong>云对象存储(用于文件存储)、邮件发送服务(用于验证码和通知)及支付处理商。这些方依据数据处理协议代表我们处理数据。</li>
|
||||
<li><strong>Google:</strong>使用 Google OAuth 时,数据将依据 Google 隐私政策与 Google 进行交换。</li>
|
||||
<li><strong>法律合规:</strong>法律、法院命令或政府机构要求时,或为保护 TexPixel、用户或公众的权利、财产或安全时,我们可能披露相关信息。</li>
|
||||
<li><strong>业务转让:</strong>发生合并、收购或资产出售时,您的信息可能转移至继承方,并受同等隐私保护。</li>
|
||||
</ul>
|
||||
|
||||
<h2>4. 数据保留</h2>
|
||||
<ul>
|
||||
<li><strong>账户数据:</strong>在账户有效期间保留,账户删除后额外保留 30 天宽限期。</li>
|
||||
<li><strong>用户内容:</strong>上传的文件在任务完成后最多保留 90 天,之后永久删除,除非您将其保存至账户历史记录。</li>
|
||||
<li><strong>任务历史:</strong>账户中的识别结果保留至您删除它们或注销账户为止。</li>
|
||||
<li><strong>支付记录:</strong>依据适用财务法规保留 7 年。</li>
|
||||
<li><strong>日志数据:</strong>服务器及访问日志保留最多 90 天。</li>
|
||||
</ul>
|
||||
|
||||
<h2>5. 数据安全</h2>
|
||||
<p>
|
||||
我们实施行业标准安全措施,包括传输中的 TLS 加密、敏感凭证的加密存储,
|
||||
以及限制生产数据访问权限的访问控制。然而,任何系统都无法做到绝对安全。
|
||||
建议您使用强密码,并在怀疑账户遭到未授权访问时立即联系我们。
|
||||
</p>
|
||||
|
||||
<h2>6. 您的权利</h2>
|
||||
<p>根据您所在地区,您可能对个人数据享有以下权利:</p>
|
||||
<ul>
|
||||
<li><strong>访问权:</strong>请求获取我们持有的关于您的个人数据副本。</li>
|
||||
<li><strong>更正权:</strong>请求更正不准确或不完整的数据。</li>
|
||||
<li><strong>删除权:</strong>请求删除您的个人数据("被遗忘权"),受法律保留义务约束。</li>
|
||||
<li><strong>数据可携权:</strong>以结构化、机器可读的格式接收您的数据。</li>
|
||||
<li><strong>反对权/限制权:</strong>反对或要求限制某些处理活动。</li>
|
||||
<li><strong>撤回同意:</strong>对于基于同意的处理,可随时撤回同意。</li>
|
||||
</ul>
|
||||
<p>
|
||||
如需行使上述任何权利,请发送邮件至{' '}
|
||||
<a href="mailto:privacy@texpixel.com">privacy@texpixel.com</a>。
|
||||
我们将在 30 天内答复。处理请求前,我们可能需要验证您的身份。
|
||||
</p>
|
||||
|
||||
<h2>7. 儿童隐私</h2>
|
||||
<p>
|
||||
本服务不面向 13 周岁以下儿童。我们不会有意收集 13 周岁以下儿童的个人信息。
|
||||
若发现 13 周岁以下儿童向我们提供了个人信息,我们将立即予以删除。
|
||||
如您认为儿童已向我们提供个人数据,请立即联系我们。
|
||||
</p>
|
||||
|
||||
<h2>8. 国际数据传输</h2>
|
||||
<p>
|
||||
TexPixel 主要依托位于中国的服务器运营。如您从中国境外访问本服务,
|
||||
您的数据将被传输至中国并在中国境内处理。使用本服务即表示您同意此类传输。
|
||||
无论数据在何处处理,我们均采取适当保障措施,确保您的数据受本隐私政策保护。
|
||||
</p>
|
||||
|
||||
<h2>9. 第三方链接</h2>
|
||||
<p>
|
||||
本服务可能包含第三方网站的链接。本隐私政策不适用于这些网站。
|
||||
建议您查阅所访问的任何第三方服务的隐私政策。
|
||||
</p>
|
||||
|
||||
<h2>10. 政策变更</h2>
|
||||
<p>
|
||||
我们可能不时更新本隐私政策。对于重大变更,我们将通过电子邮件或服务内显著通知的方式,
|
||||
在变更生效前至少 14 天提前告知您。页面顶部的"生效日期"表示本政策最近一次修订的时间。
|
||||
</p>
|
||||
|
||||
<h2>11. 联系我们</h2>
|
||||
<p>
|
||||
如有隐私相关问题或需行使您的权利,请通过{' '}
|
||||
<a href="mailto:privacy@texpixel.com">privacy@texpixel.com</a> 联系我们。
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
322
src/pages/TermsPage.tsx
Normal file
322
src/pages/TermsPage.tsx
Normal file
@@ -0,0 +1,322 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useLanguage } from '../contexts/LanguageContext';
|
||||
import SEOHead from '../components/seo/SEOHead';
|
||||
|
||||
const EFFECTIVE_DATE_EN = 'March 26, 2026';
|
||||
const EFFECTIVE_DATE_ZH = '2026年3月26日';
|
||||
|
||||
export default function TermsPage() {
|
||||
const { language } = useLanguage();
|
||||
const zh = language === 'zh';
|
||||
|
||||
return (
|
||||
<>
|
||||
<SEOHead
|
||||
title={zh ? '服务条款 — TexPixel' : 'Terms of Service — TexPixel'}
|
||||
description={zh
|
||||
? 'TexPixel 服务条款:账户注册、积分制付费、用户内容与使用限制。'
|
||||
: 'TexPixel Terms of Service: account registration, credit-based billing, user content, and usage restrictions.'}
|
||||
path="/terms"
|
||||
/>
|
||||
|
||||
<div className="docs-detail">
|
||||
<Link to="/" className="docs-back-link">
|
||||
<svg viewBox="0 0 24 24"><path d="M15 18l-6-6 6-6" /></svg>
|
||||
{zh ? '返回首页' : 'Back to home'}
|
||||
</Link>
|
||||
|
||||
<div className="docs-article-header">
|
||||
<div className="docs-article-tags">
|
||||
<span className="docs-tag">Legal</span>
|
||||
</div>
|
||||
<h1 className="docs-article-h1">{zh ? '服务条款' : 'Terms of Service'}</h1>
|
||||
<div className="docs-meta-row">
|
||||
<span>{zh ? `生效日期:${EFFECTIVE_DATE_ZH}` : `Effective: ${EFFECTIVE_DATE_EN}`}</span>
|
||||
<span className="docs-meta-sep">·</span>
|
||||
<span>TexPixel</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="docs-prose">
|
||||
{zh ? <TermsZh /> : <TermsEn />}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function TermsEn() {
|
||||
return (
|
||||
<>
|
||||
<p>
|
||||
Please read these Terms of Service ("Terms") carefully before using TexPixel ("Service", "we", "us", or "our").
|
||||
By creating an account or using the Service, you agree to be bound by these Terms. If you do not agree, do not use the Service.
|
||||
</p>
|
||||
|
||||
<h2>1. Eligibility</h2>
|
||||
<p>
|
||||
You must be at least 13 years old to use the Service. If you are under 18, you must have your parent or legal guardian's
|
||||
permission. By using the Service, you represent and warrant that you meet these requirements. Paid features (credits
|
||||
purchases) are available only to users who are 18 or older or have legal capacity to enter into contracts in their jurisdiction.
|
||||
</p>
|
||||
|
||||
<h2>2. Account Registration</h2>
|
||||
<p>
|
||||
You may register using an email address and one-time verification code, or by linking a Google account via OAuth 2.0.
|
||||
You are responsible for maintaining the confidentiality of your account credentials and for all activities that occur
|
||||
under your account. You must provide accurate, current, and complete information and keep your account information updated.
|
||||
We reserve the right to suspend or terminate accounts that contain false information or that are used fraudulently.
|
||||
</p>
|
||||
|
||||
<h2>3. Credits & Payment</h2>
|
||||
<p>
|
||||
Access to certain features of the Service requires purchasing credits. Credits are a prepaid, non-refundable virtual
|
||||
currency used to pay for recognition tasks (image-to-LaTeX, PDF-to-Markdown, handwriting OCR, etc.).
|
||||
</p>
|
||||
<ul>
|
||||
<li><strong>No Refunds.</strong> Credits are non-refundable except where required by applicable law. If you believe
|
||||
you were charged in error, contact us within 30 days of the charge.</li>
|
||||
<li><strong>Expiration.</strong> Credits do not expire as long as your account remains active. We reserve the right
|
||||
to introduce expiration policies with at least 60 days' notice.</li>
|
||||
<li><strong>Price Changes.</strong> We may change credit prices at any time. Changes will not affect credits already purchased.</li>
|
||||
<li><strong>Taxes.</strong> You are responsible for all applicable taxes on credit purchases in your jurisdiction.</li>
|
||||
<li><strong>Free Tier.</strong> Guest users receive a limited number of free recognition tasks. Free usage is subject
|
||||
to change without notice.</li>
|
||||
<li><strong>Chargebacks.</strong> Initiating a chargeback without first contacting us may result in immediate account suspension.</li>
|
||||
</ul>
|
||||
|
||||
<h2>4. User Content</h2>
|
||||
<p>
|
||||
You retain all intellectual property rights in the documents, images, and files you upload ("User Content"). By
|
||||
uploading User Content, you grant TexPixel a limited, non-exclusive, royalty-free license to process, store, and
|
||||
transmit your content solely to provide the Service.
|
||||
</p>
|
||||
<ul>
|
||||
<li>We do not use your User Content to train AI models without your explicit consent.</li>
|
||||
<li>We do not share your User Content with third parties except as necessary to operate the Service (e.g., cloud
|
||||
storage providers).</li>
|
||||
<li>You are responsible for ensuring you have the right to upload and process any content you submit.</li>
|
||||
<li>Do not upload content that is illegal, infringes third-party rights, or contains personally identifiable
|
||||
information of others without their consent.</li>
|
||||
</ul>
|
||||
|
||||
<h2>5. Accuracy Disclaimer</h2>
|
||||
<p>
|
||||
TexPixel uses AI-based recognition technology. Recognition results (LaTeX, Markdown, text) are provided on an
|
||||
"as-is" basis. We do not warrant that results will be accurate, complete, or suitable for any particular purpose.
|
||||
You are responsible for verifying all outputs before use in academic, professional, or published work.
|
||||
</p>
|
||||
|
||||
<h2>6. Prohibited Uses</h2>
|
||||
<p>You agree not to:</p>
|
||||
<ul>
|
||||
<li>Use the Service to process content that is illegal, obscene, defamatory, or violates third-party rights.</li>
|
||||
<li>Reverse-engineer, decompile, or attempt to extract source code from the Service.</li>
|
||||
<li>Automate requests to the Service in a manner that exceeds your plan limits or degrades service for others.</li>
|
||||
<li>Use the Service to build a competing product or service without our written permission.</li>
|
||||
<li>Share, sell, or transfer your account or credits to another party.</li>
|
||||
<li>Use bots, scrapers, or automated tools to access the Service beyond what is permitted by the API documentation.</li>
|
||||
<li>Circumvent any technical measures we use to limit usage or enforce these Terms.</li>
|
||||
</ul>
|
||||
|
||||
<h2>7. Intellectual Property</h2>
|
||||
<p>
|
||||
The Service, including its software, design, trademarks, and content (excluding User Content), is owned by TexPixel
|
||||
and protected by applicable intellectual property laws. You may not copy, modify, distribute, sell, or lease any part
|
||||
of the Service without our prior written consent.
|
||||
</p>
|
||||
|
||||
<h2>8. Third-Party Services</h2>
|
||||
<p>
|
||||
The Service integrates with third-party services including Google (for OAuth), cloud object storage providers (for
|
||||
file storage), and payment processors. Your use of these services is also subject to their respective terms and
|
||||
privacy policies. We are not responsible for the practices of third-party services.
|
||||
</p>
|
||||
|
||||
<h2>9. Disclaimer of Warranties</h2>
|
||||
<p>
|
||||
THE SERVICE IS PROVIDED "AS IS" AND "AS AVAILABLE" WITHOUT WARRANTIES OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
|
||||
WE DO NOT WARRANT THAT THE SERVICE WILL BE UNINTERRUPTED, ERROR-FREE, OR SECURE.
|
||||
</p>
|
||||
|
||||
<h2>10. Limitation of Liability</h2>
|
||||
<p>
|
||||
TO THE MAXIMUM EXTENT PERMITTED BY LAW, TEXPIXEL SHALL NOT BE LIABLE FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
|
||||
CONSEQUENTIAL, OR PUNITIVE DAMAGES, INCLUDING LOSS OF DATA, LOSS OF PROFITS, OR BUSINESS INTERRUPTION, ARISING
|
||||
FROM YOUR USE OF THE SERVICE, EVEN IF WE HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||
</p>
|
||||
<p>
|
||||
OUR TOTAL LIABILITY TO YOU FOR ANY CLAIMS ARISING FROM YOUR USE OF THE SERVICE SHALL NOT EXCEED THE AMOUNT YOU
|
||||
PAID US IN THE 12 MONTHS PRECEDING THE CLAIM.
|
||||
</p>
|
||||
|
||||
<h2>11. Termination</h2>
|
||||
<p>
|
||||
We may suspend or terminate your account at any time, with or without notice, for conduct that we determine violates
|
||||
these Terms or is harmful to other users, us, or third parties. You may delete your account at any time by contacting
|
||||
us. Upon termination, your right to use the Service ceases immediately. Unused credits at the time of termination for
|
||||
cause are forfeited; credits remaining upon voluntary account closure may be refunded at our discretion.
|
||||
</p>
|
||||
|
||||
<h2>12. Indemnification</h2>
|
||||
<p>
|
||||
You agree to indemnify, defend, and hold harmless TexPixel and its officers, directors, employees, and agents from
|
||||
any claims, liabilities, damages, and expenses (including reasonable attorneys' fees) arising from your use of the
|
||||
Service, your User Content, or your violation of these Terms.
|
||||
</p>
|
||||
|
||||
<h2>13. Governing Law & Dispute Resolution</h2>
|
||||
<p>
|
||||
These Terms are governed by the laws of the People's Republic of China without regard to conflict-of-law principles.
|
||||
Any disputes arising from these Terms shall first be subject to good-faith negotiation. If unresolved within 30 days,
|
||||
disputes shall be submitted to binding arbitration in accordance with applicable rules, or to the competent courts of
|
||||
the jurisdiction where TexPixel is incorporated.
|
||||
</p>
|
||||
|
||||
<h2>14. Changes to These Terms</h2>
|
||||
<p>
|
||||
We may modify these Terms at any time. We will provide notice of material changes via email or a prominent notice on
|
||||
the Service at least 14 days before changes take effect. Your continued use of the Service after the effective date
|
||||
constitutes your acceptance of the revised Terms.
|
||||
</p>
|
||||
|
||||
<h2>15. Contact Us</h2>
|
||||
<p>
|
||||
If you have questions about these Terms, please contact us at{' '}
|
||||
<a href="mailto:legal@texpixel.com">legal@texpixel.com</a>.
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function TermsZh() {
|
||||
return (
|
||||
<>
|
||||
<p>
|
||||
请在使用 TexPixel("服务"、"我们")之前仔细阅读本服务条款("条款")。
|
||||
注册账户或使用本服务,即表示您同意受本条款约束。如您不同意,请勿使用本服务。
|
||||
</p>
|
||||
|
||||
<h2>1. 使用资格</h2>
|
||||
<p>
|
||||
您须年满 13 周岁方可使用本服务。若您未满 18 周岁,须取得父母或法定监护人的同意。
|
||||
付费功能(积分购买)仅向具备完全民事行为能力的成年用户开放。
|
||||
使用本服务即表示您保证符合上述要求。
|
||||
</p>
|
||||
|
||||
<h2>2. 账户注册</h2>
|
||||
<p>
|
||||
您可通过电子邮件地址及一次性验证码注册账户,或通过 Google OAuth 2.0 关联 Google 账号登录。
|
||||
您有责任妥善保管账户凭证,并对账户下发生的所有活动承担责任。
|
||||
您须提供真实、准确、完整的注册信息,并保持信息更新。
|
||||
对于包含虚假信息或存在欺诈行为的账户,我们保留暂停或注销的权利。
|
||||
</p>
|
||||
|
||||
<h2>3. 积分与付费</h2>
|
||||
<p>
|
||||
使用本服务的部分功能需要购买积分。积分是一种预付制虚拟货币,用于支付识别任务费用(图片转 LaTeX、PDF 转 Markdown、手写识别等)。
|
||||
</p>
|
||||
<ul>
|
||||
<li><strong>不予退款。</strong>除法律另有规定外,积分一经购买不予退款。若您认为被错误扣款,请在扣款后 30 天内联系我们。</li>
|
||||
<li><strong>有效期。</strong>只要账户保持活跃,积分不会过期。我们保留引入有效期规则的权利,届时将提前至少 60 天通知。</li>
|
||||
<li><strong>价格变动。</strong>我们可随时调整积分价格,但调整不影响已购积分。</li>
|
||||
<li><strong>税费。</strong>您须自行承担所在地区购买积分时产生的适用税费。</li>
|
||||
<li><strong>免费额度。</strong>访客用户可享有有限次数的免费识别任务,免费额度可能随时变更,恕不另行通知。</li>
|
||||
<li><strong>拒付争议。</strong>在未事先联系我们的情况下发起拒付,可能导致账户被立即暂停。</li>
|
||||
</ul>
|
||||
|
||||
<h2>4. 用户内容</h2>
|
||||
<p>
|
||||
您对上传的文档、图片及文件("用户内容")保留全部知识产权。上传用户内容,即表示您授予 TexPixel
|
||||
有限的、非独占的、免版税的许可,以仅为提供服务之目的处理、存储和传输您的内容。
|
||||
</p>
|
||||
<ul>
|
||||
<li>未经您明确同意,我们不会使用您的用户内容训练 AI 模型。</li>
|
||||
<li>除运营服务所必需(如云存储服务商)外,我们不会将您的用户内容分享给第三方。</li>
|
||||
<li>您须确保有权上传并处理所提交的任何内容。</li>
|
||||
<li>请勿上传违法、侵犯第三方权利,或在未经当事人同意的情况下包含他人个人身份信息的内容。</li>
|
||||
</ul>
|
||||
|
||||
<h2>5. 识别结果免责声明</h2>
|
||||
<p>
|
||||
TexPixel 采用基于 AI 的识别技术。识别结果(LaTeX、Markdown、文本等)按"现状"提供。
|
||||
我们不保证结果的准确性、完整性或适用于特定用途。
|
||||
在将输出用于学术、专业或发表场景之前,您有责任自行核验所有结果。
|
||||
</p>
|
||||
|
||||
<h2>6. 禁止行为</h2>
|
||||
<p>您同意不得:</p>
|
||||
<ul>
|
||||
<li>使用本服务处理违法、淫秽、诽谤或侵犯第三方权利的内容。</li>
|
||||
<li>对服务进行逆向工程、反编译或尝试提取源代码。</li>
|
||||
<li>以超出套餐限额或影响其他用户体验的方式自动化发送请求。</li>
|
||||
<li>在未获得我们书面许可的情况下,利用本服务构建竞争性产品或服务。</li>
|
||||
<li>转让、出售或将账户或积分转移给他人。</li>
|
||||
<li>使用机器人、爬虫或自动化工具以超出 API 文档许可范围的方式访问服务。</li>
|
||||
<li>绕过我们为限制使用或执行本条款而采取的任何技术措施。</li>
|
||||
</ul>
|
||||
|
||||
<h2>7. 知识产权</h2>
|
||||
<p>
|
||||
本服务(包括软件、设计、商标及内容,但用户内容除外)归 TexPixel 所有,受适用知识产权法律保护。
|
||||
未经我们事先书面同意,您不得复制、修改、分发、出售或出租服务的任何部分。
|
||||
</p>
|
||||
|
||||
<h2>8. 第三方服务</h2>
|
||||
<p>
|
||||
本服务集成了第三方服务,包括 Google(用于 OAuth 登录)、云对象存储服务商(用于文件存储)及支付处理商。
|
||||
您对上述服务的使用同样受其各自条款和隐私政策约束。我们不对第三方服务的做法承担责任。
|
||||
</p>
|
||||
|
||||
<h2>9. 免责声明</h2>
|
||||
<p>
|
||||
本服务按"现状"和"可用状态"提供,不附带任何明示或暗示的保证,
|
||||
包括但不限于适销性、特定用途适用性及不侵权保证。
|
||||
我们不保证服务不中断、无错误或绝对安全。
|
||||
</p>
|
||||
|
||||
<h2>10. 责任限制</h2>
|
||||
<p>
|
||||
在法律允许的最大范围内,TexPixel 不对因您使用本服务而产生的任何间接、附带、特殊、
|
||||
后果性或惩罚性损害(包括数据丢失、利润损失或业务中断)承担责任,
|
||||
即便我们已被告知此类损害的可能性。
|
||||
</p>
|
||||
<p>
|
||||
我们就您使用本服务的任何索赔所承担的全部责任,不超过索赔发生前 12 个月内您向我们支付的金额。
|
||||
</p>
|
||||
|
||||
<h2>11. 账户终止</h2>
|
||||
<p>
|
||||
对于违反本条款或有害于其他用户、我们或第三方的行为,我们可随时暂停或注销相关账户,无需事先通知。
|
||||
您可随时通过联系我们删除账户。账户终止后,您使用本服务的权利立即终止。
|
||||
因违规而终止账户时,未使用的积分将被没收;主动注销账户时,剩余积分是否退款由我们自行决定。
|
||||
</p>
|
||||
|
||||
<h2>12. 赔偿</h2>
|
||||
<p>
|
||||
您同意就因您使用本服务、您的用户内容或您违反本条款而产生的任何索赔、责任、损害及费用
|
||||
(包括合理律师费),向 TexPixel 及其管理人员、董事、员工和代理人进行赔偿和辩护。
|
||||
</p>
|
||||
|
||||
<h2>13. 适用法律与争议解决</h2>
|
||||
<p>
|
||||
本条款受中华人民共和国法律管辖,不考虑法律冲突原则。
|
||||
因本条款引发的争议,双方应首先进行善意协商。
|
||||
若 30 天内未能解决,争议将提交仲裁或由 TexPixel 注册地的有管辖权的法院处理。
|
||||
</p>
|
||||
|
||||
<h2>14. 条款变更</h2>
|
||||
<p>
|
||||
我们可随时修改本条款。对于重大变更,我们将通过电子邮件或服务内显著通知的方式,
|
||||
在变更生效前至少 14 天提前告知您。变更生效后继续使用本服务,即表示您接受修订后的条款。
|
||||
</p>
|
||||
|
||||
<h2>15. 联系我们</h2>
|
||||
<p>
|
||||
如您对本条款有任何疑问,请通过{' '}
|
||||
<a href="mailto:legal@texpixel.com">legal@texpixel.com</a> 联系我们。
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -10,6 +10,11 @@ const DocsListPage = lazy(() => import('../pages/DocsListPage'));
|
||||
const DocDetailPage = lazy(() => import('../pages/DocDetailPage'));
|
||||
const BlogListPage = lazy(() => import('../pages/BlogListPage'));
|
||||
const BlogDetailPage = lazy(() => import('../pages/BlogDetailPage'));
|
||||
const TermsPage = lazy(() => import('../pages/TermsPage'));
|
||||
const PrivacyPage = lazy(() => import('../pages/PrivacyPage'));
|
||||
const CookiePolicyPage = lazy(() => import('../pages/CookiePolicyPage'));
|
||||
const AboutPage = lazy(() => import('../pages/AboutPage'));
|
||||
const ContactPage = lazy(() => import('../pages/ContactPage'));
|
||||
|
||||
function LoadingFallback() {
|
||||
return (
|
||||
@@ -29,6 +34,11 @@ export default function AppRouter() {
|
||||
<Route path="/docs/:slug" element={<DocDetailPage />} />
|
||||
<Route path="/blog" element={<BlogListPage />} />
|
||||
<Route path="/blog/:slug" element={<BlogDetailPage />} />
|
||||
<Route path="/terms" element={<TermsPage />} />
|
||||
<Route path="/privacy" element={<PrivacyPage />} />
|
||||
<Route path="/cookies" element={<CookiePolicyPage />} />
|
||||
<Route path="/about" element={<AboutPage />} />
|
||||
<Route path="/contact" element={<ContactPage />} />
|
||||
</Route>
|
||||
<Route element={<AppLayout />}>
|
||||
<Route path="/app" element={<WorkspacePage />} />
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user