Compare commits
2 Commits
3a0192518c
...
2ac783d222
| Author | SHA1 | Date | |
|---|---|---|---|
| 2ac783d222 | |||
| 941e8245f1 |
@@ -4,9 +4,9 @@
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
||||
<meta name="theme-color" content="#ED6956" />
|
||||
<meta name="description" content="تدریسینو — یادگیری ریاضی پایه سوم تا دوازدهم، قدمبهقدم و با دستیار هوشمند." />
|
||||
<meta name="description" content="تدریسینو ریاضی — یادگیری ریاضی پایه سوم تا دوازدهم، قدمبهقدم و با دستیار هوشمند." />
|
||||
<meta name="robots" content="noindex, nofollow" />
|
||||
<title>تدریسینو</title>
|
||||
<title>تدریسینو ریاضی</title>
|
||||
<!-- تنظیماتِ زمانِ اجرا (آدرسِ API)؛ پیش از خودِ اپ بار میشود. -->
|
||||
<script src="/config.js"></script>
|
||||
<!-- اسپلشِ ثابت: همینجا در HTML است تا از اولین فریم دیده شود (نه صفحهی سفید تا بار شدنِ JS)؛
|
||||
@@ -30,12 +30,12 @@
|
||||
.in-app .boot-splash > * { visibility: hidden; }
|
||||
.in-old-app .boot-splash span { display: none; }
|
||||
.in-old-app .boot-splash img { width: 160px; height: 160px; }
|
||||
.boot-splash span { font: 900 34px/1.2 system-ui, -apple-system, sans-serif; color: #1a1a2e; }
|
||||
.boot-splash span { font: 900 min(34px, 8.5vw)/1.2 system-ui, -apple-system, sans-serif; color: #1a1a2e; white-space: nowrap; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root">
|
||||
<div class="boot-splash" dir="rtl"><img src="/app-icon.png" alt="" width="144" height="144" /><span>تدریسینو</span></div>
|
||||
<div class="boot-splash" dir="rtl"><img src="/app-icon.png" alt="" width="144" height="144" /><span>تدریسینو ریاضی</span></div>
|
||||
</div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
|
||||
2
pay.html
2
pay.html
@@ -6,7 +6,7 @@
|
||||
<meta name="robots" content="noindex, nofollow" />
|
||||
<!-- Referer باید از دامنهی خودمان برود؛ زرینپال بیآن صفحه را باز نمیکند (src/pay.ts). -->
|
||||
<meta name="referrer" content="strict-origin-when-cross-origin" />
|
||||
<title>رفتن به درگاه — تدریسینو</title>
|
||||
<title>رفتن به درگاه — تدریسینو ریاضی</title>
|
||||
<script src="/config.js"></script>
|
||||
<style>
|
||||
body { margin: 0; min-height: 100vh; display: flex; align-items: center; justify-content: center; background: #f6f5fb; color: #1a1a2e; font-family: Vazirmatn, system-ui, sans-serif; }
|
||||
|
||||
@@ -7,7 +7,7 @@ export function BrandFooter() {
|
||||
<footer className="flex flex-col items-center gap-1 pb-4 pt-3 text-center">
|
||||
<BrandMark size={24} tone="dark" />
|
||||
<span className="text-[10px] text-muted">ریاضی، قدمبهقدم، از روی کتاب درسی</span>
|
||||
<span className="text-[10px] text-faint">© {toFa(1405)} تدریسینو · همه حقوق محفوظ است</span>
|
||||
<span className="text-[10px] text-faint">© {toFa(1405)} تدریسینو ریاضی · همه حقوق محفوظ است</span>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ export function BrandMark({ size = 28, tone = 'light', withName = true }: { size
|
||||
const c = useCompanion();
|
||||
const gid = useId();
|
||||
return (
|
||||
<span className="inline-flex items-center gap-1.5" aria-label="تدریسینو">
|
||||
<span className="inline-flex items-center gap-1.5" aria-label="تدریسینو ریاضی">
|
||||
<svg width={size} height={size} viewBox="0 0 32 32" aria-hidden="true">
|
||||
<defs>
|
||||
<linearGradient id={gid} x1="0" y1="0" x2="1" y2="1">
|
||||
@@ -25,7 +25,7 @@ export function BrandMark({ size = 28, tone = 'light', withName = true }: { size
|
||||
</text>
|
||||
<path className="spark" d="M25 4.5l.9 1.9 1.9.9-1.9.9-.9 1.9-.9-1.9-1.9-.9 1.9-.9z" fill="#FFD23F" />
|
||||
</svg>
|
||||
{withName && <span className="font-black tracking-tight" style={{ color: ink, fontSize: size * 0.62 }}>تدریسینو</span>}
|
||||
{withName && <span className="font-black tracking-tight" style={{ color: ink, fontSize: size * 0.62 }}>تدریسینو ریاضی</span>}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -33,7 +33,15 @@ const EDGE_END = /[\s،؛:!؟«»"'.=]+$/;
|
||||
// سمتِ خودشان میکشیدند، پس فهرست باز برعکس میشد.
|
||||
const LTR = { unicodeBidi: 'isolate-override', direction: 'ltr' } as const;
|
||||
|
||||
export function MathText({ text: raw }: { text: string }) {
|
||||
// فرمولِ «واقعی» (نه عددِ تنها): نمادِ رابطه یا عمل دارد و دستِکم چهار نویسه است.
|
||||
const FORMULA = /[=<>≤≥≠∈∉⊂⊆∪∩+×÷^√∑∫]|[0-9۰-۹A-Za-z)][\s]*[−\-/][\s]*[0-9۰-۹A-Za-z(]/;
|
||||
|
||||
/**
|
||||
* `display`: فرمولِ وسطِ جملهی فارسی در خطِ جدا و وسطچین مینشیند (سوالها و درسها)، تا
|
||||
* چشم وسطِ فرمول به خطِ بعد نیفتد. عددِ تنها و عبارتِ کوتاهِ بدونِ نمادِ عمل داخلِ جمله میماند.
|
||||
* آزموده نشده: صفحهی واقعیِ اپ در Chrome و Safari؛ فقط منطقِ تشخیص با نمونههای بانک سنجیده شد.
|
||||
*/
|
||||
export function MathText({ text: raw, display = false }: { text: string; display?: boolean }) {
|
||||
const text = plainPersian(plainMath(raw));
|
||||
const out: React.ReactNode[] = [];
|
||||
const depth = bracketDepth(text);
|
||||
@@ -56,7 +64,13 @@ export function MathText({ text: raw }: { text: string }) {
|
||||
run = edge.run;
|
||||
if (!run || !HAS_MATH.test(run)) continue;
|
||||
out.push(text.slice(last, start + head.length));
|
||||
const before = text.slice(0, start + head.length), after = text.slice(start + head.length + run.length);
|
||||
const inSentence = new RegExp(`[${LETTER}]`).test(before) || new RegExp(`[${LETTER}]`).test(after);
|
||||
if (display && inSentence && run.trim().length >= 4 && FORMULA.test(run) && depth[start + head.length] === 0) {
|
||||
out.push(<span key={i++} style={{ ...LTR, display: 'block', textAlign: 'center', margin: '4px 0', fontWeight: 800 }}>{run}</span>);
|
||||
} else {
|
||||
out.push(<span key={i++} style={LTR}>{run}</span>);
|
||||
}
|
||||
last = start + head.length + run.length;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,17 +28,17 @@ export function parseNumbered(text: string): { lead: string; items: string[] } |
|
||||
|
||||
export function StepText({ text, color, className = '' }: { text: string; color: string; className?: string }) {
|
||||
const list = parseNumbered(text);
|
||||
if (!list) return <p className={`whitespace-pre-line ${className}`}><MathText text={text} /></p>;
|
||||
if (!list) return <p className={`whitespace-pre-line ${className}`}><MathText text={text} display /></p>;
|
||||
return (
|
||||
<div className={className}>
|
||||
{list.lead && <p className="mb-2 whitespace-pre-line"><MathText text={list.lead} /></p>}
|
||||
{list.lead && <p className="mb-2 whitespace-pre-line"><MathText text={list.lead} display /></p>}
|
||||
<ol className="flex flex-col gap-2.5">
|
||||
{list.items.map((item, i) => (
|
||||
<li key={i} className="flex gap-2.5">
|
||||
<span className="mt-0.5 flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full text-xs font-black text-white" style={{ backgroundColor: color }}>
|
||||
{toFa(i + 1)}
|
||||
</span>
|
||||
<span className="flex-1 whitespace-pre-line"><MathText text={item} /></span>
|
||||
<span className="flex-1 whitespace-pre-line"><MathText text={item} display /></span>
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
|
||||
@@ -201,7 +201,7 @@ export const SEYAR_INFO: Record<SeyarChar, { name: string; symbol: string; role:
|
||||
sigma: { name: "سیگما", symbol: "Σ", role: "راهنمای آزمون و کوییز" },
|
||||
delta: { name: "دلتا", symbol: "Δ", role: "همتیمی تمرینهای سریع" },
|
||||
pi: { name: "پی", symbol: "π", role: "همراه دلگرمی" },
|
||||
tedi: { name: "امگا", symbol: "ω", role: "رفیق همیشگی تدریسینو" },
|
||||
tedi: { name: "امگا", symbol: "ω", role: "رفیق همیشگی تدریسینو ریاضی" },
|
||||
};
|
||||
|
||||
/** slugِ کاراکترِ سرور (sigma/delta/pi/tedi) را به یکی از همراهها نگاشت میکند. */
|
||||
|
||||
@@ -19,7 +19,7 @@ export function InviteScreen() {
|
||||
const text = 'با تدریسینو ریاضی رو قدمبهقدم یاد بگیر:';
|
||||
if (navigator.share) {
|
||||
try {
|
||||
await navigator.share({ title: 'تدریسینو', text, url });
|
||||
await navigator.share({ title: 'تدریسینو ریاضی', text, url });
|
||||
return;
|
||||
} catch {
|
||||
/* کاربر لغو کرد؛ میرویم سراغِ کپی */
|
||||
|
||||
@@ -81,7 +81,7 @@ export function LessonScreen({ lessonId, nodeId }: { lessonId: number; nodeId?:
|
||||
<span className="flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full text-xs font-black text-white" style={{ backgroundColor: color }}>
|
||||
{toFa(i + 1)}
|
||||
</span>
|
||||
<span className="flex-1 whitespace-pre-line"><MathText text={step} />{figs('step', i)}</span>
|
||||
<span className="flex-1 whitespace-pre-line"><MathText text={step} display />{figs('step', i)}</span>
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
|
||||
@@ -106,7 +106,7 @@ export function LoginScreen({ onDone, onCancel }: { onDone: () => void; onCancel
|
||||
)}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h1 className="text-xl font-black">ورود به تدریسینو</h1>
|
||||
<h1 className="text-balance text-xl font-black">ورود به تدریسینو ریاضی</h1>
|
||||
<p className="mt-1 text-sm text-white/75">
|
||||
{step === 'phone'
|
||||
? 'شماره موبایلت رو وارد کن'
|
||||
|
||||
@@ -8,7 +8,7 @@ const SLIDES: { char: SeyarChar; mood: SeyarMood; bg: string; sub: string; title
|
||||
{
|
||||
char: 'sigma', mood: 'happy', bg: '#4C5FD5',
|
||||
sub: 'از پایه سوم تا دوازدهم',
|
||||
title: 'به تدریسینو خوش اومدی!',
|
||||
title: 'به تدریسینو ریاضی خوش اومدی!',
|
||||
desc: 'من سیگمام. با هم ریاضی کتاب درسیت رو جزیره به جزیره جلو میبریم؛ هر جزیره چند درس کوتاه داره و کلی تمرین.',
|
||||
},
|
||||
{
|
||||
@@ -64,7 +64,7 @@ export function OnboardingScreen({ onDone }: { onDone: () => void }) {
|
||||
</div>
|
||||
<div>
|
||||
<p className="mb-1 text-sm font-medium text-white/70">{slide.sub}</p>
|
||||
<h1 className="mb-3 text-2xl font-black leading-snug">{slide.title}</h1>
|
||||
<h1 className="mb-3 text-balance text-2xl font-black leading-snug">{slide.title}</h1>
|
||||
<p className="text-sm leading-[1.9] text-white/85">{slide.desc}</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
@@ -148,7 +148,7 @@ function SubscriptionCard({ onOpen }: { onOpen: () => void }) {
|
||||
<div className="flex items-center gap-3">
|
||||
<span className={`glyph flex h-11 w-11 flex-shrink-0 items-center justify-center rounded-2xl ${s.active ? 'bg-white/20' : 'bg-[#FFF4D6] text-[#E0A200]'}`}><Crown size={20} /></span>
|
||||
<span className="min-w-0 flex-1">
|
||||
<span className={`block text-xs font-bold ${s.active ? 'text-white/80' : 'text-muted'}`}>اشتراک تدریسینو</span>
|
||||
<span className={`block text-xs font-bold ${s.active ? 'text-white/80' : 'text-muted'}`}>اشتراک تدریسینو ریاضی</span>
|
||||
<span className="block text-base font-black">{s.active ? (s.planTitle ?? 'فعال') : 'اشتراک نداری'}</span>
|
||||
</span>
|
||||
<span className="text-left">
|
||||
|
||||
@@ -345,7 +345,7 @@ export function QuizScreen({ lessonId, nodeId }: { lessonId?: number; nodeId?: s
|
||||
<div className="mb-2 flex items-center justify-between">
|
||||
<span className="text-xs font-medium text-muted">سوال {toFa(index + 1)}</span>
|
||||
</div>
|
||||
<h2 className="whitespace-pre-line text-base font-bold leading-8 text-ink"><MathText text={question.prompt} /></h2>
|
||||
<h2 className="whitespace-pre-line text-base font-bold leading-8 text-ink"><MathText text={question.prompt} display /></h2>
|
||||
{question.figure ? <div className="mt-3"><Diagram spec={question.figure} /></div> : null}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ export function SplashScreen() {
|
||||
return (
|
||||
<div className="boot-splash" dir="rtl">
|
||||
<img src="/app-icon.png" alt="" width={144} height={144} />
|
||||
<span>تدریسینو</span>
|
||||
<span>تدریسینو ریاضی</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ export function SubscriptionScreen() {
|
||||
<Screen>
|
||||
<div className="flex-1 overflow-y-auto">
|
||||
<ScreenHeader
|
||||
title={active ? 'تمدید اشتراک' : 'اشتراک تدریسینو'}
|
||||
title={active ? 'تمدید اشتراک' : 'اشتراک تدریسینو ریاضی'}
|
||||
subtitle="همه فصلها، قلب نامحدود"
|
||||
color="#ED6956"
|
||||
exact
|
||||
|
||||
Reference in New Issue
Block a user