diff --git a/src/components/ui.tsx b/src/components/ui.tsx
index b433ea8..b7cf2ff 100644
--- a/src/components/ui.tsx
+++ b/src/components/ui.tsx
@@ -122,7 +122,7 @@ export function Spinner({ size = 20, className = '' }: { size?: number; classNam
return (
diff --git a/src/dev/DiagramGallery.tsx b/src/dev/DiagramGallery.tsx
index c94b1c9..d7fb0f2 100644
--- a/src/dev/DiagramGallery.tsx
+++ b/src/dev/DiagramGallery.tsx
@@ -8,7 +8,7 @@ const TEXTS = [
'✅ جواب: رأس سهمی در (1, -1) است.',
'۴) ۲ - ۳ = -۱.',
'مجموع زاویههای داخلی = (5 - 2) × 180 = 3 × 180 = 540 درجه',
- 'بازهی [-2, 3) یعنی از −۲ تا ۳، خودِ ۳ نه.',
+ 'بازه [-2, 3) یعنی از −۲ تا ۳، خود ۳ نه.',
'sin 30° = 0.5 و cos 30° ≈ 0.87 است.',
'در کتاب ۲/۵ یعنی دو و نیم (اختیاری).',
];
diff --git a/src/dev/diagramSamples.ts b/src/dev/diagramSamples.ts
index b527585..781db3c 100644
--- a/src/dev/diagramSamples.ts
+++ b/src/dev/diagramSamples.ts
@@ -3,14 +3,14 @@ import type { DiagramSpec } from '@/diagram/types';
/** نمونههای دستنویس از هر نوع شکل، از روی مسئلههای واقعیِ کتابها. فقط برای گالری. */
export const SAMPLES: { title: string; spec: DiagramSpec }[] = [
{
- title: 'یازدهم — سهمی با ریشه، رأس، محورِ تقارن و عرض از مبدأ',
+ title: 'یازدهم — سهمی با ریشه، رأس، محور تقارن و عرض از مبدأ',
spec: { kind: 'plane', title: 'y = x² − 2x − 3', axes: true, points: {}, items: [
{ type: 'function', expr: 'x^2-2x-3', from: -2.5, to: 4.5, label: 'y = x² − 2x − 3', marks: ['roots', 'vertex', 'axis', 'yIntercept'] },
] },
},
{
- title: 'دهم — دایرهی مثلثاتی، سینوس و کسینوسِ ۳۰°',
- spec: { kind: 'plane', title: 'زاویهی ۳۰ درجه', axes: true, points: { O: [0, 0], A: [1, 0], P: { polar: [1, 30] }, H: { foot: ['P', 'O', 'A'] } }, items: [
+ title: 'دهم — دایره مثلثاتی، سینوس و کسینوس ۳۰°',
+ spec: { kind: 'plane', title: 'زاویه ۳۰ درجه', axes: true, points: { O: [0, 0], A: [1, 0], P: { polar: [1, 30] }, H: { foot: ['P', 'O', 'A'] } }, items: [
{ type: 'circle', center: 'O', r: 1 },
{ type: 'segment', from: 'O', to: 'P' },
{ type: 'segment', from: 'P', to: 'H', dashed: true, color: 'green', label: 'sin 30° = 0.5' },
@@ -20,7 +20,7 @@ export const SAMPLES: { title: string; spec: DiagramSpec }[] = [
] },
},
{
- title: 'نهم — مثلثِ قائمالزاویه، فیثاغورس',
+ title: 'نهم — مثلث قائمالزاویه، فیثاغورس',
spec: { kind: 'plane', title: 'وتر = ۵', axes: false, points: { A: [0, 0], B: [4, 0], C: [0, 3] }, items: [
{ type: 'polygon', points: ['A', 'B', 'C'] },
{ type: 'rightAngle', vertex: 'A', from: 'B', to: 'C' },
@@ -30,7 +30,7 @@ export const SAMPLES: { title: string; spec: DiagramSpec }[] = [
] },
},
{
- title: 'هشتم — خطوطِ موازی و مورب، زاویههای مساوی',
+ title: 'هشتم — خطوط موازی و مورب، زاویههای مساوی',
spec: { kind: 'plane', title: 'a ∥ b و d مورب', axes: false, points: { _a1: [0, 2], _a2: [6, 2], _b1: [0, 0], _b2: [6, 0], _d1: [1.2, -1], _d2: [4.8, 3], P: { intersect: [['_a1', '_a2'], ['_d1', '_d2']] }, Q: { intersect: [['_b1', '_b2'], ['_d1', '_d2']] } }, items: [
{ type: 'line', through: ['_a1', '_a2'], label: 'a' },
{ type: 'line', through: ['_b1', '_b2'], label: 'b' },
@@ -40,8 +40,8 @@ export const SAMPLES: { title: string; spec: DiagramSpec }[] = [
] },
},
{
- title: 'هشتم — زاویهی محاطی و مرکزیِ روبهروی یک کمان',
- spec: { kind: 'plane', title: 'زاویهی محاطی نصفِ مرکزی است', axes: false, points: { O: [0, 0], A: { polar: [2, 200] }, B: { polar: [2, -20] }, C: { polar: [2, 100] } }, items: [
+ title: 'هشتم — زاویه محاطی و مرکزی روبهروی یک کمان',
+ spec: { kind: 'plane', title: 'زاویه محاطی نصف مرکزی است', axes: false, points: { O: [0, 0], A: { polar: [2, 200] }, B: { polar: [2, -20] }, C: { polar: [2, 100] } }, items: [
{ type: 'circle', center: 'O', r: 2 },
{ type: 'arc', center: 'O', r: 2, start: 200, end: 340, label: 'AB' },
{ type: 'segment', from: 'C', to: 'A' }, { type: 'segment', from: 'C', to: 'B' },
@@ -51,8 +51,8 @@ export const SAMPLES: { title: string; spec: DiagramSpec }[] = [
] },
},
{
- title: 'نهم — متوازیالاضلاع و وسطِ ضلعها، ضلعهای مساوی',
- spec: { kind: 'plane', title: 'M و N وسطِ ضلعها', axes: false, points: { A: [0, 0], B: [4, 0], C: [5, 2.5], D: [1, 2.5], M: { mid: ['A', 'B'] }, N: { mid: ['C', 'D'] } }, items: [
+ title: 'نهم — متوازیالاضلاع و وسط ضلعها، ضلعهای مساوی',
+ spec: { kind: 'plane', title: 'M و N وسط ضلعها', axes: false, points: { A: [0, 0], B: [4, 0], C: [5, 2.5], D: [1, 2.5], M: { mid: ['A', 'B'] }, N: { mid: ['C', 'D'] } }, items: [
{ type: 'polygon', points: ['A', 'B', 'C', 'D'] },
{ type: 'segment', from: 'A', to: 'M', ticks: 1 }, { type: 'segment', from: 'M', to: 'B', ticks: 1 },
{ type: 'segment', from: 'D', to: 'N', ticks: 2 }, { type: 'segment', from: 'N', to: 'C', ticks: 2 },
@@ -60,14 +60,14 @@ export const SAMPLES: { title: string; spec: DiagramSpec }[] = [
] },
},
{
- title: 'هفتم — دورانِ ۹۰° مثلث حولِ C در جهتِ عقربهها، روی شبکه',
- spec: { kind: 'plane', title: 'دورانِ ۹۰°', axes: false, grid: true, points: { A: [1, 1], B: [4, 1], C: [1, 3] }, items: [
+ title: 'هفتم — دوران ۹۰° مثلث حول C در جهت عقربهها، روی شبکه',
+ spec: { kind: 'plane', title: 'دوران ۹۰°', axes: false, grid: true, points: { A: [1, 1], B: [4, 1], C: [1, 3] }, items: [
{ type: 'polygon', points: ['A', 'B', 'C'] },
{ type: 'image', of: ['A', 'B', 'C'], transform: { rotate: { center: 'C', deg: -90 } } },
] },
},
{
- title: 'هشتم — بردار و جمعِ بردارها روی شبکه',
+ title: 'هشتم — بردار و جمع بردارها روی شبکه',
spec: { kind: 'plane', title: 'AB + BC = AC', axes: false, grid: true, points: { A: [0, 0], B: [3, 1], C: [4, 4] }, items: [
{ type: 'vector', from: 'A', to: 'B', label: 'a' },
{ type: 'vector', from: 'B', to: 'C', label: 'b', color: 'blue' },
@@ -75,7 +75,7 @@ export const SAMPLES: { title: string; spec: DiagramSpec }[] = [
] },
},
{
- title: 'نهم — دو خط و محلِ برخورد',
+ title: 'نهم — دو خط و محل برخورد',
spec: { kind: 'plane', title: 'y = 2x − 1 و y = −x + 5', axes: true, points: { P: { intersect: [[[0, -1], [1, 1]], [[0, 5], [1, 4]]] } }, items: [
{ type: 'function', expr: '2x-1', from: -1, to: 4, label: 'y = 2x − 1' },
{ type: 'function', expr: '-x+5', from: -1, to: 6, label: 'y = −x + 5' },
@@ -83,21 +83,21 @@ export const SAMPLES: { title: string; spec: DiagramSpec }[] = [
] },
},
{
- title: 'یازدهم — نمودارِ سینوس با مضربهای π',
+ title: 'یازدهم — نمودار سینوس با مضربهای π',
spec: { kind: 'plane', title: 'y = sin x', axes: true, trig: true, points: {}, items: [
{ type: 'function', expr: 'sin(x)', from: -6.2832, to: 6.2832, label: 'y = sin x', marks: ['roots', 'extrema'] },
{ type: 'hline', y: 0.5, color: 'orange', label: 'y = ½' },
] },
},
{
- title: 'یازدهم — تابعِ چندضابطهای و حد (نقطهی توخالی)',
- spec: { kind: 'plane', title: 'حدِ چپ و راست در x = 1', axes: true, points: {}, items: [
+ title: 'یازدهم — تابع چندضابطهای و حد (نقطه توخالی)',
+ spec: { kind: 'plane', title: 'حد چپ و راست در x = 1', axes: true, points: {}, items: [
{ type: 'piecewise', pieces: [{ expr: 'x+1', from: -2, to: 1, closedFrom: true, closedTo: false }, { expr: '-x+4', from: 1, to: 4, closedFrom: true, closedTo: true }] },
{ type: 'vline', x: 1, label: 'x = 1' },
] },
},
{
- title: 'دوازدهم — خطِ مماس بر نمودار',
+ title: 'دوازدهم — خط مماس بر نمودار',
spec: { kind: 'plane', title: 'مماس بر y = x² در x = 1', axes: true, points: {}, items: [
{ type: 'function', expr: 'x^2', from: -2, to: 2.5, label: 'y = x²' },
{ type: 'tangent', expr: 'x^2', at: 1, label: 'y = 2x − 1' },
@@ -105,14 +105,14 @@ export const SAMPLES: { title: string; spec: DiagramSpec }[] = [
},
{
title: 'دوازدهم — مجانبها: y = 1/(x − 2) + 1',
- spec: { kind: 'plane', title: 'مجانبِ قائم و افقی', axes: true, xRange: [-2, 6], yRange: [-4, 6], points: {}, items: [
+ spec: { kind: 'plane', title: 'مجانب قائم و افقی', axes: true, xRange: [-2, 6], yRange: [-4, 6], points: {}, items: [
{ type: 'function', expr: '1/(x-2)+1', from: -2, to: 6, label: 'y = 1/(x−2) + 1' },
{ type: 'vline', x: 2, label: 'x = 2', color: 'red' },
{ type: 'hline', y: 1, label: 'y = 1', color: 'red' },
] },
},
{
- title: 'دوازدهم — اکسترممهای نسبیِ تابعِ درجهی سوم',
+ title: 'دوازدهم — اکسترممهای نسبی تابع درجه سوم',
spec: { kind: 'plane', title: 'y = x³ − 3x', axes: true, points: {}, items: [
{ type: 'function', expr: 'x^3-3x', from: -2.3, to: 2.3, label: 'y = x³ − 3x', marks: ['extrema', 'roots'] },
] },
@@ -126,20 +126,20 @@ export const SAMPLES: { title: string; spec: DiagramSpec }[] = [
] },
},
{
- title: 'دوازدهم — مساحتِ زیرِ نمودار',
- spec: { kind: 'plane', title: 'ناحیهی بینِ y = √x و محورِ x', axes: true, points: {}, items: [
+ title: 'دوازدهم — مساحت زیر نمودار',
+ spec: { kind: 'plane', title: 'ناحیه بین y = √x و محور x', axes: true, points: {}, items: [
{ type: 'area', expr: 'sqrt(x)', from: 0, to: 4, color: 'green', label: 'S' },
{ type: 'function', expr: 'sqrt(x)', from: 0, to: 5, label: 'y = √x', color: 'green' },
] },
},
{
- title: 'دهم — ناحیهی جوابِ نامعادله',
+ title: 'دهم — ناحیه جواب نامعادله',
spec: { kind: 'plane', title: 'y > 2x − 1', axes: true, xRange: [-3, 3], yRange: [-4, 4], points: {}, items: [
{ type: 'region', expr: '2x-1', side: 'above', strict: true, label: 'y > 2x − 1' },
] },
},
{
- title: 'هشتم — محورِ اعداد: از ۲ سه واحد به چپ',
+ title: 'هشتم — محور اعداد: از ۲ سه واحد به چپ',
spec: { kind: 'numberline', title: '۲ − ۳ = −۱', min: -4, max: 4, points: [{ at: 2, label: 'شروع' }, { at: -1, label: 'پایان' }], jumps: [{ from: 2, to: -1, label: '۳ واحد به چپ' }] },
},
{
@@ -162,7 +162,7 @@ export const SAMPLES: { title: string; spec: DiagramSpec }[] = [
spec: { kind: 'grid', title: '۲۰ درصد', rows: 5, cols: 5, shaded: 5 },
},
{
- title: 'سوم — مدلِ ضرب: ۳ گروهِ ۴تایی',
+ title: 'سوم — مدل ضرب: ۳ گروه ۴تایی',
spec: { kind: 'groups', title: '۳ × ۴', groups: 3, each: 4, item: 'circle' },
},
{
@@ -170,23 +170,23 @@ export const SAMPLES: { title: string; spec: DiagramSpec }[] = [
spec: { kind: 'clock', title: 'ساعت ۳:۲۵', hour: 3, minute: 25 },
},
{
- title: 'سوم — نمودارِ ستونیِ آبمیوهها',
- spec: { kind: 'bar', title: 'آبمیوهی موردِ علاقه', labels: ['پرتقال', 'سیب', 'انگور', 'هلو'], values: [8, 5, 3, 4], unit: 'نفر' },
+ title: 'سوم — نمودار ستونی آبمیوهها',
+ spec: { kind: 'bar', title: 'آبمیوه مورد علاقه', labels: ['پرتقال', 'سیب', 'انگور', 'هلو'], values: [8, 5, 3, 4], unit: 'نفر' },
},
{
- title: 'هشتم — نمودارِ دایرهای',
- spec: { kind: 'pie', title: 'ورزشِ موردِ علاقه', labels: ['فوتبال', 'والیبال', 'شنا', 'بقیه'], values: [12, 6, 4, 2] },
+ title: 'هشتم — نمودار دایرهای',
+ spec: { kind: 'pie', title: 'ورزش مورد علاقه', labels: ['فوتبال', 'والیبال', 'شنا', 'بقیه'], values: [12, 6, 4, 2] },
},
{
- title: 'دهم — دمای زمین بر حسبِ عمق (خطشکسته)',
+ title: 'دهم — دمای زمین بر حسب عمق (خطشکسته)',
spec: { kind: 'linechart', title: 'دما (°C)', labels: ['1', '2', '3', '4', '5', '6'], values: [55, 90, 125, 160, 195, 230], unit: '°C' },
},
{
- title: 'دهم — جدولِ تعیینِ علامت',
+ title: 'دهم — جدول تعیین علامت',
spec: { kind: 'table', title: 'y = (x + 1)(x − 2)', headers: ['x', '−∞', '−1', '', '2', '+∞'], rows: [['y', '+', '0', '−', '0', '+']] },
},
{
- title: 'دهم — نمودارِ ون: A ∩ B',
+ title: 'دهم — نمودار ون: A ∩ B',
spec: { kind: 'venn', title: 'A = {1,2,3,4} و B = {3,4,5}', sets: ['A', 'B'], regions: { A: ['1', '2'], AB: ['3', '4'], B: ['5'], out: ['6'] }, shade: ['AB'] },
},
{
@@ -194,11 +194,11 @@ export const SAMPLES: { title: string; spec: DiagramSpec }[] = [
spec: { kind: 'venn', title: 'A − (B ∪ C)', sets: ['A', 'B', 'C'], regions: { A: ['1'], B: ['2'], C: ['3'], AB: ['4'], ABC: ['5'] }, shade: ['A'] },
},
{
- title: 'دهم — نمودارِ پیکانیِ تابع',
+ title: 'دهم — نمودار پیکانی تابع',
spec: { kind: 'mapping', title: 'f(x) = 3(x − 1)²', left: ['0', '0.5', '1'], right: ['3', '0.75', '0'], arrows: [[0, 0], [1, 1], [2, 2]], leftLabel: 'دامنه', rightLabel: 'برد' },
},
{
- title: 'نهم — نمودارِ درختیِ دو بار پرتابِ سکه',
+ title: 'نهم — نمودار درختی دو بار پرتاب سکه',
spec: { kind: 'tree', title: '۴ حالت', root: { label: 'شروع', children: [
{ label: 'رو', p: '1/2', children: [{ label: 'رو', p: '1/2' }, { label: 'پشت', p: '1/2' }] },
{ label: 'پشت', p: '1/2', children: [{ label: 'رو', p: '1/2' }, { label: 'پشت', p: '1/2' }] },
@@ -206,42 +206,42 @@ export const SAMPLES: { title: string; spec: DiagramSpec }[] = [
},
{ title: 'هفتم — مکعبمستطیل', spec: { kind: 'solid', title: 'حجم = ۵ × ۳ × ۴', shape: 'cuboid', dims: { a: 5, b: 3, c: 4 }, labels: { a: '5 cm', b: '3 cm', c: '4 cm' } } },
{ title: 'هفتم — مکعب', spec: { kind: 'solid', title: 'مکعب', shape: 'cube', dims: { a: 3 }, labels: { a: 'a' } } },
- { title: 'هفتم — منشورِ سهپهلو', spec: { kind: 'solid', title: 'منشورِ مثلثی', shape: 'prism', dims: { n: 3, r: 1.2, h: 2 }, labels: { h: 'h' } } },
- { title: 'نهم — منشورِ ششپهلو', spec: { kind: 'solid', title: 'منشورِ ششضلعی', shape: 'prism', dims: { n: 6, r: 1.2, h: 1.6 }, labels: { h: '8 cm' } } },
- { title: 'نهم — هرمِ مربعالقاعده با ارتفاع', spec: { kind: 'solid', title: 'هرم', shape: 'pyramid', dims: { n: 4, r: 1.3, h: 2 }, labels: { h: 'h', a: 'a' } } },
- { title: 'نهم — هرمِ پنجپهلو', spec: { kind: 'solid', title: 'قاعدهی ABCDE', shape: 'pyramid', dims: { n: 5, r: 1.3, h: 2 }, labels: { h: 'OH' } } },
- { title: 'نهم — استوانه', spec: { kind: 'solid', title: 'لیوانِ استوانهای', shape: 'cylinder', dims: { r: 6, h: 10 }, labels: { r: '6 cm', h: 'h' } } },
- { title: 'نهم — مخروط', spec: { kind: 'solid', title: 'ظرفِ مخروطی', shape: 'cone', dims: { r: 4, h: 12 }, labels: { r: '4 cm', h: '12 cm' } } },
+ { title: 'هفتم — منشور سهپهلو', spec: { kind: 'solid', title: 'منشور مثلثی', shape: 'prism', dims: { n: 3, r: 1.2, h: 2 }, labels: { h: 'h' } } },
+ { title: 'نهم — منشور ششپهلو', spec: { kind: 'solid', title: 'منشور ششضلعی', shape: 'prism', dims: { n: 6, r: 1.2, h: 1.6 }, labels: { h: '8 cm' } } },
+ { title: 'نهم — هرم مربعالقاعده با ارتفاع', spec: { kind: 'solid', title: 'هرم', shape: 'pyramid', dims: { n: 4, r: 1.3, h: 2 }, labels: { h: 'h', a: 'a' } } },
+ { title: 'نهم — هرم پنجپهلو', spec: { kind: 'solid', title: 'قاعده ABCDE', shape: 'pyramid', dims: { n: 5, r: 1.3, h: 2 }, labels: { h: 'OH' } } },
+ { title: 'نهم — استوانه', spec: { kind: 'solid', title: 'لیوان استوانهای', shape: 'cylinder', dims: { r: 6, h: 10 }, labels: { r: '6 cm', h: 'h' } } },
+ { title: 'نهم — مخروط', spec: { kind: 'solid', title: 'ظرف مخروطی', shape: 'cone', dims: { r: 4, h: 12 }, labels: { r: '4 cm', h: '12 cm' } } },
{ title: 'نهم — کره', spec: { kind: 'solid', title: 'کره', shape: 'sphere', dims: { r: 1 }, labels: { r: 'r' } } },
- { title: 'نهم — نیمکره', spec: { kind: 'solid', title: 'پیمانهی نیمکره', shape: 'hemisphere', dims: { r: 12 }, labels: { r: '12 cm' } } },
- { title: 'ششم — مقایسهی یکپنجم و نُهدهم', spec: { kind: 'fraction', title: 'کدام بزرگتر است؟', shape: 'rect', parts: 5, shaded: 1, compare: [{ parts: 5, shaded: 1, label: 'یکپنجم' }, { parts: 10, shaded: 9, label: 'نُهدهم' }] } },
+ { title: 'نهم — نیمکره', spec: { kind: 'solid', title: 'پیمانه نیمکره', shape: 'hemisphere', dims: { r: 12 }, labels: { r: '12 cm' } } },
+ { title: 'ششم — مقایسه یکپنجم و نهدهم', spec: { kind: 'fraction', title: 'کدام بزرگتر است؟', shape: 'rect', parts: 5, shaded: 1, compare: [{ parts: 5, shaded: 1, label: 'یکپنجم' }, { parts: 10, shaded: 9, label: 'نهدهم' }] } },
{ title: 'ششم — دو و هفتدهم', spec: { kind: 'fraction', title: '۲٫۷', shape: 'circle', parts: 10, shaded: 7, wholes: 2 } },
- { title: 'دهم — دایره و خطِ مماس از معادله', spec: { kind: 'plane', title: 'فاصلهی مرکز تا خط', axes: true, points: { O: [-1, 2] }, items: [
+ { title: 'دهم — دایره و خط مماس از معادله', spec: { kind: 'plane', title: 'فاصله مرکز تا خط', axes: true, points: { O: [-1, 2] }, items: [
{ type: 'line', eq: [3, 5, 4], label: '3x + 5y = 4' }, { type: 'circle', center: 'O', r: 0.5145, color: 'green' },
] } },
- { title: 'هشتم — زاویهی خارجیِ پنجضلعی با امتدادِ ضلع', spec: { kind: 'plane', title: 'زاویهی خارجی = ۷۲°', axes: false, points: { A: { polar: [2, 234] }, B: { polar: [2, 306] }, C: { polar: [2, 18] }, D: { polar: [2, 90] }, E: { polar: [2, 162] }, X: { along: ['A', 'B', 1.6] } }, items: [
+ { title: 'هشتم — زاویه خارجی پنجضلعی با امتداد ضلع', spec: { kind: 'plane', title: 'زاویه خارجی = ۷۲°', axes: false, points: { A: { polar: [2, 234] }, B: { polar: [2, 306] }, C: { polar: [2, 18] }, D: { polar: [2, 90] }, E: { polar: [2, 162] }, X: { along: ['A', 'B', 1.6] } }, items: [
{ type: 'polygon', points: ['A', 'B', 'C', 'D', 'E'] }, { type: 'segment', from: 'B', to: 'X', dashed: true }, { type: 'angle', vertex: 'B', from: 'X', to: 'C', label: '72°' },
] } },
- { title: 'سوم — ضرب روی محورِ اعداد (تیکهای زیاد)', spec: { kind: 'numberline', title: '۲ × ۱۱', min: 0, max: 25, points: [{ at: 11, label: '' }, { at: 22, label: '' }], jumps: [{ from: 0, to: 11, label: '۱۱' }, { from: 11, to: 22, label: '۱۱' }] } },
- { title: 'نهم — مثلث از روی داده: AB = 6، AC = 8، زاویهی A قائمه', spec: { kind: 'plane', title: 'فیثاغورس', axes: false, points: {}, items: [
+ { title: 'سوم — ضرب روی محور اعداد (تیکهای زیاد)', spec: { kind: 'numberline', title: '۲ × ۱۱', min: 0, max: 25, points: [{ at: 11, label: '' }, { at: 22, label: '' }], jumps: [{ from: 0, to: 11, label: '۱۱' }, { from: 11, to: 22, label: '۱۱' }] } },
+ { title: 'نهم — مثلث از روی داده: AB = 6، AC = 8، زاویه A قائمه', spec: { kind: 'plane', title: 'فیثاغورس', axes: false, points: {}, items: [
{ type: 'triangle', names: ['A', 'B', 'C'], sides: { AB: 6, AC: 8 }, angles: { A: 90 }, labels: { BC: 'x' } },
] } },
- { title: 'هشتم — مثلث با سه ضلع ۵، ۷، ۹ و ارتفاع از C', spec: { kind: 'plane', title: 'ارتفاعِ وارد بر AB', axes: false, points: {}, items: [
+ { title: 'هشتم — مثلث با سه ضلع ۵، ۷، ۹ و ارتفاع از C', spec: { kind: 'plane', title: 'ارتفاع وارد بر AB', axes: false, points: {}, items: [
{ type: 'triangle', names: ['A', 'B', 'C'], sides: { AB: 9, BC: 7, CA: 5 }, altitude: 'C', labels: { h: 'h' } },
] } },
- { title: 'هفتم — مثلث با دو زاویه (۴۰° و ۷۵°)', spec: { kind: 'plane', title: 'زاویهی سوم؟', axes: false, points: {}, items: [
+ { title: 'هفتم — مثلث با دو زاویه (۴۰° و ۷۵°)', spec: { kind: 'plane', title: 'زاویه سوم؟', axes: false, points: {}, items: [
{ type: 'triangle', names: ['A', 'B', 'C'], angles: { A: 40, B: 75 }, labels: { C: '?' } },
] } },
- { title: 'منفرجه — ارتفاع بیرونِ مثلث', spec: { kind: 'plane', title: 'زاویهی B = ۱۲۰°', axes: false, points: {}, items: [
+ { title: 'منفرجه — ارتفاع بیرون مثلث', spec: { kind: 'plane', title: 'زاویه B = ۱۲۰°', axes: false, points: {}, items: [
{ type: 'triangle', names: ['A', 'B', 'C'], sides: { AB: 4, BC: 3 }, angles: { B: 120 }, altitude: 'C' },
] } },
- { title: 'ششم — مستطیلِ باغچه ۸ × ۵', spec: { kind: 'plane', title: 'باغچه', axes: false, points: {}, items: [{ type: 'quad', shape: 'rectangle', names: ['A', 'B', 'C', 'D'], w: 8, h: 5 }] } },
- { title: 'مربع با ضلعِ ۴', spec: { kind: 'plane', title: 'مربع', axes: false, points: {}, items: [{ type: 'quad', shape: 'square', names: ['A', 'B', 'C', 'D'], side: 4 }] } },
+ { title: 'ششم — مستطیل باغچه ۸ × ۵', spec: { kind: 'plane', title: 'باغچه', axes: false, points: {}, items: [{ type: 'quad', shape: 'rectangle', names: ['A', 'B', 'C', 'D'], w: 8, h: 5 }] } },
+ { title: 'مربع با ضلع ۴', spec: { kind: 'plane', title: 'مربع', axes: false, points: {}, items: [{ type: 'quad', shape: 'square', names: ['A', 'B', 'C', 'D'], side: 4 }] } },
{ title: 'ششم — متوازیالاضلاع: قاعده ۷، ارتفاع ۴', spec: { kind: 'plane', title: 'مساحت = قاعده × ارتفاع', axes: false, points: {}, items: [{ type: 'quad', shape: 'parallelogram', names: ['A', 'B', 'C', 'D'], w: 7, h: 4 }] } },
- { title: 'هفتم — لوزی با قطرهای ۸ و ۶', spec: { kind: 'plane', title: 'مساحت = نصفِ حاصلضربِ قطرها', axes: false, points: {}, items: [{ type: 'quad', shape: 'rhombus', names: ['A', 'B', 'C', 'D'], d1: 8, d2: 6 }] } },
+ { title: 'هفتم — لوزی با قطرهای ۸ و ۶', spec: { kind: 'plane', title: 'مساحت = نصف حاصلضرب قطرها', axes: false, points: {}, items: [{ type: 'quad', shape: 'rhombus', names: ['A', 'B', 'C', 'D'], d1: 8, d2: 6 }] } },
{ title: 'هشتم — ذوزنقه: قاعدههای ۱۰ و ۶، ارتفاع ۴', spec: { kind: 'plane', title: 'ذوزنقه', axes: false, points: {}, items: [{ type: 'quad', shape: 'trapezoid', names: ['A', 'B', 'C', 'D'], w: 10, top: 6, h: 4 }] } },
- { title: 'ذوزنقهی قائمالزاویه', spec: { kind: 'plane', title: 'ذوزنقهی قائم', axes: false, points: {}, items: [{ type: 'quad', shape: 'trapezoid', names: ['A', 'B', 'C', 'D'], w: 8, top: 5, h: 4, right: true }] } },
- { title: 'هفتم — الگوی مربعی ۱، ۴، ۹ و شکلِ بعدی', spec: { kind: 'pattern', title: 'الگوی n²', layout: 'square', unit: 'square', stages: [1, 2, 3], next: true } },
+ { title: 'ذوزنقه قائمالزاویه', spec: { kind: 'plane', title: 'ذوزنقه قائم', axes: false, points: {}, items: [{ type: 'quad', shape: 'trapezoid', names: ['A', 'B', 'C', 'D'], w: 8, top: 5, h: 4, right: true }] } },
+ { title: 'هفتم — الگوی مربعی ۱، ۴، ۹ و شکل بعدی', spec: { kind: 'pattern', title: 'الگوی n²', layout: 'square', unit: 'square', stages: [1, 2, 3], next: true } },
{ title: 'هفتم — الگوی چوبکبریت', spec: { kind: 'pattern', title: 'چوبکبریت: 3n + 1', layout: 'row', unit: 'stick', stages: [1, 2, 3, 4], next: true } },
{ title: 'دهم — عددهای مثلثی با نقطه', spec: { kind: 'pattern', title: '۱، ۳، ۶، ۱۰', layout: 'triangle', unit: 'dot', stages: [1, 2, 3, 4] } },
];
diff --git a/src/diagram/expr.ts b/src/diagram/expr.ts
index 3617b60..edb9f39 100644
--- a/src/diagram/expr.ts
+++ b/src/diagram/expr.ts
@@ -32,11 +32,11 @@ function tokenize(src: string): string[] {
if (w === 'x' || w === 'pi' || w === 'e' || w in FUNCS) { out.push(w); i += w.length; continue; }
// «xsin» یا «pix»: نامِ چسبیده را تکهتکه کن
const known = ['sqrt', 'sin', 'cos', 'tan', 'abs', 'log', 'ln', 'pi', 'x', 'e'].find((k) => w.startsWith(k));
- if (!known) throw new Error(`نامِ ناشناخته: ${w}`);
+ if (!known) throw new Error(`نام ناشناخته: ${w}`);
out.push(known); i += known.length; continue;
}
if ('+-*/^()'.includes(s[i])) { out.push(s[i]); i++; continue; }
- throw new Error(`نویسهی ناشناخته: ${s[i]}`);
+ throw new Error(`نویسه ناشناخته: ${s[i]}`);
}
return out;
}
@@ -48,7 +48,7 @@ export function parseExpr(src: string): (x: number) => number {
const peek = () => t[p];
const eat = (tok?: string) => {
const v = t[p++];
- if (tok !== undefined && v !== tok) throw new Error(`انتظارِ ${tok}`);
+ if (tok !== undefined && v !== tok) throw new Error(`انتظار ${tok}`);
return v;
};
const startsAtom = (tok: string | undefined) => tok !== undefined && (/^[\d.]/.test(tok) || tok === 'x' || tok === 'pi' || tok === 'e' || tok === '(' || tok in FUNCS);
diff --git a/src/diagram/misc.tsx b/src/diagram/misc.tsx
index 9827d8e..5c56422 100644
--- a/src/diagram/misc.tsx
+++ b/src/diagram/misc.tsx
@@ -74,7 +74,7 @@ function MixedNumber({ wholes, parts, shaded, shape }: { wholes: number; parts:
:
))}
{parts === 1 ? : Array.from({ length: parts }, (_, i) => slice(cx(wholes), i, false))}
- {`${toFa(wholes)} واحدِ کامل و ${toFa(shaded)} تکه از ${toFa(parts)}`}
+ {`${toFa(wholes)} واحد کامل و ${toFa(shaded)} تکه از ${toFa(parts)}`}
);
@@ -268,7 +268,7 @@ export function Pattern({ spec }: { spec: PatternSpec }) {
?
:
;
})}
-
{`شکلِ ${toFa(i + 1)}`}
+
{`شکل ${toFa(i + 1)}`}
{spec.counts !== false &&
{`${toFa(count)} ${sticks ? 'چوب' : 'تا'}`} }
);
@@ -281,7 +281,7 @@ export function Pattern({ spec }: { spec: PatternSpec }) {
?
- {`شکلِ ${toFa(stages.length + 1)}`}
+ {`شکل ${toFa(stages.length + 1)}`}
)}
diff --git a/src/diagram/numberline.tsx b/src/diagram/numberline.tsx
index b452914..d3e7a8f 100644
--- a/src/diagram/numberline.tsx
+++ b/src/diagram/numberline.tsx
@@ -43,7 +43,7 @@ export function NumberLine({ spec }: { spec: NumberLineSpec }) {
};
return (
-
+
color(iv.color, COLORS[SERIES[i % SERIES.length]]))]} />
diff --git a/src/lib/MathText.tsx b/src/lib/MathText.tsx
index e3a9480..03467bd 100644
--- a/src/lib/MathText.tsx
+++ b/src/lib/MathText.tsx
@@ -1,5 +1,5 @@
import { Fragment } from 'react';
-import { plainMath } from './plainMath';
+import { plainMath, plainPersian } from './plainMath';
/**
* متنِ فارسی که ریاضی لایش است («عدد −۱ میشود»، «رأس (۱، −۴) است»).
@@ -12,7 +12,7 @@ import { plainMath } from './plainMath';
* فهرستِ داخلِ جمله («از −۳ به −۲، −۱، ۰ میرسیم»، «الگویِ ۲، ۵، ۸، …») مالِ جمله است و
* از راست خوانده میشود؛ پس «،» و «؛» بیرونِ پرانتز و آکولاد تکه را میشکنند و هر عضو
* جدا قفل میشود. داخلِ `{۱، ۲، ۳}` و `(۱، −۴)` ریاضی است و یکجا قفل میماند. سنجیده
- * با مختصاتِ رسمشده در Chrome روی همهی سؤالها و گزینههای بانک و چند جوابِ دستیار
+ * با مختصاتِ رسمشده در Chrome روی همهی سوالها و گزینههای بانک و چند جوابِ دستیار
* (۲۰۲۶-۰۹-۲۹)؛ پیش از آن هر ۴۸۲ فهرستِ داخلِ جمله برعکس دیده میشد.
*
* آزموده روی Chrome (دسکتاپ و موبایلِ شبیهسازیشده)؛ روی Safariِ واقعیِ آیفون دیده نشده.
@@ -34,7 +34,7 @@ const EDGE_END = /[\s،؛:!؟«»"'.=]+$/;
const LTR = { unicodeBidi: 'isolate-override', direction: 'ltr' } as const;
export function MathText({ text: raw }: { text: string }) {
- const text = plainMath(raw);
+ const text = plainPersian(plainMath(raw));
const out: React.ReactNode[] = [];
const depth = bracketDepth(text);
let last = 0, i = 0;
diff --git a/src/lib/paymentMethods.ts b/src/lib/paymentMethods.ts
new file mode 100644
index 0000000..a78db52
--- /dev/null
+++ b/src/lib/paymentMethods.ts
@@ -0,0 +1,30 @@
+import { inAndroidApp } from './bazaar';
+
+export type PaymentMethod = 'zarinpal' | 'bazaar';
+
+const KNOWN: PaymentMethod[] = ['zarinpal', 'bazaar'];
+
+/**
+ * پیشفرض: در مرورگر فقط زرینپال (خریدِ بازار بیرون از اپِ اندروید ممکن نیست)، داخلِ اپ هر دو، زرینپال اول.
+ * برای نسخهای که فقط در کافهبازار منتشر میشود، `app: ["bazaar"]` در dist/config.js؛ build لازم نیست.
+ */
+const DEFAULTS: Record<'web' | 'app', PaymentMethod[]> = {
+ web: ['zarinpal'],
+ app: ['zarinpal', 'bazaar'],
+};
+
+/**
+ * روشهای پرداختِ اینجا (مرورگر یا اپ)، به ترتیبِ نمایش. نامِ ناشناخته در config.js نادیده گرفته نمیشود که
+ * بیصدا یک روش گم شود: در کنسول خطا میدهد و فهرستِ پیشفرض به کار میرود.
+ */
+export function enabledPaymentMethods(): PaymentMethod[] {
+ const channel = inAndroidApp() ? 'app' : 'web';
+ const configured = window.__TADRISINO_CONFIG__?.paymentMethods?.[channel];
+ if (!configured) return DEFAULTS[channel];
+ const unknown = configured.filter((m) => !KNOWN.includes(m as PaymentMethod));
+ if (unknown.length || !configured.length) {
+ console.error(`config.js: paymentMethods.${channel} نامعتبر است`, configured);
+ return DEFAULTS[channel];
+ }
+ return configured as PaymentMethod[];
+}
diff --git a/src/lib/plainMath.ts b/src/lib/plainMath.ts
index 4cbddce..3683608 100644
--- a/src/lib/plainMath.ts
+++ b/src/lib/plainMath.ts
@@ -43,7 +43,7 @@ const script = (body: string, table: Record, marker: string) =>
const mapped = tryMap(body, table);
if (mapped) return mapped;
const b = body.trim();
- if (marker === '^') return ` به توانِ (${b}) `;
+ if (marker === '^') return ` به توان (${b}) `;
return /^[\w۰-۹]{1,4}$/.test(b) ? b : `_(${b})`;
};
@@ -64,14 +64,24 @@ export function plainMath(text: string): string {
s = s.replace(SYMBOL_RE, (_m, n: string) => SYMBOLS[n]);
s = s.replace(new RegExp(`\\^${G}`, 'g'), (_m, a: string) => script(a, SUP, '^'));
s = s.replace(new RegExp(`_${G}`, 'g'), (_m, a: string) => script(a, SUB, '_'));
- s = s.replace(/\^\(((?:[^()]|\([^()]*\))*)\)/g, (_m, a: string) => tryMap(a, SUP) ?? ` به توانِ (${a.trim()}) `);
+ s = s.replace(/\^\(((?:[^()]|\([^()]*\))*)\)/g, (_m, a: string) => tryMap(a, SUP) ?? ` به توان (${a.trim()}) `);
s = s.replace(/\^([+\-−]?[0-9۰-۹]+)/g, (m, a: string) => tryMap(a, SUP) ?? m);
- s = s.replace(/(?<=[A-Za-z0-9۰-۹)₀-₉])\^([A-Za-zα-ωΑ-Ω□])(?![A-Za-z0-9۰-۹])/g, (_m, a: string) => tryMap(a, SUP) ?? ` به توانِ ${a} `);
- s = s.replace(/((? (p === 'log' && [...a].some((c) => !(c in SUB)) ? `log پایهی ${a}` : p + [...a].map((c) => SUB[c] ?? c).join('')));
+ s = s.replace(/(?<=[A-Za-z0-9۰-۹)₀-₉])\^([A-Za-zα-ωΑ-Ω□])(?![A-Za-z0-9۰-۹])/g, (_m, a: string) => tryMap(a, SUP) ?? ` به توان ${a} `);
+ s = s.replace(/((? (p === 'log' && [...a].some((c) => !(c in SUB)) ? `log پایه ${a}` : p + [...a].map((c) => SUB[c] ?? c).join('')));
s = s.replace(/\^([+\-−])/g, (_m, a: string) => SUP[a]);
s = s.replace(/(?<=[A-Za-z)'])_([+\-−])/g, (_m, a: string) => SUB[a]);
- s = s.replace(/\^(√[0-9۰-۹A-Za-z]+)/g, ' به توانِ $1 ').replace(/\^([A-Za-z]{2,3})(?![A-Za-z0-9])/g, ' به توانِ $1 ');
+ s = s.replace(/\^(√[0-9۰-۹A-Za-z]+)/g, ' به توان $1 ').replace(/\^([A-Za-z]{2,3})(?![A-Za-z0-9])/g, ' به توان $1 ');
s = s.replace(/(?<=\S) {2,}(?=\S)/g, ' ');
s = s.replace(/\*\*([\s\S]+?)\*\*/g, '$1').replace(/(? string> = {
};
export const SEYAR_INFO: Record = {
sigma: { name: "سیگما", symbol: "Σ", role: "راهنمای آزمون و کوییز" },
- delta: { name: "دلتا", symbol: "Δ", role: "همتیمیِ تمرینهای سریع" },
- pi: { name: "پی", symbol: "π", role: "همراهِ دلگرمی" },
- tedi: { name: "امگا", symbol: "ω", role: "رفیقِ همیشگیِ تدریسینو" },
+ delta: { name: "دلتا", symbol: "Δ", role: "همتیمی تمرینهای سریع" },
+ pi: { name: "پی", symbol: "π", role: "همراه دلگرمی" },
+ tedi: { name: "امگا", symbol: "ω", role: "رفیق همیشگی تدریسینو" },
};
/** slugِ کاراکترِ سرور (sigma/delta/pi/tedi) را به یکی از همراهها نگاشت میکند. */
diff --git a/src/pay.ts b/src/pay.ts
index 562c1d0..7efd9cb 100644
--- a/src/pay.ts
+++ b/src/pay.ts
@@ -37,14 +37,14 @@ const ok =
new RegExp(`^${api.pathname.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}payment/orders/\\d+/pay$`).test(target.pathname);
if (!ok || !target) {
- fail('این لینکِ پرداخت معتبر نیست. به اپ برگرد و دوباره «پرداخت» رو بزن.');
+ fail('این لینک پرداخت معتبر نیست. به اپ برگرد و دوباره «پرداخت» رو بزن.');
} else {
go.href = target.href;
location.replace(target.href);
// اگر مرورگر جابهجا نشد (مثلاً جلوی ریدایرکت را گرفت)، دکمهی دستی.
setTimeout(() => {
spin.style.display = 'none';
- msg.textContent = 'اگه صفحهی پرداخت باز نشد، این دکمه رو بزن:';
+ msg.textContent = 'اگه صفحه پرداخت باز نشد، این دکمه رو بزن:';
go.style.display = 'inline-block';
}, 2500);
}
diff --git a/src/screens/AskScreen.tsx b/src/screens/AskScreen.tsx
index 1694866..aa6579f 100644
--- a/src/screens/AskScreen.tsx
+++ b/src/screens/AskScreen.tsx
@@ -15,7 +15,7 @@ import { toFa } from '@/lib/format';
import { useGateHold } from '@/lib/useAsync';
import { iAm, useCompanion } from '@/state/companion';
-const STARTERS = ['این فصل دربارهی چیه؟', 'یه مثالِ ساده برام حل کن', 'چطور برای امتحان تمرین کنم؟'];
+const STARTERS = ['این فصل درباره چیه؟', 'یه مثال ساده برام حل کن', 'چطور برای امتحان تمرین کنم؟'];
/** «از {همراه} بپرس»: دستیارِ ریاضی که از روی کتابِ پایهی کاربر جواب میدهد؛ چهره و نامش همراهِ انتخابیِ کاربر است. */
export function AskScreen({ lessonId, questionId, prompt }: { lessonId?: number; questionId?: string; prompt?: string }) {
@@ -139,7 +139,7 @@ export function AskScreen({ lessonId, questionId, prompt }: { lessonId?: number;
- سلام {profile.name || 'دوستِ من'}! {`من ${iAm(companion.name)}`} 🙂 هر سوالی از ریاضیِ کتابت داری بپرس؛ قدمبهقدم با هم حلش میکنیم.
+ سلام {profile.name || 'دوست من'}! {`من ${iAm(companion.name)}`} 🙂 هر سوالی از ریاضی کتابت داری بپرس؛ قدمبهقدم با هم حلش میکنیم.
{empty && chat.historyLoaded && (
@@ -252,7 +252,7 @@ const THINKING_STEPS: [number, string][] = [
[0, '{name} داره تو کتابت میگرده'],
[4, '{name} داره حلش میکنه'],
[15, 'داره قدمها رو مرتب مینویسه'],
- [28, 'جوابِ کامل یه کم طول میکشه، الان میرسه'],
+ [28, 'جواب کامل یه کم طول میکشه، الان میرسه'],
];
function Thinking() {
@@ -363,11 +363,11 @@ function QuotaPills({ free }: { free: number }) {
return (
<>
0 ? 'bg-white text-[#2E7D5B]' : 'bg-white/15 text-white/80'}`}>
- {free > 0 ? <>{toFa(free)} سوالِ امروز> : 'سوالهای امروز تموم شد'}
+ {free > 0 ? <>{toFa(free)} سوال امروز> : 'سوالهای امروز تموم شد'}
{free === 0 && (
nav.switchTab('subscription')} className="rounded-full bg-white/15 px-3 py-1.5 text-xs font-black text-white">
- سوالِ بیشتر با اشتراک
+ سوال بیشتر با اشتراک
)}
>
diff --git a/src/screens/CharactersScreen.tsx b/src/screens/CharactersScreen.tsx
index 4e84b39..c07de07 100644
--- a/src/screens/CharactersScreen.tsx
+++ b/src/screens/CharactersScreen.tsx
@@ -40,7 +40,7 @@ export function CharactersScreen() {
امتیازِ تو: {toFa(summary?.xp ?? 0)} با امتیازِ بیشتر همراههای بیشتری باز میشن>}
+ subtitle={<>امتیاز تو: {toFa(summary?.xp ?? 0)} با امتیاز بیشتر همراههای بیشتری باز میشن>}
color="#9B7BFF"
back
/>
@@ -94,7 +94,7 @@ export function CharactersScreen() {
)}
- {!c.unlocked ? `با ${toFa(c.unlockXp)} امتیاز باز میشه` : c.selected ? 'همراهِ فعلی' : 'بزن تا همراهت بشه'}
+ {!c.unlocked ? `با ${toFa(c.unlockXp)} امتیاز باز میشه` : c.selected ? 'همراه فعلی' : 'بزن تا همراهت بشه'}
);
diff --git a/src/screens/HomeScreen.tsx b/src/screens/HomeScreen.tsx
index 4b71a1f..c06c7bb 100644
--- a/src/screens/HomeScreen.tsx
+++ b/src/screens/HomeScreen.tsx
@@ -1,16 +1,14 @@
import { useEffect, useRef, useState } from 'react';
-import { Bell, ChevronLeft, Coins, Crown, Flame, Hand, PartyPopper, Star } from 'lucide-react';
+import { Bell, ChevronLeft, Coins, Crown, Flame, Star } from 'lucide-react';
import type { HomeNode } from '@/api';
import { useAppData } from '@/state/AppData';
import { findContinue, unitColor, unitProgress } from '@/state/curriculum';
import { useNav } from '@/nav/Navigator';
-import { SeyarMascot } from '@/mascots/SeyarMascot';
import { IslandPath } from '@/components/IslandPath';
import { IslandSheet } from '@/components/IslandSheet';
import { EmptyState, ErrorState, LoadingState, Screen, Sep } from '@/components/ui';
import { onWhite, shade } from '@/lib/color';
import { toFa } from '@/lib/format';
-import { MathText } from '@/lib/MathText';
import { BrandFooter } from '@/components/BrandFooter';
import { BrandMark } from '@/components/BrandMark';
import { StreakFlame, flameState } from '@/components/StreakFlame';
@@ -22,7 +20,7 @@ import { SubjectBar } from '@/components/SubjectBar';
* دستیار، فصلها، چالش) و بچه نمیدانست کدام را بزند؛ حالا یک کارِ واضح دارد.
*/
export function HomeScreen() {
- const { profile, gradeTitle, summary, home, homeError, companion, unreadCount, refreshHome } = useAppData();
+ const { gradeTitle, summary, home, homeError, unreadCount, refreshHome } = useAppData();
const nav = useNav();
// فصلِ غیرِ اول بیاشتراک: زدنِ جزیره یکراست فهرستِ اشتراکها را باز میکند.
const paywall = () => nav.push({ name: 'subscription' });
@@ -39,37 +37,18 @@ export function HomeScreen() {
return () => cancelAnimationFrame(frame);
}, [home]);
- const name = profile.name || 'دوستِ من';
const flame = flameState(summary?.streak);
const streak = summary?.streak;
- // زنجیرهی در خطر یا یخزده جلوتر از پیامِ معمول میآید: از دست دادن انگیزهی قویتری از بهدست آوردن است،
- // ولی لحن مهربان میماند و قدمِ بعدی را میگوید.
- const streakNote = cont && streak
- ? flame === 'frozen'
- ? <>{name}، زنجیرهی {toFa(streak.lost)} روزهت یخ زد یه جزیره بزن تا دوباره آتیشش روشن بشه!>
- : flame === 'risk'
- ? <>{name}، امروز هنوز تمرین نکردی! زنجیرهی {toFa(streak.current)} روزهت منتظرته >
- : null
- : null;
- const bubble = !home
- ? 'دارم مسیرت رو میارم…'
- : !home.units.length
- ? 'هنوز درسی برای این پایه ثبت نشده.'
- : streakNote
- ? streakNote
- : cont
- ? <>سلام {name}! وقتشه جزیرهی « » رو شروع کنی؛ دکمهی شروع رو بزن.>
- : <>آفرین {name}! همهی جزیرههای این پایه رو تموم کردی >;
const stats = [
- { icon: Flame, value: summary?.streak.current ?? 0, label: 'روزِ پیاپی', color: '#FF7A3D', fill: true, to: 'streak' as const },
+ { icon: Flame, value: summary?.streak.current ?? 0, label: 'روز پیاپی', color: '#FF7A3D', fill: true, to: 'streak' as const },
{ icon: Star, value: summary?.xp ?? 0, label: 'امتیاز', color: '#E8A400', fill: true, to: 'leaderboard' as const },
{ icon: Coins, value: summary?.coins ?? 0, label: 'سکه', color: '#9B7BFF', fill: false, to: 'characters' as const },
];
return (
-
-