diff --git a/src/api/types.ts b/src/api/types.ts
index 682d4e4..5580e08 100644
--- a/src/api/types.ts
+++ b/src/api/types.ts
@@ -106,10 +106,19 @@ export interface LessonIntro {
kind: 'quiz' | 'video';
example: string;
steps: string[];
+ /** شکلهای ثابتِ درس (یک بار ساخته و تأییدشده؛ هیچ تولیدِ زندهای نیست). */
+ figures: LessonFigure[];
videoUrl: string;
durationMinutes: number | null;
}
+export interface LessonFigure {
+ where: 'intro' | 'example' | 'step';
+ /** فقط برای where='step'، از صفر. */
+ index?: number;
+ diagram: DiagramSpec;
+}
+
export interface QuestionOption {
id: string;
value: string;
diff --git a/src/components/ui.tsx b/src/components/ui.tsx
index eabab70..aeabac9 100644
--- a/src/components/ui.tsx
+++ b/src/components/ui.tsx
@@ -1,4 +1,4 @@
-import type { ButtonHTMLAttributes, CSSProperties, ReactNode } from 'react';
+import { useEffect, useState, type ButtonHTMLAttributes, type CSSProperties, type ReactNode } from 'react';
import { ChevronRight, RotateCw } from 'lucide-react';
import { SeyarMascot } from '@/mascots/SeyarMascot';
import type { SeyarChar, SeyarMood } from '@/mascots/seyar';
@@ -130,8 +130,19 @@ function StateBox({ char, mood, text, children }: { char: SeyarChar; mood: Seyar
);
}
-export function LoadingState({ text = 'یه لحظه صبر کن…', char = 'sigma' as SeyarChar }) {
- return ;
+/**
+ * لودر فقط بعد از `delayMs` دیده میشود. پاسخِ API معمولاً زیرِ ۳۰۰ms میرسد و نشان دادنِ لودر برای
+ * همان چند فریم، پرشِ ریزِ «لودر ← محتوا» میساخت؛ حالا تا آن لحظه جای خالی میماند. اگر پاسخ
+ * کند باشد کاربر باز هم لودر را میبیند. در مرورگر با شبکهی کند آزموده نشده.
+ */
+export function LoadingState({ text = 'یه لحظه صبر کن…', char = 'sigma' as SeyarChar, delayMs = 300 }) {
+ const [show, setShow] = useState(delayMs <= 0);
+ useEffect(() => {
+ if (delayMs <= 0) return;
+ const t = setTimeout(() => setShow(true), delayMs);
+ return () => clearTimeout(t);
+ }, [delayMs]);
+ return show ? : null;
}
export function ErrorState({ error, onRetry, text }: { error?: unknown; onRetry?: () => void; text?: string }) {
diff --git a/src/screens/LessonScreen.tsx b/src/screens/LessonScreen.tsx
index 86e72a0..445bc97 100644
--- a/src/screens/LessonScreen.tsx
+++ b/src/screens/LessonScreen.tsx
@@ -9,6 +9,7 @@ import { moodFromServer } from '@/mascots/seyar';
import { ErrorState, LoadingState, PrimaryButton, Screen, ScreenBody, ScreenHeader, Sep } from '@/components/ui';
import { toFa } from '@/lib/format';
import { MathText } from '@/lib/MathText';
+import { Diagram } from '@/diagram';
/** معرفیِ درس، پیش از تمرین. */
export function LessonScreen({ lessonId, nodeId }: { lessonId: number; nodeId?: string }) {
@@ -18,6 +19,10 @@ export function LessonScreen({ lessonId, nodeId }: { lessonId: number; nodeId?:
const ref = findNode(home, nodeId) ?? findNodeOfLesson(home, lessonId);
const color = ref ? unitColor(ref.unitIndex) : '#ED6956';
const data = intro.data;
+ const figs = (where: 'intro' | 'example' | 'step', index?: number) =>
+ (data?.figures ?? [])
+ .filter((f) => f.where === where && (where !== 'step' || f.index === index))
+ .map((f, k) => );
return (
@@ -47,6 +52,7 @@ export function LessonScreen({ lessonId, nodeId }: { lessonId: number; nodeId?:
@@ -58,6 +64,7 @@ export function LessonScreen({ lessonId, nodeId }: { lessonId: number; nodeId?:
)}
@@ -70,7 +77,7 @@ export function LessonScreen({ lessonId, nodeId }: { lessonId: number; nodeId?:
{toFa(i + 1)}
-
+ {figs('step', i)}
))}