[Feature] Add a bunch more things to draw :) #11
+186
-167
@@ -1,167 +1,186 @@
|
||||
import Link from "next/link";
|
||||
import SiteNav from "@/components/SiteNav";
|
||||
import { LEVELS, getSubjects, BEGINNER_PACKS, lessonStepCount, type Lesson } from "@/lib/curriculum";
|
||||
import { getCurrentUser } from "@/lib/session";
|
||||
import { getCompletedSteps, hasBadge } from "@/lib/progress";
|
||||
import { isLevelUnlocked, countCompletedLessons, isPackComplete, BEGINNER_UNLOCK_THRESHOLD } from "@/lib/gating";
|
||||
|
||||
export const metadata = { title: "Learn · DrawIt" };
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
const PHASE_LABEL: Record<string, string> = {
|
||||
construct: "Shapes", outline: "Outline", detail: "Details", color: "Color", light: "Light & shadow", extra: "Color it!",
|
||||
};
|
||||
const OPTIONAL_PHASES = new Set(["extra"]);
|
||||
|
||||
type Subject = ReturnType<typeof getSubjects>[number];
|
||||
|
||||
export default async function LearnPage() {
|
||||
const user = await getCurrentUser();
|
||||
|
||||
const doneCount = (l: Lesson) => (user ? getCompletedSteps(user.id, l.level, l.sublevel).length : 0);
|
||||
const isDone = (l: Lesson) => {
|
||||
if (!user) return false;
|
||||
if (l.badgeKey && hasBadge(user.id, l.badgeKey)) return true;
|
||||
return doneCount(l) >= lessonStepCount(l);
|
||||
};
|
||||
const subjectComplete = (s: Subject) => s.lessons.every((l) => isDone(l));
|
||||
|
||||
// One card per phase, each gated behind the previous phase in the subject.
|
||||
const phaseCard = (l: Lesson, locked: boolean) => {
|
||||
const optional = OPTIONAL_PHASES.has(l.phase ?? "");
|
||||
const done = isDone(l);
|
||||
const inProg = !done && !locked && doneCount(l) > 0;
|
||||
const inner = (
|
||||
<div>
|
||||
<strong style={{ display: "block" }}>{PHASE_LABEL[l.phase ?? ""] ?? l.title}{optional ? " (optional)" : ""}</strong>
|
||||
<span className="muted" style={{ fontSize: "0.82rem" }}>
|
||||
{locked ? "🔒 Locked" : done ? "✓ Done" : inProg ? `Step ${Math.min(doneCount(l) + 1, lessonStepCount(l))}/${lessonStepCount(l)}` : l.steps.length === 0 ? "Tap to start" : `${lessonStepCount(l)} steps`}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
return locked ? (
|
||||
<div key={l.slug} style={{ ...phaseBox, opacity: 0.55 }}>{inner}</div>
|
||||
) : (
|
||||
<Link key={l.slug} href={`/learn/${l.level}/${l.slug}`} style={{ ...phaseBox, textDecoration: "none", color: "inherit", borderColor: done ? "var(--leaf)" : "var(--line)" }}>{inner}</Link>
|
||||
);
|
||||
};
|
||||
|
||||
const subjectAccordion = (subj: Subject, open: boolean) => {
|
||||
const complete = subjectComplete(subj);
|
||||
const anyStarted = subj.lessons.some((l) => doneCount(l) > 0);
|
||||
// gate each phase behind the previous one being done
|
||||
let prevDone = true;
|
||||
const cards = subj.lessons.map((l) => {
|
||||
const locked = !prevDone;
|
||||
const card = phaseCard(l, locked);
|
||||
prevDone = isDone(l);
|
||||
return card;
|
||||
});
|
||||
return (
|
||||
<details key={subj.key} open={open} style={{ border: complete ? "2px solid var(--leaf)" : "2px solid var(--line)", borderRadius: 14, padding: "10px 14px", background: "var(--surface)" }}>
|
||||
<summary style={{ cursor: "pointer", fontWeight: 800, fontSize: "1.05rem" }}>
|
||||
{subj.emoji} {subj.name}
|
||||
{complete ? <span className="pill active" style={{ marginLeft: 8 }}>✓ Done</span> : anyStarted ? <span className="pill pending" style={{ marginLeft: 8 }}>In progress</span> : null}
|
||||
</summary>
|
||||
<div style={{ display: "grid", gap: 10, gridTemplateColumns: "repeat(auto-fit, minmax(140px, 1fr))", marginTop: 12 }}>
|
||||
{cards}
|
||||
</div>
|
||||
</details>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<SiteNav />
|
||||
<main className="container page">
|
||||
<h1>Learn to draw 🎨</h1>
|
||||
<p className="muted">
|
||||
Each picture is built up in steps. Finish the lessons to earn badges and fill your gallery.
|
||||
{!user && " Log in to track your progress and earn badges."}
|
||||
</p>
|
||||
|
||||
<div className="stack" style={{ marginTop: 18 }}>
|
||||
{LEVELS.map((lvl, idx) => {
|
||||
const subjects = getSubjects(lvl.key);
|
||||
const unlocked = isLevelUnlocked(user?.id ?? null, lvl.key);
|
||||
const firstIncomplete = subjects.find((s) => !subjectComplete(s))?.key;
|
||||
|
||||
return (
|
||||
<div className="card" key={lvl.key}>
|
||||
<div className="row" style={{ justifyContent: "space-between" }}>
|
||||
<h2 style={{ margin: 0 }}>{lvl.emoji} {idx + 1}. {lvl.name}</h2>
|
||||
{subjects.length === 0 && <span className="pill pending">Coming soon</span>}
|
||||
{subjects.length > 0 && !unlocked && <span className="pill pending">🔒 Locked</span>}
|
||||
</div>
|
||||
<p className="muted">{lvl.blurb}</p>
|
||||
|
||||
{/* Locked level: show how to unlock */}
|
||||
{subjects.length > 0 && !unlocked ? (
|
||||
<div className="card" style={{ background: "var(--surface)", textAlign: "center", marginTop: 6 }}>
|
||||
{!user ? (
|
||||
<p className="muted" style={{ margin: 0 }}>
|
||||
<Link href="/signup">Sign up</Link> and finish {BEGINNER_UNLOCK_THRESHOLD} Early Beginner drawings to unlock this level.
|
||||
</p>
|
||||
) : (
|
||||
<>
|
||||
<p style={{ margin: "0 0 6px" }}>🔒 Finish <strong>{BEGINNER_UNLOCK_THRESHOLD}</strong> Early Beginner lessons to unlock the {lvl.name} level.</p>
|
||||
<p className="muted" style={{ margin: 0 }}>
|
||||
You've done {Math.min(countCompletedLessons(user.id, "early-beginner"), BEGINNER_UNLOCK_THRESHOLD)} of {BEGINNER_UNLOCK_THRESHOLD}. Keep going!
|
||||
</p>
|
||||
<Link className="btn" href="/learn/early-beginner/fish-outline" style={{ marginTop: 10 }}>Practice Early Beginner</Link>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
) : lvl.key === "beginner" ? (
|
||||
// Beginner: group subjects into packs, each with a per-pack badge.
|
||||
<div className="stack" style={{ marginTop: 6 }}>
|
||||
{BEGINNER_PACKS.map((pack) => {
|
||||
const packSubjects = subjects.filter((s) => pack.subjectKeys.includes(s.key));
|
||||
const packDone = !!user && isPackComplete(user.id, pack);
|
||||
return (
|
||||
<div key={pack.key} style={{ border: packDone ? "2px solid var(--leaf)" : "2px dashed var(--line)", borderRadius: 16, padding: "12px 14px" }}>
|
||||
<div className="row" style={{ justifyContent: "space-between", alignItems: "center", flexWrap: "wrap", gap: 8 }}>
|
||||
<strong style={{ fontSize: "1.05rem" }}>{pack.emoji} {pack.name}</strong>
|
||||
{packDone ? <span className="pill active">🏅 {pack.badgeName}</span> : <span className="muted" style={{ fontSize: "0.82rem" }}>Finish all {pack.subjectKeys.length} to earn {pack.badgeName}</span>}
|
||||
</div>
|
||||
<p className="muted" style={{ margin: "4px 0 10px", fontSize: "0.9rem" }}>{pack.blurb}</p>
|
||||
<div className="stack">
|
||||
{packSubjects.map((subj) => subjectAccordion(subj, subj.key === firstIncomplete))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
) : (
|
||||
// Early Beginner (and any future flat levels)
|
||||
<div className="stack" style={{ marginTop: 6 }}>
|
||||
{subjects.map((subj) => {
|
||||
const phased = subj.lessons.length > 1 || !!subj.lessons[0].phase;
|
||||
if (!phased) {
|
||||
const l = subj.lessons[0];
|
||||
const done = isDone(l);
|
||||
const inProg = !done && doneCount(l) > 0;
|
||||
return (
|
||||
<Link key={subj.key} href={`/learn/${l.level}/${l.slug}`} className="card" style={subCard(done)}>
|
||||
<div className="row" style={{ justifyContent: "space-between", alignItems: "center" }}>
|
||||
<strong>{subj.emoji} {l.title}</strong>
|
||||
{done ? <span className="pill active">✓ Done</span> : inProg ? <span className="pill pending">Step {Math.min(doneCount(l) + 1, lessonStepCount(l))}/{lessonStepCount(l)}</span> : <span className="muted" style={{ fontSize: "0.85rem" }}>{lessonStepCount(l)} steps</span>}
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
return subjectAccordion(subj, subj.key === firstIncomplete);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const subCard = (done: boolean): React.CSSProperties => ({ textDecoration: "none", color: "inherit", border: done ? "2px solid var(--leaf)" : "2px solid var(--line)", boxShadow: "none", padding: 14 });
|
||||
const phaseBox: React.CSSProperties = { display: "block", border: "2px solid var(--line)", borderRadius: 12, padding: "12px 14px", background: "var(--surface)" };
|
||||
import Link from "next/link";
|
||||
import SiteNav from "@/components/SiteNav";
|
||||
import { LEVELS, getSubjects, getGroupedSubjects, BEGINNER_PACKS, lessonStepCount, type Lesson } from "@/lib/curriculum";
|
||||
import { getCurrentUser } from "@/lib/session";
|
||||
import { getCompletedSteps, hasBadge } from "@/lib/progress";
|
||||
import { isLevelUnlocked, countCompletedLessons, isPackComplete, BEGINNER_UNLOCK_THRESHOLD } from "@/lib/gating";
|
||||
|
||||
export const metadata = { title: "Learn · DrawIt" };
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
const PHASE_LABEL: Record<string, string> = {
|
||||
construct: "Shapes", outline: "Outline", detail: "Details", color: "Color", light: "Light & shadow", extra: "Color it!",
|
||||
};
|
||||
const OPTIONAL_PHASES = new Set(["extra"]);
|
||||
|
||||
type Subject = ReturnType<typeof getSubjects>[number];
|
||||
|
||||
export default async function LearnPage() {
|
||||
const user = await getCurrentUser();
|
||||
|
||||
const doneCount = (l: Lesson) => (user ? getCompletedSteps(user.id, l.level, l.sublevel).length : 0);
|
||||
const isDone = (l: Lesson) => {
|
||||
if (!user) return false;
|
||||
if (l.badgeKey && hasBadge(user.id, l.badgeKey)) return true;
|
||||
return doneCount(l) >= lessonStepCount(l);
|
||||
};
|
||||
const subjectComplete = (s: Subject) => s.lessons.every((l) => isDone(l));
|
||||
|
||||
// One card per phase, each gated behind the previous phase in the subject.
|
||||
const phaseCard = (l: Lesson, locked: boolean) => {
|
||||
const optional = OPTIONAL_PHASES.has(l.phase ?? "");
|
||||
const done = isDone(l);
|
||||
const inProg = !done && !locked && doneCount(l) > 0;
|
||||
const inner = (
|
||||
<div>
|
||||
<strong style={{ display: "block" }}>{PHASE_LABEL[l.phase ?? ""] ?? l.title}{optional ? " (optional)" : ""}</strong>
|
||||
<span className="muted" style={{ fontSize: "0.82rem" }}>
|
||||
{locked ? "🔒 Locked" : done ? "✓ Done" : inProg ? `Step ${Math.min(doneCount(l) + 1, lessonStepCount(l))}/${lessonStepCount(l)}` : l.steps.length === 0 ? "Tap to start" : `${lessonStepCount(l)} steps`}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
return locked ? (
|
||||
<div key={l.slug} style={{ ...phaseBox, opacity: 0.55 }}>{inner}</div>
|
||||
) : (
|
||||
<Link key={l.slug} href={`/learn/${l.level}/${l.slug}`} style={{ ...phaseBox, textDecoration: "none", color: "inherit", borderColor: done ? "var(--leaf)" : "var(--line)" }}>{inner}</Link>
|
||||
);
|
||||
};
|
||||
|
||||
const subjectAccordion = (subj: Subject, open: boolean) => {
|
||||
const complete = subjectComplete(subj);
|
||||
const anyStarted = subj.lessons.some((l) => doneCount(l) > 0);
|
||||
// gate each phase behind the previous one being done
|
||||
let prevDone = true;
|
||||
const cards = subj.lessons.map((l) => {
|
||||
const locked = !prevDone;
|
||||
const card = phaseCard(l, locked);
|
||||
prevDone = isDone(l);
|
||||
return card;
|
||||
});
|
||||
return (
|
||||
<details key={subj.key} open={open} style={{ border: complete ? "2px solid var(--leaf)" : "2px solid var(--line)", borderRadius: 14, padding: "10px 14px", background: "var(--surface)" }}>
|
||||
<summary style={{ cursor: "pointer", fontWeight: 800, fontSize: "1.05rem" }}>
|
||||
{subj.emoji} {subj.name}
|
||||
{complete ? <span className="pill active" style={{ marginLeft: 8 }}>✓ Done</span> : anyStarted ? <span className="pill pending" style={{ marginLeft: 8 }}>In progress</span> : null}
|
||||
</summary>
|
||||
<div style={{ display: "grid", gap: 10, gridTemplateColumns: "repeat(auto-fit, minmax(140px, 1fr))", marginTop: 12 }}>
|
||||
{cards}
|
||||
</div>
|
||||
</details>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<SiteNav />
|
||||
<main className="container page">
|
||||
<h1>Learn to draw 🎨</h1>
|
||||
<p className="muted">
|
||||
Each picture is built up in steps. Finish the lessons to earn badges and fill your gallery.
|
||||
{!user && " Log in to track your progress and earn badges."}
|
||||
</p>
|
||||
|
||||
<div className="stack" style={{ marginTop: 18 }}>
|
||||
{LEVELS.map((lvl, idx) => {
|
||||
const subjects = getSubjects(lvl.key);
|
||||
const unlocked = isLevelUnlocked(user?.id ?? null, lvl.key);
|
||||
const orderedSubjects = lvl.key === "early-beginner" ? getGroupedSubjects(lvl.key).flatMap((g) => g.subjects) : subjects;
|
||||
const firstIncomplete = orderedSubjects.find((s) => !subjectComplete(s))?.key;
|
||||
|
||||
return (
|
||||
<div className="card" key={lvl.key}>
|
||||
<div className="row" style={{ justifyContent: "space-between" }}>
|
||||
<h2 style={{ margin: 0 }}>{lvl.emoji} {idx + 1}. {lvl.name}</h2>
|
||||
{subjects.length === 0 && <span className="pill pending">Coming soon</span>}
|
||||
{subjects.length > 0 && !unlocked && <span className="pill pending">🔒 Locked</span>}
|
||||
</div>
|
||||
<p className="muted">{lvl.blurb}</p>
|
||||
|
||||
{/* Locked level: show how to unlock */}
|
||||
{subjects.length > 0 && !unlocked ? (
|
||||
<div className="card" style={{ background: "var(--surface)", textAlign: "center", marginTop: 6 }}>
|
||||
{!user ? (
|
||||
<p className="muted" style={{ margin: 0 }}>
|
||||
<Link href="/signup">Sign up</Link> and finish {BEGINNER_UNLOCK_THRESHOLD} Early Beginner drawings to unlock this level.
|
||||
</p>
|
||||
) : (
|
||||
<>
|
||||
<p style={{ margin: "0 0 6px" }}>🔒 Finish <strong>{BEGINNER_UNLOCK_THRESHOLD}</strong> Early Beginner lessons to unlock the {lvl.name} level.</p>
|
||||
<p className="muted" style={{ margin: 0 }}>
|
||||
You've done {Math.min(countCompletedLessons(user.id, "early-beginner"), BEGINNER_UNLOCK_THRESHOLD)} of {BEGINNER_UNLOCK_THRESHOLD}. Keep going!
|
||||
</p>
|
||||
<Link className="btn" href="/learn/early-beginner/fish-outline" style={{ marginTop: 10 }}>Practice Early Beginner</Link>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
) : lvl.key === "beginner" ? (
|
||||
// Beginner: group subjects into packs, each with a per-pack badge.
|
||||
<div className="stack" style={{ marginTop: 6 }}>
|
||||
{BEGINNER_PACKS.map((pack) => {
|
||||
const packSubjects = subjects.filter((s) => pack.subjectKeys.includes(s.key));
|
||||
const packDone = !!user && isPackComplete(user.id, pack);
|
||||
return (
|
||||
<div key={pack.key} style={{ border: packDone ? "2px solid var(--leaf)" : "2px dashed var(--line)", borderRadius: 16, padding: "12px 14px" }}>
|
||||
<div className="row" style={{ justifyContent: "space-between", alignItems: "center", flexWrap: "wrap", gap: 8 }}>
|
||||
<strong style={{ fontSize: "1.05rem" }}>{pack.emoji} {pack.name}</strong>
|
||||
{packDone ? <span className="pill active">🏅 {pack.badgeName}</span> : <span className="muted" style={{ fontSize: "0.82rem" }}>Finish all {pack.subjectKeys.length} to earn {pack.badgeName}</span>}
|
||||
</div>
|
||||
<p className="muted" style={{ margin: "4px 0 10px", fontSize: "0.9rem" }}>{pack.blurb}</p>
|
||||
<div className="stack">
|
||||
{packSubjects.map((subj) => subjectAccordion(subj, subj.key === firstIncomplete))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
) : lvl.key === "early-beginner" ? (
|
||||
// Early Beginner: themed groups, each holding its subjects.
|
||||
<div className="stack" style={{ marginTop: 6 }}>
|
||||
{getGroupedSubjects(lvl.key).map((group) => {
|
||||
const groupDone = group.subjects.every((s) => subjectComplete(s));
|
||||
return (
|
||||
<div key={group.key} style={{ border: groupDone ? "2px solid var(--leaf)" : "2px dashed var(--line)", borderRadius: 16, padding: "12px 14px" }}>
|
||||
<div className="row" style={{ justifyContent: "space-between", alignItems: "center" }}>
|
||||
<strong style={{ fontSize: "1.05rem" }}>{group.emoji} {group.name}</strong>
|
||||
<span className="muted" style={{ fontSize: "0.82rem" }}>{group.subjects.filter((s) => subjectComplete(s)).length}/{group.subjects.length} done</span>
|
||||
</div>
|
||||
<div className="stack" style={{ marginTop: 10 }}>
|
||||
{group.subjects.map((subj) => subjectAccordion(subj, subj.key === firstIncomplete))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
) : (
|
||||
// Any future flat level
|
||||
<div className="stack" style={{ marginTop: 6 }}>
|
||||
{subjects.map((subj) => {
|
||||
const phased = subj.lessons.length > 1 || !!subj.lessons[0].phase;
|
||||
if (!phased) {
|
||||
const l = subj.lessons[0];
|
||||
const done = isDone(l);
|
||||
const inProg = !done && doneCount(l) > 0;
|
||||
return (
|
||||
<Link key={subj.key} href={`/learn/${l.level}/${l.slug}`} className="card" style={subCard(done)}>
|
||||
<div className="row" style={{ justifyContent: "space-between", alignItems: "center" }}>
|
||||
<strong>{subj.emoji} {l.title}</strong>
|
||||
{done ? <span className="pill active">✓ Done</span> : inProg ? <span className="pill pending">Step {Math.min(doneCount(l) + 1, lessonStepCount(l))}/{lessonStepCount(l)}</span> : <span className="muted" style={{ fontSize: "0.85rem" }}>{lessonStepCount(l)} steps</span>}
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
return subjectAccordion(subj, subj.key === firstIncomplete);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const subCard = (done: boolean): React.CSSProperties => ({ textDecoration: "none", color: "inherit", border: done ? "2px solid var(--leaf)" : "2px solid var(--line)", boxShadow: "none", padding: 14 });
|
||||
const phaseBox: React.CSSProperties = { display: "block", border: "2px solid var(--line)", borderRadius: 12, padding: "12px 14px", background: "var(--surface)" };
|
||||
|
||||
+124
-121
@@ -1,121 +1,124 @@
|
||||
import Link from "next/link";
|
||||
import SiteNav from "@/components/SiteNav";
|
||||
import FishSlideshow from "@/components/FishSlideshow";
|
||||
import { LEVELS, getSubjects, FISH_OUTLINE_LESSON, FISH_DETAIL_LESSON } from "@/lib/curriculum";
|
||||
|
||||
const FREE_SLUG = "fish-outline";
|
||||
|
||||
export default function Home() {
|
||||
// Build a coloring-book "video" preview from the fish outline + detail lines
|
||||
const fishSteps = [...FISH_OUTLINE_LESSON.steps, ...FISH_DETAIL_LESSON.steps];
|
||||
let cum = "";
|
||||
const slides = fishSteps.map((s, i) => {
|
||||
cum += (s.lines || []).join("\n") + "\n";
|
||||
return { n: i + 1, title: s.title, instruction: s.instruction, cumulativeSvg: cum };
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<SiteNav />
|
||||
<main>
|
||||
<section className="section">
|
||||
<div className="container" style={{ textAlign: "center" }}>
|
||||
<div className="pill role" style={{ marginBottom: 14 }}>🆓 Free & open source · 🔒 No data collected, ever</div>
|
||||
<h1>Learn to draw, one line at a time</h1>
|
||||
<p className="muted" style={{ fontSize: "1.2rem", maxWidth: 640, margin: "0 auto 24px" }}>
|
||||
DrawIt teaches kids to draw with simple, friendly steps — built for iPads and tablets. Each
|
||||
picture has an <strong>Outline</strong> lesson, a <strong>Details</strong> lesson, and a fun
|
||||
<strong> Color it!</strong> lesson. <strong>The first lesson is free</strong> — create an account to unlock the rest.
|
||||
</p>
|
||||
<div className="row" style={{ justifyContent: "center" }}>
|
||||
<Link href={`/learn/early-beginner/${FREE_SLUG}`} className="btn big">🐟 Try the first lesson free</Link>
|
||||
<Link href="/signup" className="btn secondary big">Create a free account</Link>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="section" style={{ background: "var(--surface)", borderBlock: "1px solid var(--line)" }}>
|
||||
<div className="container">
|
||||
<h2 className="center">Watch it drawn, line by line 🐟</h2>
|
||||
<p className="center muted" style={{ maxWidth: 560, margin: "0 auto 26px" }}>
|
||||
Lessons draw each line slowly so kids can follow along, like a coloring book that builds itself.
|
||||
</p>
|
||||
<FishSlideshow steps={slides} />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="section">
|
||||
<div className="container">
|
||||
<h2 className="center">Start free — unlock everything with an account</h2>
|
||||
<p className="center muted" style={{ maxWidth: 600, margin: "0 auto 26px" }}>
|
||||
The first lesson (Fish → Outline) is free to try. A free account unlocks the rest, saves your
|
||||
progress, and lets you earn badges and keep your drawings.
|
||||
</p>
|
||||
<div className="stack">
|
||||
{LEVELS.map((lvl, idx) => {
|
||||
const subjects = getSubjects(lvl.key);
|
||||
const isFirst = lvl.key === "early-beginner";
|
||||
return (
|
||||
<div className="card" key={lvl.key}>
|
||||
<div className="row" style={{ justifyContent: "space-between", alignItems: "center" }}>
|
||||
<h3 style={{ margin: 0 }}>{lvl.emoji} {idx + 1}. {lvl.name}</h3>
|
||||
{!isFirst && <span className="pill pending">🔒 Account needed</span>}
|
||||
</div>
|
||||
<p className="muted" style={{ marginTop: 6 }}>{lvl.blurb}</p>
|
||||
{isFirst && subjects.length > 0 && (
|
||||
<div style={{ display: "grid", gap: 12, gridTemplateColumns: "repeat(auto-fit, minmax(200px, 1fr))", marginTop: 8 }}>
|
||||
{subjects.map((subj) => {
|
||||
const first = subj.lessons[0];
|
||||
const free = first.slug === FREE_SLUG;
|
||||
return (
|
||||
<Link key={subj.key} href={free ? `/learn/${first.level}/${first.slug}` : "/signup"} style={card(!free)}>
|
||||
<span style={tag(free)}>{free ? "Free" : "🔒"}</span>
|
||||
<div style={{ fontSize: "1.8rem" }}>{subj.emoji}</div>
|
||||
<strong style={{ display: "block", paddingRight: 40 }}>{subj.name}</strong>
|
||||
<p className="muted" style={{ margin: "4px 0 0", fontSize: "0.85rem" }}>
|
||||
{free ? "Outline · free to try" : "Create an account to unlock"}
|
||||
</p>
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
{!isFirst && (
|
||||
<Link href="/signup" style={{ ...card(true), marginTop: 8, display: "block" }}>
|
||||
<span style={tag(false)}>🔒</span>
|
||||
<strong>Unlock {lvl.name}</strong>
|
||||
<p className="muted" style={{ margin: "4px 0 0", fontSize: "0.85rem" }}>Create a free account to explore.</p>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className="center" style={{ marginTop: 26 }}>
|
||||
<Link href="/signup" className="btn big">Create a free account</Link>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="section" style={{ background: "var(--surface)", borderTop: "1px solid var(--line)" }}>
|
||||
<div className="container">
|
||||
<h2 className="center">How DrawIt works</h2>
|
||||
<div style={{ display: "grid", gap: 16, gridTemplateColumns: "repeat(auto-fit, minmax(220px, 1fr))", marginTop: 18 }}>
|
||||
<div className="card"><h3>✏️ Outline → Details</h3><p className="muted">Trace the coloring-book outline, then add the details that bring it to life.</p></div>
|
||||
<div className="card"><h3>🎨 Color it in</h3><p className="muted">Finish with the coloring studio — color, shade, and make it your own.</p></div>
|
||||
<div className="card"><h3>🔒 Private by design</h3><p className="muted">No analytics, no trackers, no data sold or shared. Just drawing.</p></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<footer className="site">DrawIt · Free & open source · No data collected. Made for young artists. 🎨</footer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function card(locked: boolean): React.CSSProperties {
|
||||
return { position: "relative", textDecoration: "none", color: "inherit", background: "var(--surface)", borderRadius: "var(--radius)", border: "2px solid var(--line)", padding: 18, opacity: locked ? 0.6 : 1, filter: locked ? "grayscale(0.7)" : "none" };
|
||||
}
|
||||
function tag(free: boolean): React.CSSProperties {
|
||||
return { position: "absolute", top: 12, right: 12, fontWeight: 800, fontSize: "0.8rem", padding: "3px 10px", borderRadius: 999, background: free ? "#e8f7ee" : "#f0ebf7", color: free ? "#1f7a45" : "var(--ink-soft)" };
|
||||
}
|
||||
import Link from "next/link";
|
||||
import SiteNav from "@/components/SiteNav";
|
||||
import FishSlideshow from "@/components/FishSlideshow";
|
||||
import { LEVELS, getSubjects, FISH_OUTLINE_LESSON, FISH_DETAIL_LESSON } from "@/lib/curriculum";
|
||||
|
||||
const FREE_SLUG = "fish-outline";
|
||||
|
||||
export default function Home() {
|
||||
// Build a coloring-book "video" preview from the fish outline + detail lines
|
||||
const fishSteps = [...FISH_OUTLINE_LESSON.steps, ...FISH_DETAIL_LESSON.steps];
|
||||
let cum = "";
|
||||
const slides = fishSteps.map((s, i) => {
|
||||
cum += (s.lines || []).join("\n") + "\n";
|
||||
return { n: i + 1, title: s.title, instruction: s.instruction, cumulativeSvg: cum };
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<SiteNav />
|
||||
<main>
|
||||
<section className="section">
|
||||
<div className="container" style={{ textAlign: "center" }}>
|
||||
<div className="pill role" style={{ marginBottom: 14 }}>🆓 Free & open source · 🔒 No data collected, ever</div>
|
||||
<h1>Learn to draw, one line at a time</h1>
|
||||
<p className="muted" style={{ fontSize: "1.2rem", maxWidth: 640, margin: "0 auto 24px" }}>
|
||||
DrawIt teaches kids to draw with simple, friendly steps — built for iPads and tablets. Each
|
||||
picture has an <strong>Outline</strong> lesson, a <strong>Details</strong> lesson, and a fun
|
||||
<strong> Color it!</strong> lesson. <strong>The first lesson is free</strong> — create an account to unlock the rest.
|
||||
</p>
|
||||
<div className="row" style={{ justifyContent: "center" }}>
|
||||
<Link href={`/learn/early-beginner/${FREE_SLUG}`} className="btn big">🐟 Try the first lesson free</Link>
|
||||
<Link href="/signup" className="btn secondary big">Create a free account</Link>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="section" style={{ background: "var(--surface)", borderBlock: "1px solid var(--line)" }}>
|
||||
<div className="container">
|
||||
<h2 className="center">Watch it drawn, line by line 🐟</h2>
|
||||
<p className="center muted" style={{ maxWidth: 560, margin: "0 auto 26px" }}>
|
||||
Lessons draw each line slowly so kids can follow along, like a coloring book that builds itself.
|
||||
</p>
|
||||
<FishSlideshow steps={slides} />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="section">
|
||||
<div className="container">
|
||||
<h2 className="center">Start free — unlock everything with an account</h2>
|
||||
<p className="center muted" style={{ maxWidth: 600, margin: "0 auto 26px" }}>
|
||||
The first lesson (Fish → Outline) is free to try. A free account unlocks the rest, saves your
|
||||
progress, and lets you earn badges and keep your drawings.
|
||||
</p>
|
||||
<div className="stack">
|
||||
{LEVELS.map((lvl, idx) => {
|
||||
const subjects = getSubjects(lvl.key);
|
||||
const isFirst = lvl.key === "early-beginner";
|
||||
return (
|
||||
<div className="card" key={lvl.key}>
|
||||
<div className="row" style={{ justifyContent: "space-between", alignItems: "center" }}>
|
||||
<h3 style={{ margin: 0 }}>{lvl.emoji} {idx + 1}. {lvl.name}</h3>
|
||||
{!isFirst && <span className="pill pending">🔒 Account needed</span>}
|
||||
</div>
|
||||
<p className="muted" style={{ marginTop: 6 }}>{lvl.blurb}</p>
|
||||
{isFirst && subjects.length > 0 && (
|
||||
<div style={{ display: "grid", gap: 12, gridTemplateColumns: "repeat(auto-fit, minmax(200px, 1fr))", marginTop: 8 }}>
|
||||
{subjects.slice(0, 6).map((subj) => {
|
||||
const first = subj.lessons[0];
|
||||
const free = first.slug === FREE_SLUG;
|
||||
return (
|
||||
<Link key={subj.key} href={free ? `/learn/${first.level}/${first.slug}` : "/signup"} style={card(!free)}>
|
||||
<span style={tag(free)}>{free ? "Free" : "🔒"}</span>
|
||||
<div style={{ fontSize: "1.8rem" }}>{subj.emoji}</div>
|
||||
<strong style={{ display: "block", paddingRight: 40 }}>{subj.name}</strong>
|
||||
<p className="muted" style={{ margin: "4px 0 0", fontSize: "0.85rem" }}>
|
||||
{free ? "Outline · free to try" : "Create an account to unlock"}
|
||||
</p>
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
{isFirst && subjects.length > 6 && (
|
||||
<p className="muted" style={{ marginTop: 10, fontSize: "0.9rem" }}>…and lots more — shapes, animals, clothes, faces and more inside.</p>
|
||||
)}
|
||||
{!isFirst && (
|
||||
<Link href="/signup" style={{ ...card(true), marginTop: 8, display: "block" }}>
|
||||
<span style={tag(false)}>🔒</span>
|
||||
<strong>Unlock {lvl.name}</strong>
|
||||
<p className="muted" style={{ margin: "4px 0 0", fontSize: "0.85rem" }}>Create a free account to explore.</p>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className="center" style={{ marginTop: 26 }}>
|
||||
<Link href="/signup" className="btn big">Create a free account</Link>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="section" style={{ background: "var(--surface)", borderTop: "1px solid var(--line)" }}>
|
||||
<div className="container">
|
||||
<h2 className="center">How DrawIt works</h2>
|
||||
<div style={{ display: "grid", gap: 16, gridTemplateColumns: "repeat(auto-fit, minmax(220px, 1fr))", marginTop: 18 }}>
|
||||
<div className="card"><h3>✏️ Outline → Details</h3><p className="muted">Trace the coloring-book outline, then add the details that bring it to life.</p></div>
|
||||
<div className="card"><h3>🎨 Color it in</h3><p className="muted">Finish with the coloring studio — color, shade, and make it your own.</p></div>
|
||||
<div className="card"><h3>🔒 Private by design</h3><p className="muted">No analytics, no trackers, no data sold or shared. Just drawing.</p></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<footer className="site">DrawIt · Free & open source · No data collected. Made for young artists. 🎨</footer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function card(locked: boolean): React.CSSProperties {
|
||||
return { position: "relative", textDecoration: "none", color: "inherit", background: "var(--surface)", borderRadius: "var(--radius)", border: "2px solid var(--line)", padding: 18, opacity: locked ? 0.6 : 1, filter: locked ? "grayscale(0.7)" : "none" };
|
||||
}
|
||||
function tag(free: boolean): React.CSSProperties {
|
||||
return { position: "absolute", top: 12, right: 12, fontWeight: 800, fontSize: "0.8rem", padding: "3px 10px", borderRadius: 999, background: free ? "#e8f7ee" : "#f0ebf7", color: free ? "#1f7a45" : "var(--ink-soft)" };
|
||||
}
|
||||
|
||||
@@ -0,0 +1,405 @@
|
||||
/* AUTO-GENERATED by gen_animals.py — Early Beginner animals (original cute doodles). */
|
||||
import type { Lesson, LessonStep } from "./curriculum";
|
||||
|
||||
const LION_OUTLINE_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`The mane`, instruction:`Draw a big circle for the fluffy mane.`, lines:[`<circle cx="200" cy="150" r="86" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:2, title:`The face`, instruction:`Draw a round face inside.`, lines:[`<circle cx="200" cy="150" r="60" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:3, title:`The ears`, instruction:`Add two little ears.`, lines:[`<circle cx="152" cy="108" r="16" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<circle cx="248" cy="108" r="16" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const LION_DETAIL_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`Eyes & nose`, instruction:`Add eyes and a nose.`, lines:[`<circle cx="182" cy="144" r="6" fill="#2b2440"/>`,`<circle cx="218" cy="144" r="6" fill="#2b2440"/>`,`<path d="M192 164 L208 164 L200 174 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:2, title:`Big smile`, instruction:`Add the mouth.`, lines:[`<path d="M200 174 L200 184" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M200 184 q-12 10 -22 2" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M200 184 q12 10 22 2" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:3, title:`Mane fluff`, instruction:`Add fluffy lines around the mane.`, lines:[`<path d="M200 64 L200 50" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M258 82 L270 72" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M142 82 L130 72" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M286 150 L300 150" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M114 150 L100 150" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M258 218 L270 228" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M142 218 L130 228" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M200 236 L200 250" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const LION_OUTLINE_SVG = LION_OUTLINE_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
const LION_DETAIL_SVG = LION_OUTLINE_SVG + "\n" + LION_DETAIL_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
export const LION_OUTLINE_LESSON: Lesson = { level:"early-beginner", subjectKey:"lion", subjectName:`Lion`, subjectEmoji:`🦁`, emoji:`🦁`, order:20, sublevel:201, slug:"lion-outline", title:`Lion \u00b7 Outline`, subject:`lion outline`, intro:`Let\u2019s draw Lion\u2019s outline, one line at a time. Watch each line, then trace it!`, badgeKey:"", badgeName:`Lion Legend`, phase:"outline", steps:LION_OUTLINE_STEPS };
|
||||
export const LION_DETAIL_LESSON: Lesson = { level:"early-beginner", subjectKey:"lion", subjectName:`Lion`, subjectEmoji:`🦁`, emoji:`🦁`, order:20, sublevel:202, slug:"lion-detail", title:`Lion \u00b7 Details`, subject:`lion details`, intro:`Now add the details that bring your lion to life.`, badgeKey:"early-beginner-20-lion", badgeName:`Lion Legend`, phase:"detail", baseSvg:LION_OUTLINE_SVG, steps:LION_DETAIL_STEPS };
|
||||
export const LION_EXTRA_LESSON: Lesson = { level:"early-beginner", subjectKey:"lion", subjectName:`Lion`, subjectEmoji:`🦁`, emoji:`🦁`, order:20, sublevel:203, slug:"lion-extra", title:`Lion \u00b7 Color it!`, subject:`color the lion`, intro:`Bring your lion to life! Color it in.`, badgeKey:"", badgeName:`Lion Legend`, phase:"extra", baseSvg:LION_DETAIL_SVG, steps:[] };
|
||||
|
||||
const ELEPHANT_OUTLINE_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`The body`, instruction:`Draw a big round body.`, lines:[`<ellipse cx="214" cy="172" rx="82" ry="58" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:2, title:`The head`, instruction:`Add a round head on the left.`, lines:[`<circle cx="150" cy="150" r="52" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:3, title:`The trunk`, instruction:`Curl a trunk down from the head.`, lines:[`<path d="M118 158 C 96 180 100 220 120 232 C 132 236 140 226 132 216 C 122 206 126 188 142 178" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:4, title:`The legs`, instruction:`Add chunky legs.`, lines:[`<path d="M172 224 L172 254" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M214 226 L214 256" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M256 220 L256 250" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const ELEPHANT_DETAIL_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`The ear`, instruction:`Add a big floppy ear.`, lines:[`<path d="M150 110 C 108 116 108 180 152 176" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:2, title:`Eye & tusk`, instruction:`Add an eye and a tusk.`, lines:[`<circle cx="138" cy="146" r="5" fill="#2b2440"/>`,`<path d="M128 198 q-10 12 0 20" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:3, title:`The tail`, instruction:`Add a little tail.`, lines:[`<path d="M292 170 q22 8 14 34" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const ELEPHANT_OUTLINE_SVG = ELEPHANT_OUTLINE_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
const ELEPHANT_DETAIL_SVG = ELEPHANT_OUTLINE_SVG + "\n" + ELEPHANT_DETAIL_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
export const ELEPHANT_OUTLINE_LESSON: Lesson = { level:"early-beginner", subjectKey:"elephant", subjectName:`Elephant`, subjectEmoji:`🐘`, emoji:`🐘`, order:21, sublevel:211, slug:"elephant-outline", title:`Elephant \u00b7 Outline`, subject:`elephant outline`, intro:`Let\u2019s draw Elephant\u2019s outline, one line at a time. Watch each line, then trace it!`, badgeKey:"", badgeName:`Elephant Pal`, phase:"outline", steps:ELEPHANT_OUTLINE_STEPS };
|
||||
export const ELEPHANT_DETAIL_LESSON: Lesson = { level:"early-beginner", subjectKey:"elephant", subjectName:`Elephant`, subjectEmoji:`🐘`, emoji:`🐘`, order:21, sublevel:212, slug:"elephant-detail", title:`Elephant \u00b7 Details`, subject:`elephant details`, intro:`Now add the details that bring your elephant to life.`, badgeKey:"early-beginner-21-elephant", badgeName:`Elephant Pal`, phase:"detail", baseSvg:ELEPHANT_OUTLINE_SVG, steps:ELEPHANT_DETAIL_STEPS };
|
||||
export const ELEPHANT_EXTRA_LESSON: Lesson = { level:"early-beginner", subjectKey:"elephant", subjectName:`Elephant`, subjectEmoji:`🐘`, emoji:`🐘`, order:21, sublevel:213, slug:"elephant-extra", title:`Elephant \u00b7 Color it!`, subject:`color the elephant`, intro:`Bring your elephant to life! Color it in.`, badgeKey:"", badgeName:`Elephant Pal`, phase:"extra", baseSvg:ELEPHANT_DETAIL_SVG, steps:[] };
|
||||
|
||||
const GIRAFFE_OUTLINE_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`The body`, instruction:`Draw an oval body.`, lines:[`<ellipse cx="212" cy="202" rx="60" ry="38" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:2, title:`The neck`, instruction:`Draw a long tall neck.`, lines:[`<path d="M152 188 L140 102 L174 102 L188 192 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:3, title:`The head`, instruction:`Add a small head on top.`, lines:[`<ellipse cx="152" cy="94" rx="30" ry="20" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:4, title:`The legs`, instruction:`Add four long legs.`, lines:[`<path d="M178 236 L178 264" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M206 236 L206 264" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M236 232 L236 262" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M252 226 L252 258" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const GIRAFFE_DETAIL_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`Horns & ear`, instruction:`Add little horns and an ear.`, lines:[`<path d="M142 78 L140 66" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M162 78 L164 66" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<circle cx="140" cy="64" r="4" fill="#2b2440"/>`,`<circle cx="164" cy="64" r="4" fill="#2b2440"/>`,`<path d="M128 92 q-12 -2 -14 8" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:2, title:`The face`, instruction:`Add an eye and a snout.`, lines:[`<circle cx="142" cy="90" r="4" fill="#2b2440"/>`,`<path d="M126 100 q-8 6 -2 12" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:3, title:`Spots`, instruction:`Add some spots.`, lines:[`<circle cx="206" cy="196" r="7" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<circle cx="228" cy="206" r="6" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<circle cx="196" cy="214" r="6" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<circle cx="152" cy="150" r="6" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<circle cx="158" cy="176" r="5" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const GIRAFFE_OUTLINE_SVG = GIRAFFE_OUTLINE_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
const GIRAFFE_DETAIL_SVG = GIRAFFE_OUTLINE_SVG + "\n" + GIRAFFE_DETAIL_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
export const GIRAFFE_OUTLINE_LESSON: Lesson = { level:"early-beginner", subjectKey:"giraffe", subjectName:`Giraffe`, subjectEmoji:`🦒`, emoji:`🦒`, order:22, sublevel:221, slug:"giraffe-outline", title:`Giraffe \u00b7 Outline`, subject:`giraffe outline`, intro:`Let\u2019s draw Giraffe\u2019s outline, one line at a time. Watch each line, then trace it!`, badgeKey:"", badgeName:`Tall Giraffe`, phase:"outline", steps:GIRAFFE_OUTLINE_STEPS };
|
||||
export const GIRAFFE_DETAIL_LESSON: Lesson = { level:"early-beginner", subjectKey:"giraffe", subjectName:`Giraffe`, subjectEmoji:`🦒`, emoji:`🦒`, order:22, sublevel:222, slug:"giraffe-detail", title:`Giraffe \u00b7 Details`, subject:`giraffe details`, intro:`Now add the details that bring your giraffe to life.`, badgeKey:"early-beginner-22-giraffe", badgeName:`Tall Giraffe`, phase:"detail", baseSvg:GIRAFFE_OUTLINE_SVG, steps:GIRAFFE_DETAIL_STEPS };
|
||||
export const GIRAFFE_EXTRA_LESSON: Lesson = { level:"early-beginner", subjectKey:"giraffe", subjectName:`Giraffe`, subjectEmoji:`🦒`, emoji:`🦒`, order:22, sublevel:223, slug:"giraffe-extra", title:`Giraffe \u00b7 Color it!`, subject:`color the giraffe`, intro:`Bring your giraffe to life! Color it in.`, badgeKey:"", badgeName:`Tall Giraffe`, phase:"extra", baseSvg:GIRAFFE_DETAIL_SVG, steps:[] };
|
||||
|
||||
const ZEBRA_OUTLINE_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`The body`, instruction:`Draw an oval body.`, lines:[`<ellipse cx="206" cy="176" rx="78" ry="46" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:2, title:`The head`, instruction:`Add a head up to the left.`, lines:[`<ellipse cx="118" cy="148" rx="32" ry="22" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:3, title:`The neck`, instruction:`Join the head to the body.`, lines:[`<path d="M138 138 L170 130 L182 176 L150 180 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:4, title:`The legs`, instruction:`Add four legs.`, lines:[`<path d="M172 218 L172 254" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M202 220 L202 256" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M236 216 L236 252" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M256 212 L256 248" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const ZEBRA_DETAIL_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`The face`, instruction:`Add an eye, snout and ear.`, lines:[`<circle cx="110" cy="144" r="4" fill="#2b2440"/>`,`<path d="M92 152 q-8 4 -2 12" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M120 128 l4 -12" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:2, title:`Mane & tail`, instruction:`Add a spiky mane and a tail.`, lines:[`<path d="M140 120 l6 -10" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M150 122 l6 -10" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M284 162 q18 6 12 30" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:3, title:`Stripes`, instruction:`Add bold stripes.`, lines:[`<path d="M180 136 L176 216" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M205 132 L205 220" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M230 138 L234 214" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const ZEBRA_OUTLINE_SVG = ZEBRA_OUTLINE_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
const ZEBRA_DETAIL_SVG = ZEBRA_OUTLINE_SVG + "\n" + ZEBRA_DETAIL_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
export const ZEBRA_OUTLINE_LESSON: Lesson = { level:"early-beginner", subjectKey:"zebra", subjectName:`Zebra`, subjectEmoji:`🦓`, emoji:`🦓`, order:23, sublevel:231, slug:"zebra-outline", title:`Zebra \u00b7 Outline`, subject:`zebra outline`, intro:`Let\u2019s draw Zebra\u2019s outline, one line at a time. Watch each line, then trace it!`, badgeKey:"", badgeName:`Stripe Star`, phase:"outline", steps:ZEBRA_OUTLINE_STEPS };
|
||||
export const ZEBRA_DETAIL_LESSON: Lesson = { level:"early-beginner", subjectKey:"zebra", subjectName:`Zebra`, subjectEmoji:`🦓`, emoji:`🦓`, order:23, sublevel:232, slug:"zebra-detail", title:`Zebra \u00b7 Details`, subject:`zebra details`, intro:`Now add the details that bring your zebra to life.`, badgeKey:"early-beginner-23-zebra", badgeName:`Stripe Star`, phase:"detail", baseSvg:ZEBRA_OUTLINE_SVG, steps:ZEBRA_DETAIL_STEPS };
|
||||
export const ZEBRA_EXTRA_LESSON: Lesson = { level:"early-beginner", subjectKey:"zebra", subjectName:`Zebra`, subjectEmoji:`🦓`, emoji:`🦓`, order:23, sublevel:233, slug:"zebra-extra", title:`Zebra \u00b7 Color it!`, subject:`color the zebra`, intro:`Bring your zebra to life! Color it in.`, badgeKey:"", badgeName:`Stripe Star`, phase:"extra", baseSvg:ZEBRA_DETAIL_SVG, steps:[] };
|
||||
|
||||
const RHINO_OUTLINE_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`The body`, instruction:`Draw a big oval body.`, lines:[`<ellipse cx="208" cy="176" rx="82" ry="52" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:2, title:`The head`, instruction:`Add a big head on the left.`, lines:[`<ellipse cx="120" cy="170" rx="46" ry="38" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:3, title:`The horn`, instruction:`Add a pointy horn.`, lines:[`<path d="M86 152 L74 120 L102 148 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:4, title:`The legs`, instruction:`Add sturdy legs.`, lines:[`<path d="M168 224 L168 254" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M208 226 L208 256" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M250 222 L250 252" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const RHINO_DETAIL_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`Eye & ear`, instruction:`Add an eye and an ear.`, lines:[`<circle cx="120" cy="152" r="4" fill="#2b2440"/>`,`<path d="M140 132 q6 -12 16 -8" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:2, title:`Small horn & mouth`, instruction:`Add a second horn and mouth.`, lines:[`<path d="M106 152 q6 -10 14 -6" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M84 180 q-10 4 -4 10" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:3, title:`The tail`, instruction:`Add a little tail.`, lines:[`<path d="M288 170 q20 6 14 30" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const RHINO_OUTLINE_SVG = RHINO_OUTLINE_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
const RHINO_DETAIL_SVG = RHINO_OUTLINE_SVG + "\n" + RHINO_DETAIL_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
export const RHINO_OUTLINE_LESSON: Lesson = { level:"early-beginner", subjectKey:"rhino", subjectName:`Rhino`, subjectEmoji:`🦏`, emoji:`🦏`, order:24, sublevel:241, slug:"rhino-outline", title:`Rhino \u00b7 Outline`, subject:`rhino outline`, intro:`Let\u2019s draw Rhino\u2019s outline, one line at a time. Watch each line, then trace it!`, badgeKey:"", badgeName:`Rhino Rumble`, phase:"outline", steps:RHINO_OUTLINE_STEPS };
|
||||
export const RHINO_DETAIL_LESSON: Lesson = { level:"early-beginner", subjectKey:"rhino", subjectName:`Rhino`, subjectEmoji:`🦏`, emoji:`🦏`, order:24, sublevel:242, slug:"rhino-detail", title:`Rhino \u00b7 Details`, subject:`rhino details`, intro:`Now add the details that bring your rhino to life.`, badgeKey:"early-beginner-24-rhino", badgeName:`Rhino Rumble`, phase:"detail", baseSvg:RHINO_OUTLINE_SVG, steps:RHINO_DETAIL_STEPS };
|
||||
export const RHINO_EXTRA_LESSON: Lesson = { level:"early-beginner", subjectKey:"rhino", subjectName:`Rhino`, subjectEmoji:`🦏`, emoji:`🦏`, order:24, sublevel:243, slug:"rhino-extra", title:`Rhino \u00b7 Color it!`, subject:`color the rhino`, intro:`Bring your rhino to life! Color it in.`, badgeKey:"", badgeName:`Rhino Rumble`, phase:"extra", baseSvg:RHINO_DETAIL_SVG, steps:[] };
|
||||
|
||||
const LEOPARD_OUTLINE_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`The head`, instruction:`Draw a round head.`, lines:[`<circle cx="200" cy="120" r="46" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:2, title:`The ears`, instruction:`Add two ears.`, lines:[`<circle cx="168" cy="86" r="16" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<circle cx="232" cy="86" r="16" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:3, title:`The body`, instruction:`Add a sitting body.`, lines:[`<ellipse cx="200" cy="212" rx="58" ry="52" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:4, title:`The paws`, instruction:`Add two front paws.`, lines:[`<path d="M172 252 q0 14 14 14" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M228 252 q0 14 -14 14" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const LEOPARD_DETAIL_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`The face`, instruction:`Add eyes, nose and mouth.`, lines:[`<circle cx="184" cy="118" r="5" fill="#2b2440"/>`,`<circle cx="216" cy="118" r="5" fill="#2b2440"/>`,`<path d="M194 132 L206 132 L200 140 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M200 140 L200 148" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:2, title:`Whiskers & tail`, instruction:`Add whiskers and a curvy tail.`, lines:[`<path d="M168 140 l-26 -4" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M232 140 l26 -4" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M256 232 q30 0 26 -34" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:3, title:`Spots`, instruction:`Add leopard spots.`, lines:[`<circle cx="190" cy="200" r="5" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<circle cx="212" cy="206" r="5" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<circle cx="200" cy="226" r="5" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<circle cx="180" cy="222" r="4" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<circle cx="220" cy="224" r="4" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const LEOPARD_OUTLINE_SVG = LEOPARD_OUTLINE_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
const LEOPARD_DETAIL_SVG = LEOPARD_OUTLINE_SVG + "\n" + LEOPARD_DETAIL_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
export const LEOPARD_OUTLINE_LESSON: Lesson = { level:"early-beginner", subjectKey:"leopard", subjectName:`Leopard`, subjectEmoji:`🐆`, emoji:`🐆`, order:25, sublevel:251, slug:"leopard-outline", title:`Leopard \u00b7 Outline`, subject:`leopard outline`, intro:`Let\u2019s draw Leopard\u2019s outline, one line at a time. Watch each line, then trace it!`, badgeKey:"", badgeName:`Spotty Leopard`, phase:"outline", steps:LEOPARD_OUTLINE_STEPS };
|
||||
export const LEOPARD_DETAIL_LESSON: Lesson = { level:"early-beginner", subjectKey:"leopard", subjectName:`Leopard`, subjectEmoji:`🐆`, emoji:`🐆`, order:25, sublevel:252, slug:"leopard-detail", title:`Leopard \u00b7 Details`, subject:`leopard details`, intro:`Now add the details that bring your leopard to life.`, badgeKey:"early-beginner-25-leopard", badgeName:`Spotty Leopard`, phase:"detail", baseSvg:LEOPARD_OUTLINE_SVG, steps:LEOPARD_DETAIL_STEPS };
|
||||
export const LEOPARD_EXTRA_LESSON: Lesson = { level:"early-beginner", subjectKey:"leopard", subjectName:`Leopard`, subjectEmoji:`🐆`, emoji:`🐆`, order:25, sublevel:253, slug:"leopard-extra", title:`Leopard \u00b7 Color it!`, subject:`color the leopard`, intro:`Bring your leopard to life! Color it in.`, badgeKey:"", badgeName:`Spotty Leopard`, phase:"extra", baseSvg:LEOPARD_DETAIL_SVG, steps:[] };
|
||||
|
||||
const TIGER_OUTLINE_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`The head`, instruction:`Draw a round head.`, lines:[`<circle cx="200" cy="140" r="58" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:2, title:`The ears`, instruction:`Add two ears.`, lines:[`<circle cx="160" cy="98" r="18" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<circle cx="240" cy="98" r="18" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:3, title:`The body`, instruction:`Add a sitting body.`, lines:[`<ellipse cx="200" cy="228" rx="52" ry="40" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const TIGER_DETAIL_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`The face`, instruction:`Add eyes, nose and mouth.`, lines:[`<circle cx="182" cy="138" r="6" fill="#2b2440"/>`,`<circle cx="218" cy="138" r="6" fill="#2b2440"/>`,`<path d="M192 154 L208 154 L200 162 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M200 162 L200 170" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M200 170 q-10 8 -18 2" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M200 170 q10 8 18 2" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:2, title:`Cheeks`, instruction:`Add fluffy cheeks.`, lines:[`<path d="M150 150 q-22 6 -28 18" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M250 150 q22 6 28 18" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:3, title:`Stripes`, instruction:`Add tiger stripes.`, lines:[`<path d="M200 84 L200 100" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M168 96 l-8 -12" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M232 96 l8 -12" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M150 140 l-16 -6" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M250 140 l16 -6" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const TIGER_OUTLINE_SVG = TIGER_OUTLINE_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
const TIGER_DETAIL_SVG = TIGER_OUTLINE_SVG + "\n" + TIGER_DETAIL_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
export const TIGER_OUTLINE_LESSON: Lesson = { level:"early-beginner", subjectKey:"tiger", subjectName:`Tiger`, subjectEmoji:`🐯`, emoji:`🐯`, order:26, sublevel:261, slug:"tiger-outline", title:`Tiger \u00b7 Outline`, subject:`tiger outline`, intro:`Let\u2019s draw Tiger\u2019s outline, one line at a time. Watch each line, then trace it!`, badgeKey:"", badgeName:`Tiger Power`, phase:"outline", steps:TIGER_OUTLINE_STEPS };
|
||||
export const TIGER_DETAIL_LESSON: Lesson = { level:"early-beginner", subjectKey:"tiger", subjectName:`Tiger`, subjectEmoji:`🐯`, emoji:`🐯`, order:26, sublevel:262, slug:"tiger-detail", title:`Tiger \u00b7 Details`, subject:`tiger details`, intro:`Now add the details that bring your tiger to life.`, badgeKey:"early-beginner-26-tiger", badgeName:`Tiger Power`, phase:"detail", baseSvg:TIGER_OUTLINE_SVG, steps:TIGER_DETAIL_STEPS };
|
||||
export const TIGER_EXTRA_LESSON: Lesson = { level:"early-beginner", subjectKey:"tiger", subjectName:`Tiger`, subjectEmoji:`🐯`, emoji:`🐯`, order:26, sublevel:263, slug:"tiger-extra", title:`Tiger \u00b7 Color it!`, subject:`color the tiger`, intro:`Bring your tiger to life! Color it in.`, badgeKey:"", badgeName:`Tiger Power`, phase:"extra", baseSvg:TIGER_DETAIL_SVG, steps:[] };
|
||||
|
||||
const MONKEY_OUTLINE_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`The head`, instruction:`Draw a round head.`, lines:[`<circle cx="200" cy="140" r="56" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:2, title:`The ears`, instruction:`Add two big round ears.`, lines:[`<circle cx="146" cy="140" r="22" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<circle cx="254" cy="140" r="22" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:3, title:`The face`, instruction:`Add a face patch.`, lines:[`<ellipse cx="200" cy="158" rx="40" ry="42" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const MONKEY_DETAIL_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`The eyes`, instruction:`Add two eyes.`, lines:[`<circle cx="184" cy="140" r="9" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<circle cx="184" cy="140" r="4" fill="#2b2440"/>`,`<circle cx="216" cy="140" r="9" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<circle cx="216" cy="140" r="4" fill="#2b2440"/>`] },
|
||||
{ n:2, title:`Nose & mouth`, instruction:`Add a nose and a smile.`, lines:[`<circle cx="192" cy="166" r="3" fill="#2b2440"/>`,`<circle cx="208" cy="166" r="3" fill="#2b2440"/>`,`<path d="M182 180 q18 12 36 0" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:3, title:`Ear insides`, instruction:`Add the inside of the ears.`, lines:[`<circle cx="146" cy="140" r="11" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<circle cx="254" cy="140" r="11" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const MONKEY_OUTLINE_SVG = MONKEY_OUTLINE_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
const MONKEY_DETAIL_SVG = MONKEY_OUTLINE_SVG + "\n" + MONKEY_DETAIL_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
export const MONKEY_OUTLINE_LESSON: Lesson = { level:"early-beginner", subjectKey:"monkey", subjectName:`Monkey`, subjectEmoji:`🐵`, emoji:`🐵`, order:27, sublevel:271, slug:"monkey-outline", title:`Monkey \u00b7 Outline`, subject:`monkey outline`, intro:`Let\u2019s draw Monkey\u2019s outline, one line at a time. Watch each line, then trace it!`, badgeKey:"", badgeName:`Cheeky Monkey`, phase:"outline", steps:MONKEY_OUTLINE_STEPS };
|
||||
export const MONKEY_DETAIL_LESSON: Lesson = { level:"early-beginner", subjectKey:"monkey", subjectName:`Monkey`, subjectEmoji:`🐵`, emoji:`🐵`, order:27, sublevel:272, slug:"monkey-detail", title:`Monkey \u00b7 Details`, subject:`monkey details`, intro:`Now add the details that bring your monkey to life.`, badgeKey:"early-beginner-27-monkey", badgeName:`Cheeky Monkey`, phase:"detail", baseSvg:MONKEY_OUTLINE_SVG, steps:MONKEY_DETAIL_STEPS };
|
||||
export const MONKEY_EXTRA_LESSON: Lesson = { level:"early-beginner", subjectKey:"monkey", subjectName:`Monkey`, subjectEmoji:`🐵`, emoji:`🐵`, order:27, sublevel:273, slug:"monkey-extra", title:`Monkey \u00b7 Color it!`, subject:`color the monkey`, intro:`Bring your monkey to life! Color it in.`, badgeKey:"", badgeName:`Cheeky Monkey`, phase:"extra", baseSvg:MONKEY_DETAIL_SVG, steps:[] };
|
||||
|
||||
const CROCODILE_OUTLINE_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`The body`, instruction:`Draw a long body and snout.`, lines:[`<path d="M70 196 C 130 176 250 178 330 190 L368 188 L356 204 L368 214 C 250 230 120 230 80 214 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:2, title:`The legs`, instruction:`Add short legs.`, lines:[`<path d="M150 222 l-8 18 l16 0" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M250 224 l-8 18 l16 0" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:3, title:`The tail tip`, instruction:`Point the tail.`, lines:[`<path d="M80 200 L44 186 L52 208 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const CROCODILE_DETAIL_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`The eye`, instruction:`Add a bumpy eye.`, lines:[`<circle cx="120" cy="176" r="7" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<circle cx="120" cy="176" r="4" fill="#2b2440"/>`] },
|
||||
{ n:2, title:`Teeth`, instruction:`Add a toothy grin.`, lines:[`<path d="M110 214 l6 8" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M130 216 l6 8" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M150 216 l6 8" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M170 216 l6 8" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:3, title:`Back ridges`, instruction:`Add spikes on the back.`, lines:[`<path d="M180 178 l8 -10 l8 10" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M210 178 l8 -10 l8 10" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M240 180 l8 -10 l8 10" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const CROCODILE_OUTLINE_SVG = CROCODILE_OUTLINE_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
const CROCODILE_DETAIL_SVG = CROCODILE_OUTLINE_SVG + "\n" + CROCODILE_DETAIL_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
export const CROCODILE_OUTLINE_LESSON: Lesson = { level:"early-beginner", subjectKey:"crocodile", subjectName:`Crocodile`, subjectEmoji:`🐊`, emoji:`🐊`, order:28, sublevel:281, slug:"crocodile-outline", title:`Crocodile \u00b7 Outline`, subject:`crocodile outline`, intro:`Let\u2019s draw Crocodile\u2019s outline, one line at a time. Watch each line, then trace it!`, badgeKey:"", badgeName:`Snappy Croc`, phase:"outline", steps:CROCODILE_OUTLINE_STEPS };
|
||||
export const CROCODILE_DETAIL_LESSON: Lesson = { level:"early-beginner", subjectKey:"crocodile", subjectName:`Crocodile`, subjectEmoji:`🐊`, emoji:`🐊`, order:28, sublevel:282, slug:"crocodile-detail", title:`Crocodile \u00b7 Details`, subject:`crocodile details`, intro:`Now add the details that bring your crocodile to life.`, badgeKey:"early-beginner-28-crocodile", badgeName:`Snappy Croc`, phase:"detail", baseSvg:CROCODILE_OUTLINE_SVG, steps:CROCODILE_DETAIL_STEPS };
|
||||
export const CROCODILE_EXTRA_LESSON: Lesson = { level:"early-beginner", subjectKey:"crocodile", subjectName:`Crocodile`, subjectEmoji:`🐊`, emoji:`🐊`, order:28, sublevel:283, slug:"crocodile-extra", title:`Crocodile \u00b7 Color it!`, subject:`color the crocodile`, intro:`Bring your crocodile to life! Color it in.`, badgeKey:"", badgeName:`Snappy Croc`, phase:"extra", baseSvg:CROCODILE_DETAIL_SVG, steps:[] };
|
||||
|
||||
const FOX_OUTLINE_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`The face`, instruction:`Draw a pointy fox face.`, lines:[`<path d="M160 138 Q200 92 240 138 Q220 176 200 176 Q180 176 160 138 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:2, title:`The ears`, instruction:`Add two pointy ears.`, lines:[`<path d="M166 120 L150 80 L190 110 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M234 120 L250 80 L210 110 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:3, title:`The body`, instruction:`Add a sitting body.`, lines:[`<ellipse cx="200" cy="222" rx="46" ry="42" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:4, title:`The tail`, instruction:`Add a bushy tail.`, lines:[`<path d="M242 232 C 290 222 296 182 270 167 C 286 197 262 222 236 224 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const FOX_DETAIL_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`The face`, instruction:`Add eyes and a nose.`, lines:[`<circle cx="184" cy="135" r="5" fill="#2b2440"/>`,`<circle cx="216" cy="135" r="5" fill="#2b2440"/>`,`<path d="M194 150 L206 150 L200 158 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:2, title:`Cheeks`, instruction:`Add fluffy cheeks.`, lines:[`<path d="M176 150 q-14 6 -16 18" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M224 150 q14 6 16 18" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:3, title:`Ear insides`, instruction:`Color the ears.`, lines:[`<path d="M172 110 L162 88 L182 104 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M228 110 L238 88 L218 104 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const FOX_OUTLINE_SVG = FOX_OUTLINE_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
const FOX_DETAIL_SVG = FOX_OUTLINE_SVG + "\n" + FOX_DETAIL_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
export const FOX_OUTLINE_LESSON: Lesson = { level:"early-beginner", subjectKey:"fox", subjectName:`Fox`, subjectEmoji:`🦊`, emoji:`🦊`, order:29, sublevel:291, slug:"fox-outline", title:`Fox \u00b7 Outline`, subject:`fox outline`, intro:`Let\u2019s draw Fox\u2019s outline, one line at a time. Watch each line, then trace it!`, badgeKey:"", badgeName:`Foxy Friend`, phase:"outline", steps:FOX_OUTLINE_STEPS };
|
||||
export const FOX_DETAIL_LESSON: Lesson = { level:"early-beginner", subjectKey:"fox", subjectName:`Fox`, subjectEmoji:`🦊`, emoji:`🦊`, order:29, sublevel:292, slug:"fox-detail", title:`Fox \u00b7 Details`, subject:`fox details`, intro:`Now add the details that bring your fox to life.`, badgeKey:"early-beginner-29-fox", badgeName:`Foxy Friend`, phase:"detail", baseSvg:FOX_OUTLINE_SVG, steps:FOX_DETAIL_STEPS };
|
||||
export const FOX_EXTRA_LESSON: Lesson = { level:"early-beginner", subjectKey:"fox", subjectName:`Fox`, subjectEmoji:`🦊`, emoji:`🦊`, order:29, sublevel:293, slug:"fox-extra", title:`Fox \u00b7 Color it!`, subject:`color the fox`, intro:`Bring your fox to life! Color it in.`, badgeKey:"", badgeName:`Foxy Friend`, phase:"extra", baseSvg:FOX_DETAIL_SVG, steps:[] };
|
||||
|
||||
const BEAR_OUTLINE_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`The head`, instruction:`Draw a round head.`, lines:[`<circle cx="200" cy="130" r="52" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:2, title:`The ears`, instruction:`Add two round ears.`, lines:[`<circle cx="162" cy="96" r="18" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<circle cx="238" cy="96" r="18" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:3, title:`The body`, instruction:`Add a cuddly body.`, lines:[`<ellipse cx="200" cy="216" rx="54" ry="46" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const BEAR_DETAIL_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`The snout`, instruction:`Add a snout and nose.`, lines:[`<ellipse cx="200" cy="150" rx="22" ry="16" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<circle cx="200" cy="142" r="5" fill="#2b2440"/>`] },
|
||||
{ n:2, title:`The eyes`, instruction:`Add two eyes.`, lines:[`<circle cx="182" cy="124" r="5" fill="#2b2440"/>`,`<circle cx="218" cy="124" r="5" fill="#2b2440"/>`] },
|
||||
{ n:3, title:`Ear insides & tummy`, instruction:`Add details.`, lines:[`<circle cx="162" cy="96" r="9" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<circle cx="238" cy="96" r="9" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M200 186 q0 22 0 26" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const BEAR_OUTLINE_SVG = BEAR_OUTLINE_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
const BEAR_DETAIL_SVG = BEAR_OUTLINE_SVG + "\n" + BEAR_DETAIL_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
export const BEAR_OUTLINE_LESSON: Lesson = { level:"early-beginner", subjectKey:"bear", subjectName:`Bear`, subjectEmoji:`🐻`, emoji:`🐻`, order:30, sublevel:301, slug:"bear-outline", title:`Bear \u00b7 Outline`, subject:`bear outline`, intro:`Let\u2019s draw Bear\u2019s outline, one line at a time. Watch each line, then trace it!`, badgeKey:"", badgeName:`Bear Hug`, phase:"outline", steps:BEAR_OUTLINE_STEPS };
|
||||
export const BEAR_DETAIL_LESSON: Lesson = { level:"early-beginner", subjectKey:"bear", subjectName:`Bear`, subjectEmoji:`🐻`, emoji:`🐻`, order:30, sublevel:302, slug:"bear-detail", title:`Bear \u00b7 Details`, subject:`bear details`, intro:`Now add the details that bring your bear to life.`, badgeKey:"early-beginner-30-bear", badgeName:`Bear Hug`, phase:"detail", baseSvg:BEAR_OUTLINE_SVG, steps:BEAR_DETAIL_STEPS };
|
||||
export const BEAR_EXTRA_LESSON: Lesson = { level:"early-beginner", subjectKey:"bear", subjectName:`Bear`, subjectEmoji:`🐻`, emoji:`🐻`, order:30, sublevel:303, slug:"bear-extra", title:`Bear \u00b7 Color it!`, subject:`color the bear`, intro:`Bring your bear to life! Color it in.`, badgeKey:"", badgeName:`Bear Hug`, phase:"extra", baseSvg:BEAR_DETAIL_SVG, steps:[] };
|
||||
|
||||
const DEER_OUTLINE_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`The head`, instruction:`Draw an oval head.`, lines:[`<ellipse cx="200" cy="150" rx="40" ry="46" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:2, title:`The ears`, instruction:`Add two ears.`, lines:[`<ellipse cx="160" cy="120" rx="15" ry="26" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<ellipse cx="240" cy="120" rx="15" ry="26" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:3, title:`The antlers`, instruction:`Add branchy antlers.`, lines:[`<path d="M178 110 L168 70 L156 84" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M178 110 L172 86" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M222 110 L232 70 L244 84" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M222 110 L228 86" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:4, title:`The body`, instruction:`Add a small body.`, lines:[`<ellipse cx="200" cy="226" rx="40" ry="34" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const DEER_DETAIL_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`The face`, instruction:`Add eyes and a nose.`, lines:[`<circle cx="186" cy="148" r="5" fill="#2b2440"/>`,`<circle cx="214" cy="148" r="5" fill="#2b2440"/>`,`<path d="M192 170 q8 8 16 0" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:2, title:`The snout`, instruction:`Add the snout line.`, lines:[`<path d="M200 162 L200 170" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const DEER_OUTLINE_SVG = DEER_OUTLINE_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
const DEER_DETAIL_SVG = DEER_OUTLINE_SVG + "\n" + DEER_DETAIL_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
export const DEER_OUTLINE_LESSON: Lesson = { level:"early-beginner", subjectKey:"deer", subjectName:`Deer`, subjectEmoji:`🦌`, emoji:`🦌`, order:31, sublevel:311, slug:"deer-outline", title:`Deer \u00b7 Outline`, subject:`deer outline`, intro:`Let\u2019s draw Deer\u2019s outline, one line at a time. Watch each line, then trace it!`, badgeKey:"", badgeName:`Deer Dear`, phase:"outline", steps:DEER_OUTLINE_STEPS };
|
||||
export const DEER_DETAIL_LESSON: Lesson = { level:"early-beginner", subjectKey:"deer", subjectName:`Deer`, subjectEmoji:`🦌`, emoji:`🦌`, order:31, sublevel:312, slug:"deer-detail", title:`Deer \u00b7 Details`, subject:`deer details`, intro:`Now add the details that bring your deer to life.`, badgeKey:"early-beginner-31-deer", badgeName:`Deer Dear`, phase:"detail", baseSvg:DEER_OUTLINE_SVG, steps:DEER_DETAIL_STEPS };
|
||||
export const DEER_EXTRA_LESSON: Lesson = { level:"early-beginner", subjectKey:"deer", subjectName:`Deer`, subjectEmoji:`🦌`, emoji:`🦌`, order:31, sublevel:313, slug:"deer-extra", title:`Deer \u00b7 Color it!`, subject:`color the deer`, intro:`Bring your deer to life! Color it in.`, badgeKey:"", badgeName:`Deer Dear`, phase:"extra", baseSvg:DEER_DETAIL_SVG, steps:[] };
|
||||
|
||||
const RABBIT_OUTLINE_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`The head`, instruction:`Draw a round head.`, lines:[`<circle cx="200" cy="162" r="44" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:2, title:`The ears`, instruction:`Add two tall ears.`, lines:[`<ellipse cx="180" cy="90" rx="15" ry="46" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<ellipse cx="220" cy="90" rx="15" ry="46" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:3, title:`The body`, instruction:`Add a round body.`, lines:[`<ellipse cx="200" cy="236" rx="40" ry="28" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const RABBIT_DETAIL_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`The face`, instruction:`Add eyes, nose and mouth.`, lines:[`<circle cx="186" cy="158" r="5" fill="#2b2440"/>`,`<circle cx="214" cy="158" r="5" fill="#2b2440"/>`,`<path d="M194 172 L206 172 L200 178 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M200 178 L200 184" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:2, title:`Ears & whiskers`, instruction:`Add ear insides and whiskers.`, lines:[`<ellipse cx="180" cy="92" rx="7" ry="30" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<ellipse cx="220" cy="92" rx="7" ry="30" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M176 174 l-24 -4" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M224 174 l24 -4" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const RABBIT_OUTLINE_SVG = RABBIT_OUTLINE_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
const RABBIT_DETAIL_SVG = RABBIT_OUTLINE_SVG + "\n" + RABBIT_DETAIL_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
export const RABBIT_OUTLINE_LESSON: Lesson = { level:"early-beginner", subjectKey:"rabbit", subjectName:`Rabbit`, subjectEmoji:`🐰`, emoji:`🐰`, order:32, sublevel:321, slug:"rabbit-outline", title:`Rabbit \u00b7 Outline`, subject:`rabbit outline`, intro:`Let\u2019s draw Rabbit\u2019s outline, one line at a time. Watch each line, then trace it!`, badgeKey:"", badgeName:`Hoppy Rabbit`, phase:"outline", steps:RABBIT_OUTLINE_STEPS };
|
||||
export const RABBIT_DETAIL_LESSON: Lesson = { level:"early-beginner", subjectKey:"rabbit", subjectName:`Rabbit`, subjectEmoji:`🐰`, emoji:`🐰`, order:32, sublevel:322, slug:"rabbit-detail", title:`Rabbit \u00b7 Details`, subject:`rabbit details`, intro:`Now add the details that bring your rabbit to life.`, badgeKey:"early-beginner-32-rabbit", badgeName:`Hoppy Rabbit`, phase:"detail", baseSvg:RABBIT_OUTLINE_SVG, steps:RABBIT_DETAIL_STEPS };
|
||||
export const RABBIT_EXTRA_LESSON: Lesson = { level:"early-beginner", subjectKey:"rabbit", subjectName:`Rabbit`, subjectEmoji:`🐰`, emoji:`🐰`, order:32, sublevel:323, slug:"rabbit-extra", title:`Rabbit \u00b7 Color it!`, subject:`color the rabbit`, intro:`Bring your rabbit to life! Color it in.`, badgeKey:"", badgeName:`Hoppy Rabbit`, phase:"extra", baseSvg:RABBIT_DETAIL_SVG, steps:[] };
|
||||
|
||||
const HEDGEHOG_OUTLINE_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`The body`, instruction:`Draw a round spiky body.`, lines:[`<path d="M96 212 C 92 156 150 126 206 130 C 262 134 292 168 292 212 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:2, title:`The snout`, instruction:`Add a pointy snout.`, lines:[`<path d="M292 200 C 320 196 332 206 326 216 C 320 224 300 222 292 214" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const HEDGEHOG_DETAIL_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`The spikes`, instruction:`Add zig-zag spikes.`, lines:[`<path d="M130 168 l10 -16 l9 16" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M168 146 l10 -16 l9 16" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M206 140 l10 -16 l9 16" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M244 150 l10 -16 l9 16" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:2, title:`The face`, instruction:`Add an eye and a nose.`, lines:[`<circle cx="300" cy="202" r="4" fill="#2b2440"/>`,`<circle cx="326" cy="212" r="4" fill="#2b2440"/>`] },
|
||||
{ n:3, title:`The feet`, instruction:`Add little feet.`, lines:[`<path d="M150 212 l0 12" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M200 212 l0 12" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M250 212 l0 12" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const HEDGEHOG_OUTLINE_SVG = HEDGEHOG_OUTLINE_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
const HEDGEHOG_DETAIL_SVG = HEDGEHOG_OUTLINE_SVG + "\n" + HEDGEHOG_DETAIL_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
export const HEDGEHOG_OUTLINE_LESSON: Lesson = { level:"early-beginner", subjectKey:"hedgehog", subjectName:`Hedgehog`, subjectEmoji:`🦔`, emoji:`🦔`, order:33, sublevel:331, slug:"hedgehog-outline", title:`Hedgehog \u00b7 Outline`, subject:`hedgehog outline`, intro:`Let\u2019s draw Hedgehog\u2019s outline, one line at a time. Watch each line, then trace it!`, badgeKey:"", badgeName:`Prickly Pal`, phase:"outline", steps:HEDGEHOG_OUTLINE_STEPS };
|
||||
export const HEDGEHOG_DETAIL_LESSON: Lesson = { level:"early-beginner", subjectKey:"hedgehog", subjectName:`Hedgehog`, subjectEmoji:`🦔`, emoji:`🦔`, order:33, sublevel:332, slug:"hedgehog-detail", title:`Hedgehog \u00b7 Details`, subject:`hedgehog details`, intro:`Now add the details that bring your hedgehog to life.`, badgeKey:"early-beginner-33-hedgehog", badgeName:`Prickly Pal`, phase:"detail", baseSvg:HEDGEHOG_OUTLINE_SVG, steps:HEDGEHOG_DETAIL_STEPS };
|
||||
export const HEDGEHOG_EXTRA_LESSON: Lesson = { level:"early-beginner", subjectKey:"hedgehog", subjectName:`Hedgehog`, subjectEmoji:`🦔`, emoji:`🦔`, order:33, sublevel:333, slug:"hedgehog-extra", title:`Hedgehog \u00b7 Color it!`, subject:`color the hedgehog`, intro:`Bring your hedgehog to life! Color it in.`, badgeKey:"", badgeName:`Prickly Pal`, phase:"extra", baseSvg:HEDGEHOG_DETAIL_SVG, steps:[] };
|
||||
|
||||
const OWL_OUTLINE_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`The body`, instruction:`Draw a rounded owl body.`, lines:[`<path d="M150 110 C 150 80 250 80 250 110 L250 210 C 250 240 150 240 150 210 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:2, title:`Ear tufts`, instruction:`Add two ear tufts.`, lines:[`<path d="M158 96 L150 72 L176 90 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M242 96 L250 72 L224 90 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:3, title:`The wings`, instruction:`Add two wings.`, lines:[`<path d="M152 140 q-12 40 6 70" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M248 140 q12 40 -6 70" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const OWL_DETAIL_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`Big eyes`, instruction:`Add two big eyes.`, lines:[`<circle cx="180" cy="130" r="18" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<circle cx="180" cy="130" r="7" fill="#2b2440"/>`,`<circle cx="220" cy="130" r="18" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<circle cx="220" cy="130" r="7" fill="#2b2440"/>`] },
|
||||
{ n:2, title:`Beak & feet`, instruction:`Add a beak and feet.`, lines:[`<path d="M200 142 L192 154 L208 154 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M186 236 l-6 10" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M186 236 l6 8" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M214 236 l6 10" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M214 236 l-6 8" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:3, title:`The tummy`, instruction:`Add a tummy curve.`, lines:[`<path d="M180 172 q20 20 40 0" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const OWL_OUTLINE_SVG = OWL_OUTLINE_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
const OWL_DETAIL_SVG = OWL_OUTLINE_SVG + "\n" + OWL_DETAIL_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
export const OWL_OUTLINE_LESSON: Lesson = { level:"early-beginner", subjectKey:"owl", subjectName:`Owl`, subjectEmoji:`🦉`, emoji:`🦉`, order:34, sublevel:341, slug:"owl-outline", title:`Owl \u00b7 Outline`, subject:`owl outline`, intro:`Let\u2019s draw Owl\u2019s outline, one line at a time. Watch each line, then trace it!`, badgeKey:"", badgeName:`Wise Owl`, phase:"outline", steps:OWL_OUTLINE_STEPS };
|
||||
export const OWL_DETAIL_LESSON: Lesson = { level:"early-beginner", subjectKey:"owl", subjectName:`Owl`, subjectEmoji:`🦉`, emoji:`🦉`, order:34, sublevel:342, slug:"owl-detail", title:`Owl \u00b7 Details`, subject:`owl details`, intro:`Now add the details that bring your owl to life.`, badgeKey:"early-beginner-34-owl", badgeName:`Wise Owl`, phase:"detail", baseSvg:OWL_OUTLINE_SVG, steps:OWL_DETAIL_STEPS };
|
||||
export const OWL_EXTRA_LESSON: Lesson = { level:"early-beginner", subjectKey:"owl", subjectName:`Owl`, subjectEmoji:`🦉`, emoji:`🦉`, order:34, sublevel:343, slug:"owl-extra", title:`Owl \u00b7 Color it!`, subject:`color the owl`, intro:`Bring your owl to life! Color it in.`, badgeKey:"", badgeName:`Wise Owl`, phase:"extra", baseSvg:OWL_DETAIL_SVG, steps:[] };
|
||||
|
||||
const CAT_OUTLINE_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`The head`, instruction:`Draw a round head.`, lines:[`<circle cx="200" cy="140" r="50" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:2, title:`The ears`, instruction:`Add two pointy ears.`, lines:[`<path d="M165 108 L150 72 L192 98 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M235 108 L250 72 L208 98 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:3, title:`The body`, instruction:`Add a sitting body.`, lines:[`<ellipse cx="200" cy="222" rx="46" ry="42" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:4, title:`The tail`, instruction:`Add a curvy tail.`, lines:[`<path d="M244 236 C 288 230 292 188 268 178" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const CAT_DETAIL_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`The face`, instruction:`Add eyes, nose and mouth.`, lines:[`<circle cx="184" cy="138" r="5" fill="#2b2440"/>`,`<circle cx="216" cy="138" r="5" fill="#2b2440"/>`,`<path d="M194 152 L206 152 L200 158 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M200 158 L200 164" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:2, title:`Whiskers`, instruction:`Add whiskers.`, lines:[`<path d="M170 150 l-28 -6" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M170 158 l-28 6" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M230 150 l28 -6" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M230 158 l28 6" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const CAT_OUTLINE_SVG = CAT_OUTLINE_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
const CAT_DETAIL_SVG = CAT_OUTLINE_SVG + "\n" + CAT_DETAIL_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
export const CAT_OUTLINE_LESSON: Lesson = { level:"early-beginner", subjectKey:"cat", subjectName:`Cat`, subjectEmoji:`🐱`, emoji:`🐱`, order:35, sublevel:351, slug:"cat-outline", title:`Cat \u00b7 Outline`, subject:`cat outline`, intro:`Let\u2019s draw Cat\u2019s outline, one line at a time. Watch each line, then trace it!`, badgeKey:"", badgeName:`Cool Cat`, phase:"outline", steps:CAT_OUTLINE_STEPS };
|
||||
export const CAT_DETAIL_LESSON: Lesson = { level:"early-beginner", subjectKey:"cat", subjectName:`Cat`, subjectEmoji:`🐱`, emoji:`🐱`, order:35, sublevel:352, slug:"cat-detail", title:`Cat \u00b7 Details`, subject:`cat details`, intro:`Now add the details that bring your cat to life.`, badgeKey:"early-beginner-35-cat", badgeName:`Cool Cat`, phase:"detail", baseSvg:CAT_OUTLINE_SVG, steps:CAT_DETAIL_STEPS };
|
||||
export const CAT_EXTRA_LESSON: Lesson = { level:"early-beginner", subjectKey:"cat", subjectName:`Cat`, subjectEmoji:`🐱`, emoji:`🐱`, order:35, sublevel:353, slug:"cat-extra", title:`Cat \u00b7 Color it!`, subject:`color the cat`, intro:`Bring your cat to life! Color it in.`, badgeKey:"", badgeName:`Cool Cat`, phase:"extra", baseSvg:CAT_DETAIL_SVG, steps:[] };
|
||||
|
||||
const TURTLE_OUTLINE_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`The shell`, instruction:`Draw a dome shell.`, lines:[`<path d="M118 200 C 118 152 282 152 282 200 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:2, title:`Head & legs`, instruction:`Add a head, legs and tail.`, lines:[`<path d="M282 188 C 312 184 318 200 308 210 C 300 216 286 212 282 204" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M150 200 l-6 18 l14 0" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M250 200 l-6 18 l14 0" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M118 196 q-14 4 -10 16" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const TURTLE_DETAIL_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`Shell pattern`, instruction:`Add a shell pattern.`, lines:[`<path d="M200 156 L160 176 L160 198" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M200 156 L240 176 L240 198" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M160 176 L240 176" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:2, title:`The eye`, instruction:`Add a friendly eye.`, lines:[`<circle cx="300" cy="194" r="4" fill="#2b2440"/>`] },
|
||||
];
|
||||
|
||||
const TURTLE_OUTLINE_SVG = TURTLE_OUTLINE_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
const TURTLE_DETAIL_SVG = TURTLE_OUTLINE_SVG + "\n" + TURTLE_DETAIL_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
export const TURTLE_OUTLINE_LESSON: Lesson = { level:"early-beginner", subjectKey:"turtle", subjectName:`Turtle`, subjectEmoji:`🐢`, emoji:`🐢`, order:36, sublevel:361, slug:"turtle-outline", title:`Turtle \u00b7 Outline`, subject:`turtle outline`, intro:`Let\u2019s draw Turtle\u2019s outline, one line at a time. Watch each line, then trace it!`, badgeKey:"", badgeName:`Turtle Buddy`, phase:"outline", steps:TURTLE_OUTLINE_STEPS };
|
||||
export const TURTLE_DETAIL_LESSON: Lesson = { level:"early-beginner", subjectKey:"turtle", subjectName:`Turtle`, subjectEmoji:`🐢`, emoji:`🐢`, order:36, sublevel:362, slug:"turtle-detail", title:`Turtle \u00b7 Details`, subject:`turtle details`, intro:`Now add the details that bring your turtle to life.`, badgeKey:"early-beginner-36-turtle", badgeName:`Turtle Buddy`, phase:"detail", baseSvg:TURTLE_OUTLINE_SVG, steps:TURTLE_DETAIL_STEPS };
|
||||
export const TURTLE_EXTRA_LESSON: Lesson = { level:"early-beginner", subjectKey:"turtle", subjectName:`Turtle`, subjectEmoji:`🐢`, emoji:`🐢`, order:36, sublevel:363, slug:"turtle-extra", title:`Turtle \u00b7 Color it!`, subject:`color the turtle`, intro:`Bring your turtle to life! Color it in.`, badgeKey:"", badgeName:`Turtle Buddy`, phase:"extra", baseSvg:TURTLE_DETAIL_SVG, steps:[] };
|
||||
|
||||
const BIRD_OUTLINE_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`The body`, instruction:`Draw an egg-shaped body.`, lines:[`<ellipse cx="190" cy="162" rx="52" ry="40" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:2, title:`The head`, instruction:`Add a round head.`, lines:[`<circle cx="245" cy="136" r="26" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:3, title:`The tail`, instruction:`Add a pointy tail.`, lines:[`<path d="M138 162 L100 146 L110 164 L100 180 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:4, title:`The wing`, instruction:`Add a wing.`, lines:[`<path d="M180 152 q30 10 44 34" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const BIRD_DETAIL_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`Beak & eye`, instruction:`Add a beak and an eye.`, lines:[`<path d="M271 133 L294 128 L272 144 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<circle cx="250" cy="131" r="4" fill="#2b2440"/>`] },
|
||||
{ n:2, title:`The legs`, instruction:`Add two legs.`, lines:[`<path d="M190 200 l-6 18" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M205 200 l4 18" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const BIRD_OUTLINE_SVG = BIRD_OUTLINE_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
const BIRD_DETAIL_SVG = BIRD_OUTLINE_SVG + "\n" + BIRD_DETAIL_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
export const BIRD_OUTLINE_LESSON: Lesson = { level:"early-beginner", subjectKey:"bird", subjectName:`Bird`, subjectEmoji:`🐦`, emoji:`🐦`, order:37, sublevel:371, slug:"bird-outline", title:`Bird \u00b7 Outline`, subject:`bird outline`, intro:`Let\u2019s draw Bird\u2019s outline, one line at a time. Watch each line, then trace it!`, badgeKey:"", badgeName:`Little Birdie`, phase:"outline", steps:BIRD_OUTLINE_STEPS };
|
||||
export const BIRD_DETAIL_LESSON: Lesson = { level:"early-beginner", subjectKey:"bird", subjectName:`Bird`, subjectEmoji:`🐦`, emoji:`🐦`, order:37, sublevel:372, slug:"bird-detail", title:`Bird \u00b7 Details`, subject:`bird details`, intro:`Now add the details that bring your bird to life.`, badgeKey:"early-beginner-37-bird", badgeName:`Little Birdie`, phase:"detail", baseSvg:BIRD_OUTLINE_SVG, steps:BIRD_DETAIL_STEPS };
|
||||
export const BIRD_EXTRA_LESSON: Lesson = { level:"early-beginner", subjectKey:"bird", subjectName:`Bird`, subjectEmoji:`🐦`, emoji:`🐦`, order:37, sublevel:373, slug:"bird-extra", title:`Bird \u00b7 Color it!`, subject:`color the bird`, intro:`Bring your bird to life! Color it in.`, badgeKey:"", badgeName:`Little Birdie`, phase:"extra", baseSvg:BIRD_DETAIL_SVG, steps:[] };
|
||||
|
||||
const BUTTERFLY_OUTLINE_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`The body`, instruction:`Draw a thin body.`, lines:[`<ellipse cx="200" cy="162" rx="8" ry="44" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:2, title:`Top wings`, instruction:`Add two big top wings.`, lines:[`<path d="M196 140 C 150 100 120 120 130 150 C 138 174 182 168 196 154 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M204 140 C 250 100 280 120 270 150 C 262 174 218 168 204 154 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:3, title:`Bottom wings`, instruction:`Add two bottom wings.`, lines:[`<path d="M196 168 C 160 182 150 216 180 222 C 198 226 200 196 198 180 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M204 168 C 240 182 250 216 220 222 C 202 226 200 196 202 180 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const BUTTERFLY_DETAIL_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`Antennae`, instruction:`Add two antennae.`, lines:[`<path d="M196 122 q-10 -16 -22 -16" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M204 122 q10 -16 22 -16" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:2, title:`Wing dots`, instruction:`Add pretty dots.`, lines:[`<circle cx="160" cy="138" r="7" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<circle cx="240" cy="138" r="7" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<circle cx="176" cy="200" r="6" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<circle cx="224" cy="200" r="6" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const BUTTERFLY_OUTLINE_SVG = BUTTERFLY_OUTLINE_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
const BUTTERFLY_DETAIL_SVG = BUTTERFLY_OUTLINE_SVG + "\n" + BUTTERFLY_DETAIL_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
export const BUTTERFLY_OUTLINE_LESSON: Lesson = { level:"early-beginner", subjectKey:"butterfly", subjectName:`Butterfly`, subjectEmoji:`🦋`, emoji:`🦋`, order:38, sublevel:381, slug:"butterfly-outline", title:`Butterfly \u00b7 Outline`, subject:`butterfly outline`, intro:`Let\u2019s draw Butterfly\u2019s outline, one line at a time. Watch each line, then trace it!`, badgeKey:"", badgeName:`Flutter By`, phase:"outline", steps:BUTTERFLY_OUTLINE_STEPS };
|
||||
export const BUTTERFLY_DETAIL_LESSON: Lesson = { level:"early-beginner", subjectKey:"butterfly", subjectName:`Butterfly`, subjectEmoji:`🦋`, emoji:`🦋`, order:38, sublevel:382, slug:"butterfly-detail", title:`Butterfly \u00b7 Details`, subject:`butterfly details`, intro:`Now add the details that bring your butterfly to life.`, badgeKey:"early-beginner-38-butterfly", badgeName:`Flutter By`, phase:"detail", baseSvg:BUTTERFLY_OUTLINE_SVG, steps:BUTTERFLY_DETAIL_STEPS };
|
||||
export const BUTTERFLY_EXTRA_LESSON: Lesson = { level:"early-beginner", subjectKey:"butterfly", subjectName:`Butterfly`, subjectEmoji:`🦋`, emoji:`🦋`, order:38, sublevel:383, slug:"butterfly-extra", title:`Butterfly \u00b7 Color it!`, subject:`color the butterfly`, intro:`Bring your butterfly to life! Color it in.`, badgeKey:"", badgeName:`Flutter By`, phase:"extra", baseSvg:BUTTERFLY_DETAIL_SVG, steps:[] };
|
||||
|
||||
export const ANIMAL_LESSONS: Lesson[] = [
|
||||
LION_OUTLINE_LESSON,
|
||||
LION_DETAIL_LESSON,
|
||||
LION_EXTRA_LESSON,
|
||||
ELEPHANT_OUTLINE_LESSON,
|
||||
ELEPHANT_DETAIL_LESSON,
|
||||
ELEPHANT_EXTRA_LESSON,
|
||||
GIRAFFE_OUTLINE_LESSON,
|
||||
GIRAFFE_DETAIL_LESSON,
|
||||
GIRAFFE_EXTRA_LESSON,
|
||||
ZEBRA_OUTLINE_LESSON,
|
||||
ZEBRA_DETAIL_LESSON,
|
||||
ZEBRA_EXTRA_LESSON,
|
||||
RHINO_OUTLINE_LESSON,
|
||||
RHINO_DETAIL_LESSON,
|
||||
RHINO_EXTRA_LESSON,
|
||||
LEOPARD_OUTLINE_LESSON,
|
||||
LEOPARD_DETAIL_LESSON,
|
||||
LEOPARD_EXTRA_LESSON,
|
||||
TIGER_OUTLINE_LESSON,
|
||||
TIGER_DETAIL_LESSON,
|
||||
TIGER_EXTRA_LESSON,
|
||||
MONKEY_OUTLINE_LESSON,
|
||||
MONKEY_DETAIL_LESSON,
|
||||
MONKEY_EXTRA_LESSON,
|
||||
CROCODILE_OUTLINE_LESSON,
|
||||
CROCODILE_DETAIL_LESSON,
|
||||
CROCODILE_EXTRA_LESSON,
|
||||
FOX_OUTLINE_LESSON,
|
||||
FOX_DETAIL_LESSON,
|
||||
FOX_EXTRA_LESSON,
|
||||
BEAR_OUTLINE_LESSON,
|
||||
BEAR_DETAIL_LESSON,
|
||||
BEAR_EXTRA_LESSON,
|
||||
DEER_OUTLINE_LESSON,
|
||||
DEER_DETAIL_LESSON,
|
||||
DEER_EXTRA_LESSON,
|
||||
RABBIT_OUTLINE_LESSON,
|
||||
RABBIT_DETAIL_LESSON,
|
||||
RABBIT_EXTRA_LESSON,
|
||||
HEDGEHOG_OUTLINE_LESSON,
|
||||
HEDGEHOG_DETAIL_LESSON,
|
||||
HEDGEHOG_EXTRA_LESSON,
|
||||
OWL_OUTLINE_LESSON,
|
||||
OWL_DETAIL_LESSON,
|
||||
OWL_EXTRA_LESSON,
|
||||
CAT_OUTLINE_LESSON,
|
||||
CAT_DETAIL_LESSON,
|
||||
CAT_EXTRA_LESSON,
|
||||
TURTLE_OUTLINE_LESSON,
|
||||
TURTLE_DETAIL_LESSON,
|
||||
TURTLE_EXTRA_LESSON,
|
||||
BIRD_OUTLINE_LESSON,
|
||||
BIRD_DETAIL_LESSON,
|
||||
BIRD_EXTRA_LESSON,
|
||||
BUTTERFLY_OUTLINE_LESSON,
|
||||
BUTTERFLY_DETAIL_LESSON,
|
||||
BUTTERFLY_EXTRA_LESSON
|
||||
];
|
||||
@@ -0,0 +1,234 @@
|
||||
/* AUTO-GENERATED by gen_eb2.py — Early Beginner geometric subjects (Simple Shapes, Fun Things, Clothes, Faces). */
|
||||
import type { Lesson, LessonStep } from "./curriculum";
|
||||
|
||||
const SQUARE_OUTLINE_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`The square`, instruction:`Draw four straight sides to make a square.`, tip:`Keep the corners square!`, lines:[`<path d="M132 92 L268 92 L268 228 L132 228 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const SQUARE_DETAIL_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`Happy face`, instruction:`Add two eyes and a big smile.`, tip:`Now it's a friendly square!`, lines:[`<circle cx="176" cy="150" r="8" fill="#2b2440"/>`,`<circle cx="224" cy="150" r="8" fill="#2b2440"/>`,`<path d="M172 182 q28 26 56 0" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const SQUARE_OUTLINE_SVG = SQUARE_OUTLINE_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
const SQUARE_DETAIL_SVG = SQUARE_OUTLINE_SVG + "\n" + SQUARE_DETAIL_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
export const SQUARE_OUTLINE_LESSON: Lesson = { level:"early-beginner", subjectKey:"square", subjectName:`Square`, subjectEmoji:`🟦`, emoji:`🟦`, order:7, sublevel:71, slug:"square-outline", title:`Square \u00b7 Outline`, subject:`square outline`, intro:`Let\u2019s draw Square\u2019s outline, one line at a time. Watch each line, then trace it!`, badgeKey:"", badgeName:`Square Star`, phase:"outline", steps:SQUARE_OUTLINE_STEPS };
|
||||
export const SQUARE_DETAIL_LESSON: Lesson = { level:"early-beginner", subjectKey:"square", subjectName:`Square`, subjectEmoji:`🟦`, emoji:`🟦`, order:7, sublevel:72, slug:"square-detail", title:`Square \u00b7 Details`, subject:`square details`, intro:`Now add the details that make your square fun.`, badgeKey:"early-beginner-7-square", badgeName:`Square Star`, phase:"detail", baseSvg:SQUARE_OUTLINE_SVG, steps:SQUARE_DETAIL_STEPS };
|
||||
export const SQUARE_EXTRA_LESSON: Lesson = { level:"early-beginner", subjectKey:"square", subjectName:`Square`, subjectEmoji:`🟦`, emoji:`🟦`, order:7, sublevel:73, slug:"square-extra", title:`Square \u00b7 Color it!`, subject:`color the square`, intro:`Bring your square to life! Color it in.`, badgeKey:"", badgeName:`Square Star`, phase:"extra", baseSvg:SQUARE_DETAIL_SVG, steps:[] };
|
||||
|
||||
const TRIANGLE_OUTLINE_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`The triangle`, instruction:`Draw three straight sides that meet at a point.`, lines:[`<path d="M200 80 L290 230 L110 230 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const TRIANGLE_DETAIL_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`Happy face`, instruction:`Add a cute face near the bottom.`, lines:[`<circle cx="178" cy="182" r="7" fill="#2b2440"/>`,`<circle cx="222" cy="182" r="7" fill="#2b2440"/>`,`<path d="M180 200 q20 18 40 0" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const TRIANGLE_OUTLINE_SVG = TRIANGLE_OUTLINE_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
const TRIANGLE_DETAIL_SVG = TRIANGLE_OUTLINE_SVG + "\n" + TRIANGLE_DETAIL_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
export const TRIANGLE_OUTLINE_LESSON: Lesson = { level:"early-beginner", subjectKey:"triangle", subjectName:`Triangle`, subjectEmoji:`🔺`, emoji:`🔺`, order:8, sublevel:81, slug:"triangle-outline", title:`Triangle \u00b7 Outline`, subject:`triangle outline`, intro:`Let\u2019s draw Triangle\u2019s outline, one line at a time. Watch each line, then trace it!`, badgeKey:"", badgeName:`Triangle Ace`, phase:"outline", steps:TRIANGLE_OUTLINE_STEPS };
|
||||
export const TRIANGLE_DETAIL_LESSON: Lesson = { level:"early-beginner", subjectKey:"triangle", subjectName:`Triangle`, subjectEmoji:`🔺`, emoji:`🔺`, order:8, sublevel:82, slug:"triangle-detail", title:`Triangle \u00b7 Details`, subject:`triangle details`, intro:`Now add the details that make your triangle fun.`, badgeKey:"early-beginner-8-triangle", badgeName:`Triangle Ace`, phase:"detail", baseSvg:TRIANGLE_OUTLINE_SVG, steps:TRIANGLE_DETAIL_STEPS };
|
||||
export const TRIANGLE_EXTRA_LESSON: Lesson = { level:"early-beginner", subjectKey:"triangle", subjectName:`Triangle`, subjectEmoji:`🔺`, emoji:`🔺`, order:8, sublevel:83, slug:"triangle-extra", title:`Triangle \u00b7 Color it!`, subject:`color the triangle`, intro:`Bring your triangle to life! Color it in.`, badgeKey:"", badgeName:`Triangle Ace`, phase:"extra", baseSvg:TRIANGLE_DETAIL_SVG, steps:[] };
|
||||
|
||||
const HEART_OUTLINE_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`The heart`, instruction:`Draw two bumps on top that swoop to a point.`, tip:`Slow on the bumps!`, lines:[`<path d="M200 224 C 112 168 126 92 174 92 C 196 92 200 112 200 124 C 200 112 204 92 226 92 C 274 92 288 168 200 224 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const HEART_DETAIL_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`Shine & smile`, instruction:`Add a shine line and a little smile.`, lines:[`<path d="M162 124 q-8 18 4 34" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M184 168 q16 12 32 0" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const HEART_OUTLINE_SVG = HEART_OUTLINE_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
const HEART_DETAIL_SVG = HEART_OUTLINE_SVG + "\n" + HEART_DETAIL_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
export const HEART_OUTLINE_LESSON: Lesson = { level:"early-beginner", subjectKey:"heart", subjectName:`Heart`, subjectEmoji:`❤️`, emoji:`❤️`, order:9, sublevel:91, slug:"heart-outline", title:`Heart \u00b7 Outline`, subject:`heart outline`, intro:`Let\u2019s draw Heart\u2019s outline, one line at a time. Watch each line, then trace it!`, badgeKey:"", badgeName:`Heart Hero`, phase:"outline", steps:HEART_OUTLINE_STEPS };
|
||||
export const HEART_DETAIL_LESSON: Lesson = { level:"early-beginner", subjectKey:"heart", subjectName:`Heart`, subjectEmoji:`❤️`, emoji:`❤️`, order:9, sublevel:92, slug:"heart-detail", title:`Heart \u00b7 Details`, subject:`heart details`, intro:`Now add the details that make your heart fun.`, badgeKey:"early-beginner-9-heart", badgeName:`Heart Hero`, phase:"detail", baseSvg:HEART_OUTLINE_SVG, steps:HEART_DETAIL_STEPS };
|
||||
export const HEART_EXTRA_LESSON: Lesson = { level:"early-beginner", subjectKey:"heart", subjectName:`Heart`, subjectEmoji:`❤️`, emoji:`❤️`, order:9, sublevel:93, slug:"heart-extra", title:`Heart \u00b7 Color it!`, subject:`color the heart`, intro:`Bring your heart to life! Color it in.`, badgeKey:"", badgeName:`Heart Hero`, phase:"extra", baseSvg:HEART_DETAIL_SVG, steps:[] };
|
||||
|
||||
const BOW_TIE_OUTLINE_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`The two wings`, instruction:`Draw a triangle on each side meeting in the middle.`, lines:[`<path d="M194 150 L138 118 L138 182 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M206 150 L262 118 L262 182 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:2, title:`The knot`, instruction:`Add a little square knot in the middle.`, lines:[`<path d="M192 136 L208 136 L208 164 L192 164 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const BOW_TIE_DETAIL_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`Polka dots`, instruction:`Add fold lines and some dots.`, lines:[`<path d="M150 130 L150 170" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M250 130 L250 170" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<circle cx="166" cy="144" r="5" fill="#2b2440"/>`,`<circle cx="170" cy="162" r="4" fill="#2b2440"/>`,`<circle cx="234" cy="144" r="5" fill="#2b2440"/>`,`<circle cx="230" cy="162" r="4" fill="#2b2440"/>`] },
|
||||
];
|
||||
|
||||
const BOW_TIE_OUTLINE_SVG = BOW_TIE_OUTLINE_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
const BOW_TIE_DETAIL_SVG = BOW_TIE_OUTLINE_SVG + "\n" + BOW_TIE_DETAIL_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
export const BOW_TIE_OUTLINE_LESSON: Lesson = { level:"early-beginner", subjectKey:"bow-tie", subjectName:`Bow Tie`, subjectEmoji:`🎀`, emoji:`🎀`, order:10, sublevel:101, slug:"bow-tie-outline", title:`Bow Tie \u00b7 Outline`, subject:`bow tie outline`, intro:`Let\u2019s draw Bow Tie\u2019s outline, one line at a time. Watch each line, then trace it!`, badgeKey:"", badgeName:`Dapper Bow`, phase:"outline", steps:BOW_TIE_OUTLINE_STEPS };
|
||||
export const BOW_TIE_DETAIL_LESSON: Lesson = { level:"early-beginner", subjectKey:"bow-tie", subjectName:`Bow Tie`, subjectEmoji:`🎀`, emoji:`🎀`, order:10, sublevel:102, slug:"bow-tie-detail", title:`Bow Tie \u00b7 Details`, subject:`bow tie details`, intro:`Now add the details that make your bow tie fun.`, badgeKey:"early-beginner-10-bow-tie", badgeName:`Dapper Bow`, phase:"detail", baseSvg:BOW_TIE_OUTLINE_SVG, steps:BOW_TIE_DETAIL_STEPS };
|
||||
export const BOW_TIE_EXTRA_LESSON: Lesson = { level:"early-beginner", subjectKey:"bow-tie", subjectName:`Bow Tie`, subjectEmoji:`🎀`, emoji:`🎀`, order:10, sublevel:103, slug:"bow-tie-extra", title:`Bow Tie \u00b7 Color it!`, subject:`color the bow tie`, intro:`Bring your bow tie to life! Color it in.`, badgeKey:"", badgeName:`Dapper Bow`, phase:"extra", baseSvg:BOW_TIE_DETAIL_SVG, steps:[] };
|
||||
|
||||
const FOOTBALL_OUTLINE_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`The ball`, instruction:`Draw a pointed oval.`, tip:`Pointy on each end.`, lines:[`<path d="M118 150 C 150 108 250 108 282 150 C 250 192 150 192 118 150 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const FOOTBALL_DETAIL_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`Laces`, instruction:`Add the laces and stitches.`, lines:[`<path d="M178 150 L222 150" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M186 142 L186 158" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M200 140 L200 160" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M214 142 L214 158" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M132 150 L150 150" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M250 150 L268 150" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const FOOTBALL_OUTLINE_SVG = FOOTBALL_OUTLINE_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
const FOOTBALL_DETAIL_SVG = FOOTBALL_OUTLINE_SVG + "\n" + FOOTBALL_DETAIL_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
export const FOOTBALL_OUTLINE_LESSON: Lesson = { level:"early-beginner", subjectKey:"football", subjectName:`Football`, subjectEmoji:`🏈`, emoji:`🏈`, order:11, sublevel:111, slug:"football-outline", title:`Football \u00b7 Outline`, subject:`football outline`, intro:`Let\u2019s draw Football\u2019s outline, one line at a time. Watch each line, then trace it!`, badgeKey:"", badgeName:`Touchdown`, phase:"outline", steps:FOOTBALL_OUTLINE_STEPS };
|
||||
export const FOOTBALL_DETAIL_LESSON: Lesson = { level:"early-beginner", subjectKey:"football", subjectName:`Football`, subjectEmoji:`🏈`, emoji:`🏈`, order:11, sublevel:112, slug:"football-detail", title:`Football \u00b7 Details`, subject:`football details`, intro:`Now add the details that make your football fun.`, badgeKey:"early-beginner-11-football", badgeName:`Touchdown`, phase:"detail", baseSvg:FOOTBALL_OUTLINE_SVG, steps:FOOTBALL_DETAIL_STEPS };
|
||||
export const FOOTBALL_EXTRA_LESSON: Lesson = { level:"early-beginner", subjectKey:"football", subjectName:`Football`, subjectEmoji:`🏈`, emoji:`🏈`, order:11, sublevel:113, slug:"football-extra", title:`Football \u00b7 Color it!`, subject:`color the football`, intro:`Bring your football to life! Color it in.`, badgeKey:"", badgeName:`Touchdown`, phase:"extra", baseSvg:FOOTBALL_DETAIL_SVG, steps:[] };
|
||||
|
||||
const BASKETBALL_OUTLINE_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`The ball`, instruction:`Draw one big round circle.`, lines:[`<circle cx="200" cy="150" r="82" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const BASKETBALL_DETAIL_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`The seams`, instruction:`Add the lines across the ball.`, lines:[`<path d="M200 70 L200 230" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M118 150 L282 150" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M142 94 Q176 150 142 206" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M258 94 Q224 150 258 206" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const BASKETBALL_OUTLINE_SVG = BASKETBALL_OUTLINE_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
const BASKETBALL_DETAIL_SVG = BASKETBALL_OUTLINE_SVG + "\n" + BASKETBALL_DETAIL_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
export const BASKETBALL_OUTLINE_LESSON: Lesson = { level:"early-beginner", subjectKey:"basketball", subjectName:`Basketball`, subjectEmoji:`🏀`, emoji:`🏀`, order:12, sublevel:121, slug:"basketball-outline", title:`Basketball \u00b7 Outline`, subject:`basketball outline`, intro:`Let\u2019s draw Basketball\u2019s outline, one line at a time. Watch each line, then trace it!`, badgeKey:"", badgeName:`Hoop Star`, phase:"outline", steps:BASKETBALL_OUTLINE_STEPS };
|
||||
export const BASKETBALL_DETAIL_LESSON: Lesson = { level:"early-beginner", subjectKey:"basketball", subjectName:`Basketball`, subjectEmoji:`🏀`, emoji:`🏀`, order:12, sublevel:122, slug:"basketball-detail", title:`Basketball \u00b7 Details`, subject:`basketball details`, intro:`Now add the details that make your basketball fun.`, badgeKey:"early-beginner-12-basketball", badgeName:`Hoop Star`, phase:"detail", baseSvg:BASKETBALL_OUTLINE_SVG, steps:BASKETBALL_DETAIL_STEPS };
|
||||
export const BASKETBALL_EXTRA_LESSON: Lesson = { level:"early-beginner", subjectKey:"basketball", subjectName:`Basketball`, subjectEmoji:`🏀`, emoji:`🏀`, order:12, sublevel:123, slug:"basketball-extra", title:`Basketball \u00b7 Color it!`, subject:`color the basketball`, intro:`Bring your basketball to life! Color it in.`, badgeKey:"", badgeName:`Hoop Star`, phase:"extra", baseSvg:BASKETBALL_DETAIL_SVG, steps:[] };
|
||||
|
||||
const CUP_OUTLINE_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`The cup`, instruction:`Draw a cup that is wider at the top.`, tip:`Flat top — no oval yet!`, lines:[`<path d="M152 100 L248 100 L236 222 L164 222 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:2, title:`The lid`, instruction:`Add a lid on top.`, lines:[`<path d="M146 100 L254 100 L250 82 L150 82 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const CUP_DETAIL_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`Straw & stripe`, instruction:`Add a straw and a wavy stripe.`, lines:[`<path d="M206 82 L226 50" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M162 150 q20 -10 38 0 q18 10 38 0" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const CUP_OUTLINE_SVG = CUP_OUTLINE_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
const CUP_DETAIL_SVG = CUP_OUTLINE_SVG + "\n" + CUP_DETAIL_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
export const CUP_OUTLINE_LESSON: Lesson = { level:"early-beginner", subjectKey:"cup", subjectName:`Cup`, subjectEmoji:`🥤`, emoji:`🥤`, order:13, sublevel:131, slug:"cup-outline", title:`Cup \u00b7 Outline`, subject:`cup outline`, intro:`Let\u2019s draw Cup\u2019s outline, one line at a time. Watch each line, then trace it!`, badgeKey:"", badgeName:`Cup Champ`, phase:"outline", steps:CUP_OUTLINE_STEPS };
|
||||
export const CUP_DETAIL_LESSON: Lesson = { level:"early-beginner", subjectKey:"cup", subjectName:`Cup`, subjectEmoji:`🥤`, emoji:`🥤`, order:13, sublevel:132, slug:"cup-detail", title:`Cup \u00b7 Details`, subject:`cup details`, intro:`Now add the details that make your cup fun.`, badgeKey:"early-beginner-13-cup", badgeName:`Cup Champ`, phase:"detail", baseSvg:CUP_OUTLINE_SVG, steps:CUP_DETAIL_STEPS };
|
||||
export const CUP_EXTRA_LESSON: Lesson = { level:"early-beginner", subjectKey:"cup", subjectName:`Cup`, subjectEmoji:`🥤`, emoji:`🥤`, order:13, sublevel:133, slug:"cup-extra", title:`Cup \u00b7 Color it!`, subject:`color the cup`, intro:`Bring your cup to life! Color it in.`, badgeKey:"", badgeName:`Cup Champ`, phase:"extra", baseSvg:CUP_DETAIL_SVG, steps:[] };
|
||||
|
||||
const TROPHY_OUTLINE_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`The cup`, instruction:`Draw the trophy bowl.`, lines:[`<path d="M162 92 L238 92 L230 150 C 230 176 170 176 170 150 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:2, title:`Stem & base`, instruction:`Add the stem and a base.`, lines:[`<path d="M194 172 L206 172 L206 200 L194 200 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M168 200 L232 200 L238 222 L162 222 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const TROPHY_DETAIL_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`Handles & star`, instruction:`Add two handles and a number 1.`, tip:`You're a winner!`, lines:[`<path d="M162 102 C 132 102 132 146 164 146" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M238 102 C 268 102 268 146 236 146" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M200 110 L200 140" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M200 110 L192 118" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const TROPHY_OUTLINE_SVG = TROPHY_OUTLINE_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
const TROPHY_DETAIL_SVG = TROPHY_OUTLINE_SVG + "\n" + TROPHY_DETAIL_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
export const TROPHY_OUTLINE_LESSON: Lesson = { level:"early-beginner", subjectKey:"trophy", subjectName:`Trophy`, subjectEmoji:`🏆`, emoji:`🏆`, order:14, sublevel:141, slug:"trophy-outline", title:`Trophy \u00b7 Outline`, subject:`trophy outline`, intro:`Let\u2019s draw Trophy\u2019s outline, one line at a time. Watch each line, then trace it!`, badgeKey:"", badgeName:`Trophy Winner`, phase:"outline", steps:TROPHY_OUTLINE_STEPS };
|
||||
export const TROPHY_DETAIL_LESSON: Lesson = { level:"early-beginner", subjectKey:"trophy", subjectName:`Trophy`, subjectEmoji:`🏆`, emoji:`🏆`, order:14, sublevel:142, slug:"trophy-detail", title:`Trophy \u00b7 Details`, subject:`trophy details`, intro:`Now add the details that make your trophy fun.`, badgeKey:"early-beginner-14-trophy", badgeName:`Trophy Winner`, phase:"detail", baseSvg:TROPHY_OUTLINE_SVG, steps:TROPHY_DETAIL_STEPS };
|
||||
export const TROPHY_EXTRA_LESSON: Lesson = { level:"early-beginner", subjectKey:"trophy", subjectName:`Trophy`, subjectEmoji:`🏆`, emoji:`🏆`, order:14, sublevel:143, slug:"trophy-extra", title:`Trophy \u00b7 Color it!`, subject:`color the trophy`, intro:`Bring your trophy to life! Color it in.`, badgeKey:"", badgeName:`Trophy Winner`, phase:"extra", baseSvg:TROPHY_DETAIL_SVG, steps:[] };
|
||||
|
||||
const SHIRT_OUTLINE_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`The body`, instruction:`Draw the shirt body with sleeves.`, lines:[`<path d="M160 110 L240 110 L282 146 L252 176 L240 160 L240 236 L160 236 L160 160 L148 176 L118 146 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:2, title:`The neck`, instruction:`Add a round neckline.`, lines:[`<path d="M180 110 q20 22 40 0" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const SHIRT_DETAIL_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`Collar & pocket`, instruction:`Add a collar and a little pocket.`, lines:[`<path d="M184 113 L200 128 L216 113" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M176 168 L196 168 L196 188 L176 188 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const SHIRT_OUTLINE_SVG = SHIRT_OUTLINE_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
const SHIRT_DETAIL_SVG = SHIRT_OUTLINE_SVG + "\n" + SHIRT_DETAIL_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
export const SHIRT_OUTLINE_LESSON: Lesson = { level:"early-beginner", subjectKey:"shirt", subjectName:`Shirt`, subjectEmoji:`👕`, emoji:`👕`, order:15, sublevel:151, slug:"shirt-outline", title:`Shirt \u00b7 Outline`, subject:`shirt outline`, intro:`Let\u2019s draw Shirt\u2019s outline, one line at a time. Watch each line, then trace it!`, badgeKey:"", badgeName:`Cool Shirt`, phase:"outline", steps:SHIRT_OUTLINE_STEPS };
|
||||
export const SHIRT_DETAIL_LESSON: Lesson = { level:"early-beginner", subjectKey:"shirt", subjectName:`Shirt`, subjectEmoji:`👕`, emoji:`👕`, order:15, sublevel:152, slug:"shirt-detail", title:`Shirt \u00b7 Details`, subject:`shirt details`, intro:`Now add the details that make your shirt fun.`, badgeKey:"early-beginner-15-shirt", badgeName:`Cool Shirt`, phase:"detail", baseSvg:SHIRT_OUTLINE_SVG, steps:SHIRT_DETAIL_STEPS };
|
||||
export const SHIRT_EXTRA_LESSON: Lesson = { level:"early-beginner", subjectKey:"shirt", subjectName:`Shirt`, subjectEmoji:`👕`, emoji:`👕`, order:15, sublevel:153, slug:"shirt-extra", title:`Shirt \u00b7 Color it!`, subject:`color the shirt`, intro:`Bring your shirt to life! Color it in.`, badgeKey:"", badgeName:`Cool Shirt`, phase:"extra", baseSvg:SHIRT_DETAIL_SVG, steps:[] };
|
||||
|
||||
const DRESS_OUTLINE_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`The top`, instruction:`Draw the dress top.`, lines:[`<path d="M168 112 L232 112 L248 152 L230 162 L170 162 L152 152 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:2, title:`The skirt`, instruction:`Add a flowy skirt.`, lines:[`<path d="M170 162 L130 236 L270 236 L230 162 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const DRESS_DETAIL_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`Straps & waist`, instruction:`Add straps and a waistline.`, lines:[`<path d="M182 112 L188 96" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M218 112 L212 96" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M170 162 L230 162" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const DRESS_OUTLINE_SVG = DRESS_OUTLINE_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
const DRESS_DETAIL_SVG = DRESS_OUTLINE_SVG + "\n" + DRESS_DETAIL_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
export const DRESS_OUTLINE_LESSON: Lesson = { level:"early-beginner", subjectKey:"dress", subjectName:`Dress`, subjectEmoji:`👗`, emoji:`👗`, order:16, sublevel:161, slug:"dress-outline", title:`Dress \u00b7 Outline`, subject:`dress outline`, intro:`Let\u2019s draw Dress\u2019s outline, one line at a time. Watch each line, then trace it!`, badgeKey:"", badgeName:`Pretty Dress`, phase:"outline", steps:DRESS_OUTLINE_STEPS };
|
||||
export const DRESS_DETAIL_LESSON: Lesson = { level:"early-beginner", subjectKey:"dress", subjectName:`Dress`, subjectEmoji:`👗`, emoji:`👗`, order:16, sublevel:162, slug:"dress-detail", title:`Dress \u00b7 Details`, subject:`dress details`, intro:`Now add the details that make your dress fun.`, badgeKey:"early-beginner-16-dress", badgeName:`Pretty Dress`, phase:"detail", baseSvg:DRESS_OUTLINE_SVG, steps:DRESS_DETAIL_STEPS };
|
||||
export const DRESS_EXTRA_LESSON: Lesson = { level:"early-beginner", subjectKey:"dress", subjectName:`Dress`, subjectEmoji:`👗`, emoji:`👗`, order:16, sublevel:163, slug:"dress-extra", title:`Dress \u00b7 Color it!`, subject:`color the dress`, intro:`Bring your dress to life! Color it in.`, badgeKey:"", badgeName:`Pretty Dress`, phase:"extra", baseSvg:DRESS_DETAIL_SVG, steps:[] };
|
||||
|
||||
const SOCKS_OUTLINE_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`The sock`, instruction:`Draw an L-shaped sock.`, lines:[`<path d="M168 80 L212 80 L212 178 L252 178 L252 222 L168 222 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const SOCKS_DETAIL_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`Cuff & stripes`, instruction:`Add a folded cuff and stripes.`, lines:[`<path d="M168 104 L212 104" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M172 116 L208 116" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M172 128 L208 128" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M232 178 L232 222" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const SOCKS_OUTLINE_SVG = SOCKS_OUTLINE_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
const SOCKS_DETAIL_SVG = SOCKS_OUTLINE_SVG + "\n" + SOCKS_DETAIL_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
export const SOCKS_OUTLINE_LESSON: Lesson = { level:"early-beginner", subjectKey:"socks", subjectName:`Socks`, subjectEmoji:`🧦`, emoji:`🧦`, order:17, sublevel:171, slug:"socks-outline", title:`Socks \u00b7 Outline`, subject:`socks outline`, intro:`Let\u2019s draw Socks\u2019s outline, one line at a time. Watch each line, then trace it!`, badgeKey:"", badgeName:`Sock Star`, phase:"outline", steps:SOCKS_OUTLINE_STEPS };
|
||||
export const SOCKS_DETAIL_LESSON: Lesson = { level:"early-beginner", subjectKey:"socks", subjectName:`Socks`, subjectEmoji:`🧦`, emoji:`🧦`, order:17, sublevel:172, slug:"socks-detail", title:`Socks \u00b7 Details`, subject:`socks details`, intro:`Now add the details that make your socks fun.`, badgeKey:"early-beginner-17-socks", badgeName:`Sock Star`, phase:"detail", baseSvg:SOCKS_OUTLINE_SVG, steps:SOCKS_DETAIL_STEPS };
|
||||
export const SOCKS_EXTRA_LESSON: Lesson = { level:"early-beginner", subjectKey:"socks", subjectName:`Socks`, subjectEmoji:`🧦`, emoji:`🧦`, order:17, sublevel:173, slug:"socks-extra", title:`Socks \u00b7 Color it!`, subject:`color the socks`, intro:`Bring your socks to life! Color it in.`, badgeKey:"", badgeName:`Sock Star`, phase:"extra", baseSvg:SOCKS_DETAIL_SVG, steps:[] };
|
||||
|
||||
const EYES_OUTLINE_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`Two eyes`, instruction:`Draw two almond shapes, one eye apart.`, lines:[`<path d="M120 150 Q160 122 200 150 Q160 178 120 150 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M210 150 Q250 122 290 150 Q250 178 210 150 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const EYES_DETAIL_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`Look alive`, instruction:`Add round eyeballs and dots, plus eyebrows.`, tip:`Eyes sit halfway!`, lines:[`<circle cx="160" cy="150" r="14" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<circle cx="160" cy="150" r="7" fill="#2b2440"/>`,`<circle cx="250" cy="150" r="14" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<circle cx="250" cy="150" r="7" fill="#2b2440"/>`,`<path d="M120 118 Q160 106 200 118" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M210 118 Q250 106 290 118" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const EYES_OUTLINE_SVG = EYES_OUTLINE_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
const EYES_DETAIL_SVG = EYES_OUTLINE_SVG + "\n" + EYES_DETAIL_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
export const EYES_OUTLINE_LESSON: Lesson = { level:"early-beginner", subjectKey:"eyes", subjectName:`Eyes`, subjectEmoji:`👀`, emoji:`👀`, order:18, sublevel:181, slug:"eyes-outline", title:`Eyes \u00b7 Outline`, subject:`eyes outline`, intro:`Let\u2019s draw Eyes\u2019s outline, one line at a time. Watch each line, then trace it!`, badgeKey:"", badgeName:`Bright Eyes`, phase:"outline", steps:EYES_OUTLINE_STEPS };
|
||||
export const EYES_DETAIL_LESSON: Lesson = { level:"early-beginner", subjectKey:"eyes", subjectName:`Eyes`, subjectEmoji:`👀`, emoji:`👀`, order:18, sublevel:182, slug:"eyes-detail", title:`Eyes \u00b7 Details`, subject:`eyes details`, intro:`Now add the details that make your eyes fun.`, badgeKey:"early-beginner-18-eyes", badgeName:`Bright Eyes`, phase:"detail", baseSvg:EYES_OUTLINE_SVG, steps:EYES_DETAIL_STEPS };
|
||||
export const EYES_EXTRA_LESSON: Lesson = { level:"early-beginner", subjectKey:"eyes", subjectName:`Eyes`, subjectEmoji:`👀`, emoji:`👀`, order:18, sublevel:183, slug:"eyes-extra", title:`Eyes \u00b7 Color it!`, subject:`color the eyes`, intro:`Bring your eyes to life! Color it in.`, badgeKey:"", badgeName:`Bright Eyes`, phase:"extra", baseSvg:EYES_DETAIL_SVG, steps:[] };
|
||||
|
||||
const FACE_OUTLINE_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`The head`, instruction:`Draw a big round head.`, lines:[`<circle cx="200" cy="152" r="95" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:2, title:`The ears`, instruction:`Add an ear on each side.`, lines:[`<path d="M108 150 q-20 2 -16 24 q3 16 18 12" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M292 150 q20 2 16 24 q-3 16 -18 12" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const FACE_DETAIL_STEPS: LessonStep[] = [
|
||||
{ n:1, title:`Eyes & nose`, instruction:`Add two eyes and a little nose.`, tip:`Eyes in the middle!`, lines:[`<circle cx="170" cy="140" r="11" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<circle cx="170" cy="140" r="5" fill="#2b2440"/>`,`<circle cx="230" cy="140" r="11" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<circle cx="230" cy="140" r="5" fill="#2b2440"/>`,`<path d="M200 150 L200 174 q-8 4 -14 -1" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n:2, title:`Smile & hair`, instruction:`Add a big smile and some hair.`, lines:[`<path d="M166 190 q34 28 68 0" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,`<path d="M118 108 Q200 44 282 108" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const FACE_OUTLINE_SVG = FACE_OUTLINE_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
const FACE_DETAIL_SVG = FACE_OUTLINE_SVG + "\n" + FACE_DETAIL_STEPS.flatMap(x=>x.lines||[]).join("\n");
|
||||
export const FACE_OUTLINE_LESSON: Lesson = { level:"early-beginner", subjectKey:"face", subjectName:`Face`, subjectEmoji:`🙂`, emoji:`🙂`, order:19, sublevel:191, slug:"face-outline", title:`Face \u00b7 Outline`, subject:`face outline`, intro:`Let\u2019s draw Face\u2019s outline, one line at a time. Watch each line, then trace it!`, badgeKey:"", badgeName:`Friendly Face`, phase:"outline", steps:FACE_OUTLINE_STEPS };
|
||||
export const FACE_DETAIL_LESSON: Lesson = { level:"early-beginner", subjectKey:"face", subjectName:`Face`, subjectEmoji:`🙂`, emoji:`🙂`, order:19, sublevel:192, slug:"face-detail", title:`Face \u00b7 Details`, subject:`face details`, intro:`Now add the details that make your face fun.`, badgeKey:"early-beginner-19-face", badgeName:`Friendly Face`, phase:"detail", baseSvg:FACE_OUTLINE_SVG, steps:FACE_DETAIL_STEPS };
|
||||
export const FACE_EXTRA_LESSON: Lesson = { level:"early-beginner", subjectKey:"face", subjectName:`Face`, subjectEmoji:`🙂`, emoji:`🙂`, order:19, sublevel:193, slug:"face-extra", title:`Face \u00b7 Color it!`, subject:`color the face`, intro:`Bring your face to life! Color it in.`, badgeKey:"", badgeName:`Friendly Face`, phase:"extra", baseSvg:FACE_DETAIL_SVG, steps:[] };
|
||||
|
||||
export const EB2_LESSONS: Lesson[] = [
|
||||
SQUARE_OUTLINE_LESSON,
|
||||
SQUARE_DETAIL_LESSON,
|
||||
SQUARE_EXTRA_LESSON,
|
||||
TRIANGLE_OUTLINE_LESSON,
|
||||
TRIANGLE_DETAIL_LESSON,
|
||||
TRIANGLE_EXTRA_LESSON,
|
||||
HEART_OUTLINE_LESSON,
|
||||
HEART_DETAIL_LESSON,
|
||||
HEART_EXTRA_LESSON,
|
||||
BOW_TIE_OUTLINE_LESSON,
|
||||
BOW_TIE_DETAIL_LESSON,
|
||||
BOW_TIE_EXTRA_LESSON,
|
||||
FOOTBALL_OUTLINE_LESSON,
|
||||
FOOTBALL_DETAIL_LESSON,
|
||||
FOOTBALL_EXTRA_LESSON,
|
||||
BASKETBALL_OUTLINE_LESSON,
|
||||
BASKETBALL_DETAIL_LESSON,
|
||||
BASKETBALL_EXTRA_LESSON,
|
||||
CUP_OUTLINE_LESSON,
|
||||
CUP_DETAIL_LESSON,
|
||||
CUP_EXTRA_LESSON,
|
||||
TROPHY_OUTLINE_LESSON,
|
||||
TROPHY_DETAIL_LESSON,
|
||||
TROPHY_EXTRA_LESSON,
|
||||
SHIRT_OUTLINE_LESSON,
|
||||
SHIRT_DETAIL_LESSON,
|
||||
SHIRT_EXTRA_LESSON,
|
||||
DRESS_OUTLINE_LESSON,
|
||||
DRESS_DETAIL_LESSON,
|
||||
DRESS_EXTRA_LESSON,
|
||||
SOCKS_OUTLINE_LESSON,
|
||||
SOCKS_DETAIL_LESSON,
|
||||
SOCKS_EXTRA_LESSON,
|
||||
EYES_OUTLINE_LESSON,
|
||||
EYES_DETAIL_LESSON,
|
||||
EYES_EXTRA_LESSON,
|
||||
FACE_OUTLINE_LESSON,
|
||||
FACE_DETAIL_LESSON,
|
||||
FACE_EXTRA_LESSON
|
||||
];
|
||||
|
||||
+247
-213
@@ -1,213 +1,247 @@
|
||||
/**
|
||||
* DrawIt curriculum. Each Early Beginner subject is a 3-lesson set: Outline -> Details -> Color it.
|
||||
* Outline/Detail steps use per-step `lines` (coloring-book strokes drawn one at a time). The Detail
|
||||
* lesson awards the subject badge (it is gated behind Outline). `mode "shade"` = shading studio.
|
||||
*/
|
||||
export interface LevelMeta { key: string; name: string; emoji: string; blurb: string; }
|
||||
export const LEVELS: LevelMeta[] = [
|
||||
{ key: "early-beginner", name: "Early Beginner", emoji: "\u{1F331}", blurb: "First lines and simple shapes. Perfect for brand-new artists." },
|
||||
{ key: "beginner", name: "Beginner", emoji: "\u270F\uFE0F", blurb: "Combine shapes into friendly characters and objects." },
|
||||
{ key: "learner", name: "Learner", emoji: "\u{1F3A8}", blurb: "Add details, shading, and your own ideas." },
|
||||
{ key: "advanced-learned", name: "Advanced Learner", emoji: "\u{1F680}", blurb: "Proportion, perspective, and more complex scenes." },
|
||||
{ key: "superb", name: "Superb", emoji: "\u{1F31F}", blurb: "Confident, expressive drawing in your own style." },
|
||||
];
|
||||
export function getLevel(key: string): LevelMeta | undefined { return LEVELS.find((l) => l.key === key); }
|
||||
export type StepKind = "construct" | "outline" | "color" | "shade" | "detail";
|
||||
// Early Beginner uses outline/detail/extra; Beginner uses construct/outline/color/light.
|
||||
export type Phase = "construct" | "outline" | "detail" | "color" | "light" | "extra";
|
||||
export interface LessonStep { n: number; title: string; instruction: string; tip?: string; kind?: StepKind; svg?: string; lines?: string[]; }
|
||||
export interface Lesson {
|
||||
level: string; sublevel: number; slug: string; title: string; subject: string; emoji: string; intro: string;
|
||||
badgeKey: string; badgeName: string; mode?: "draw" | "shade"; phase?: Phase; baseSvg?: string;
|
||||
subjectKey?: string; subjectName?: string; subjectEmoji?: string; order?: number; packKey?: string; steps: LessonStep[];
|
||||
}
|
||||
|
||||
|
||||
const FISH_OUTLINE_STEPS: LessonStep[] = [
|
||||
{ n: 1, title: "The body", instruction: "Draw the big rounded body — loop all the way around.", tip: "Slow and smooth, like an egg.", lines: [`<path d="M85 150 C 95 95, 180 78, 252 96 C 292 107, 306 128, 306 150 C 306 172, 292 193, 252 204 C 180 222, 95 205, 85 150 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n: 2, title: "The tail", instruction: "Add the fan-shaped tail at the back.", lines: [`<path d="M300 150 C 332 130, 360 112, 390 100 C 374 130, 374 170, 390 200 C 360 188, 332 170, 300 150 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n: 3, title: "The fins", instruction: "Draw a fin on the back, then one under the belly.", lines: [`<path d="M150 92 C 168 62, 212 60, 240 84" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M150 178 C 168 208, 206 210, 228 196" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const FISH_DETAIL_STEPS: LessonStep[] = [
|
||||
{ n: 1, title: "The eye", instruction: "Draw a round eye, then a little dot inside.", lines: [`<path d="M147 134 a15 15 0 1 0 0.1 0" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<circle cx="129" cy="136" r="6" fill="#2b2440"/>`] },
|
||||
{ n: 2, title: "The mouth", instruction: "Add a small smile at the front.", lines: [`<path d="M90 162 q15 13 32 4" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n: 3, title: "The gill", instruction: "Add a curved gill line behind the head.", lines: [`<path d="M160 104 q-16 46 0 92" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n: 4, title: "Stripes", instruction: "Add a couple of curved stripes across the body.", tip: "Now it's ready to color!", lines: [`<path d="M205 96 q-20 54 4 110" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M240 100 q-16 50 4 100" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const FISH_OUTLINE_SVG = FISH_OUTLINE_STEPS.flatMap((s) => s.lines || []).join("\n");
|
||||
|
||||
const FISH_DETAIL_SVG = FISH_OUTLINE_SVG + "\n" + FISH_DETAIL_STEPS.flatMap((s) => s.lines || []).join("\n");
|
||||
|
||||
export const FISH_OUTLINE_LESSON: Lesson = { level:"early-beginner", sublevel:11, slug:"fish-outline", title:"Fish \u00b7 Outline", subject:"fish outline", emoji:"🐟", intro:"Let\u2019s draw Fish\u2019s outline, one line at a time. Watch each line, then trace it!", badgeKey:"", badgeName:"Fish Friend", phase:"outline", subjectKey:"fish", subjectName:"Fish", subjectEmoji:"🐟", order:1, steps: FISH_OUTLINE_STEPS };
|
||||
|
||||
export const FISH_DETAIL_LESSON: Lesson = { level:"early-beginner", sublevel:12, slug:"fish-detail", title:"Fish \u00b7 Details", subject:"fish details", emoji:"🐟", intro:"Now add the details that bring your fish to life.", badgeKey:"early-beginner-1-fish", badgeName:"Fish Friend", phase:"detail", baseSvg: FISH_OUTLINE_SVG, subjectKey:"fish", subjectName:"Fish", subjectEmoji:"🐟", order:1, steps: FISH_DETAIL_STEPS };
|
||||
|
||||
export const FISH_EXTRA_LESSON: Lesson = { level:"early-beginner", sublevel:13, slug:"fish-extra", title:"Fish \u00b7 Color it!", subject:"color the fish", emoji:"\u{1F3A8}", intro:"Bring your fish to life! Color it in, and try shading and layering.", badgeKey:"", badgeName:"Fish Friend", phase:"extra", baseSvg: FISH_DETAIL_SVG, subjectKey:"fish", subjectName:"Fish", subjectEmoji:"🐟", order:1, steps: [] };
|
||||
|
||||
const PANDA_OUTLINE_STEPS: LessonStep[] = [
|
||||
{ n: 1, title: "The head", instruction: "Draw one big round face.", tip: "Nice and round.", lines: [`<ellipse cx="200" cy="162" rx="92" ry="84" fill="none" stroke="#2b2440" stroke-width="4"/>`] },
|
||||
{ n: 2, title: "The ears", instruction: "Add two round ears on top.", lines: [`<circle cx="142" cy="92" r="30" fill="none" stroke="#2b2440" stroke-width="4"/>`, `<circle cx="258" cy="92" r="30" fill="none" stroke="#2b2440" stroke-width="4"/>`] },
|
||||
{ n: 3, title: "Eye patches", instruction: "Draw a leaf-shaped patch for each eye.", lines: [`<path d="M150 132 C 136 148, 140 178, 164 184 C 186 188, 196 166, 189 146 C 183 132, 164 124, 150 132 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M250 132 C 264 148, 260 178, 236 184 C 214 188, 204 166, 211 146 C 217 132, 236 124, 250 132 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const PANDA_DETAIL_STEPS: LessonStep[] = [
|
||||
{ n: 1, title: "The eyes", instruction: "Add a circle and a dot inside each patch.", lines: [`<circle cx="168" cy="156" r="9" fill="none" stroke="#2b2440" stroke-width="3"/>`, `<circle cx="170" cy="158" r="4" fill="#2b2440"/>`, `<circle cx="232" cy="156" r="9" fill="none" stroke="#2b2440" stroke-width="3"/>`, `<circle cx="230" cy="158" r="4" fill="#2b2440"/>`] },
|
||||
{ n: 2, title: "The nose", instruction: "Draw a little rounded nose.", lines: [`<path d="M186 184 Q200 178 214 184 Q208 198 200 200 Q192 198 186 184 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n: 3, title: "The mouth", instruction: "Add a line down and two smile curves.", lines: [`<path d="M200 200 L200 210" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M200 210 q-13 12 -26 5" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M200 210 q13 12 26 5" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const PANDA_OUTLINE_SVG = PANDA_OUTLINE_STEPS.flatMap((s) => s.lines || []).join("\n");
|
||||
|
||||
const PANDA_DETAIL_SVG = PANDA_OUTLINE_SVG + "\n" + PANDA_DETAIL_STEPS.flatMap((s) => s.lines || []).join("\n");
|
||||
|
||||
export const PANDA_OUTLINE_LESSON: Lesson = { level:"early-beginner", sublevel:21, slug:"panda-outline", title:"Panda \u00b7 Outline", subject:"panda outline", emoji:"🐼", intro:"Let\u2019s draw Panda\u2019s outline, one line at a time. Watch each line, then trace it!", badgeKey:"", badgeName:"Panda Pal", phase:"outline", subjectKey:"panda", subjectName:"Panda", subjectEmoji:"🐼", order:2, steps: PANDA_OUTLINE_STEPS };
|
||||
|
||||
export const PANDA_DETAIL_LESSON: Lesson = { level:"early-beginner", sublevel:22, slug:"panda-detail", title:"Panda \u00b7 Details", subject:"panda details", emoji:"🐼", intro:"Now add the details that bring your panda to life.", badgeKey:"early-beginner-2-panda", badgeName:"Panda Pal", phase:"detail", baseSvg: PANDA_OUTLINE_SVG, subjectKey:"panda", subjectName:"Panda", subjectEmoji:"🐼", order:2, steps: PANDA_DETAIL_STEPS };
|
||||
|
||||
export const PANDA_EXTRA_LESSON: Lesson = { level:"early-beginner", sublevel:23, slug:"panda-extra", title:"Panda \u00b7 Color it!", subject:"color the panda", emoji:"\u{1F3A8}", intro:"Bring your panda to life! Color it in, and try shading and layering.", badgeKey:"", badgeName:"Panda Pal", phase:"extra", baseSvg: PANDA_DETAIL_SVG, subjectKey:"panda", subjectName:"Panda", subjectEmoji:"🐼", order:2, steps: [] };
|
||||
|
||||
const FLOWER_OUTLINE_STEPS: LessonStep[] = [
|
||||
{ n: 1, title: "The center", instruction: "Draw a circle in the middle.", tip: "Leave room for petals.", lines: [`<circle cx="200" cy="118" r="34" fill="none" stroke="#2b2440" stroke-width="4"/>`] },
|
||||
{ n: 2, title: "Petals", instruction: "Add petals on top, bottom, left, and right.", lines: [`<path d="M200 86 C 180 78, 176 52, 200 40 C 224 52, 220 78, 200 86 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linejoin="round" transform="rotate(0 200 118)"/>`, `<path d="M200 86 C 180 78, 176 52, 200 40 C 224 52, 220 78, 200 86 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linejoin="round" transform="rotate(90 200 118)"/>`, `<path d="M200 86 C 180 78, 176 52, 200 40 C 224 52, 220 78, 200 86 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linejoin="round" transform="rotate(180 200 118)"/>`, `<path d="M200 86 C 180 78, 176 52, 200 40 C 224 52, 220 78, 200 86 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linejoin="round" transform="rotate(270 200 118)"/>`] },
|
||||
{ n: 3, title: "More petals", instruction: "Tuck four more petals into the gaps.", lines: [`<path d="M200 86 C 180 78, 176 52, 200 40 C 224 52, 220 78, 200 86 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linejoin="round" transform="rotate(45 200 118)"/>`, `<path d="M200 86 C 180 78, 176 52, 200 40 C 224 52, 220 78, 200 86 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linejoin="round" transform="rotate(135 200 118)"/>`, `<path d="M200 86 C 180 78, 176 52, 200 40 C 224 52, 220 78, 200 86 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linejoin="round" transform="rotate(225 200 118)"/>`, `<path d="M200 86 C 180 78, 176 52, 200 40 C 224 52, 220 78, 200 86 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linejoin="round" transform="rotate(315 200 118)"/>`] },
|
||||
{ n: 4, title: "The stem", instruction: "Draw a stem going down from the flower.", lines: [`<path d="M195 152 C 191 186, 205 214, 197 256 L203 256 C 211 214, 199 186, 205 152 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n: 5, title: "The leaves", instruction: "Add a leaf on each side of the stem.", lines: [`<path d="M200 198 C 168 184, 138 192, 150 218 C 162 240, 196 226, 200 198 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M200 226 C 232 214, 262 222, 250 246 C 238 266, 204 252, 200 226 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const FLOWER_DETAIL_STEPS: LessonStep[] = [
|
||||
{ n: 1, title: "Seeds", instruction: "Dot some seeds in the middle.", lines: [`<circle cx="192" cy="112" r="4" fill="#2b2440"/>`, `<circle cx="208" cy="114" r="4" fill="#2b2440"/>`, `<circle cx="200" cy="124" r="4" fill="#2b2440"/>`, `<circle cx="190" cy="126" r="3" fill="#2b2440"/>`, `<circle cx="210" cy="126" r="3" fill="#2b2440"/>`] },
|
||||
{ n: 2, title: "Leaf veins", instruction: "Add a line down each leaf.", lines: [`<path d="M170 210 q20 4 28 14" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M232 240 q-18 2 -28 -10" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n: 3, title: "Center ring", instruction: "Draw a little ring inside the middle.", lines: [`<circle cx="200" cy="118" r="18" fill="none" stroke="#2b2440" stroke-width="3"/>`] },
|
||||
];
|
||||
|
||||
const FLOWER_OUTLINE_SVG = FLOWER_OUTLINE_STEPS.flatMap((s) => s.lines || []).join("\n");
|
||||
|
||||
const FLOWER_DETAIL_SVG = FLOWER_OUTLINE_SVG + "\n" + FLOWER_DETAIL_STEPS.flatMap((s) => s.lines || []).join("\n");
|
||||
|
||||
export const FLOWER_OUTLINE_LESSON: Lesson = { level:"early-beginner", sublevel:31, slug:"flower-outline", title:"Flower \u00b7 Outline", subject:"flower outline", emoji:"🌸", intro:"Let\u2019s draw Flower\u2019s outline, one line at a time. Watch each line, then trace it!", badgeKey:"", badgeName:"Flower Power", phase:"outline", subjectKey:"flower", subjectName:"Flower", subjectEmoji:"🌸", order:3, steps: FLOWER_OUTLINE_STEPS };
|
||||
|
||||
export const FLOWER_DETAIL_LESSON: Lesson = { level:"early-beginner", sublevel:32, slug:"flower-detail", title:"Flower \u00b7 Details", subject:"flower details", emoji:"🌸", intro:"Now add the details that bring your flower to life.", badgeKey:"early-beginner-3-flower", badgeName:"Flower Power", phase:"detail", baseSvg: FLOWER_OUTLINE_SVG, subjectKey:"flower", subjectName:"Flower", subjectEmoji:"🌸", order:3, steps: FLOWER_DETAIL_STEPS };
|
||||
|
||||
export const FLOWER_EXTRA_LESSON: Lesson = { level:"early-beginner", sublevel:33, slug:"flower-extra", title:"Flower \u00b7 Color it!", subject:"color the flower", emoji:"\u{1F3A8}", intro:"Bring your flower to life! Color it in, and try shading and layering.", badgeKey:"", badgeName:"Flower Power", phase:"extra", baseSvg: FLOWER_DETAIL_SVG, subjectKey:"flower", subjectName:"Flower", subjectEmoji:"🌸", order:3, steps: [] };
|
||||
|
||||
const UNICORN_OUTLINE_STEPS: LessonStep[] = [
|
||||
{ n: 1, title: "The head", instruction: "Draw a rounded head.", lines: [`<path d="M120 152 C 120 96, 165 72, 200 72 C 235 72, 280 96, 280 152 C 280 202, 246 236, 200 240 C 154 236, 120 202, 120 152 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n: 2, title: "The ears", instruction: "Add a pointy ear on each side.", lines: [`<path d="M150 98 C 138 72, 132 58, 150 60 C 166 64, 174 84, 180 96 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M250 98 C 262 72, 268 58, 250 60 C 234 64, 226 84, 220 96 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n: 3, title: "The horn", instruction: "Draw a tall horn on top.", lines: [`<path d="M200 80 L188 18 L212 80 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n: 4, title: "The mane", instruction: "Draw the flowing mane and a curl on the forehead.", lines: [`<path d="M256 112 C 320 122, 320 200, 268 250 C 292 196, 280 150, 250 142 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M176 92 C 165 110, 175 135, 196 130 C 184 118, 188 102, 200 96 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const UNICORN_DETAIL_STEPS: LessonStep[] = [
|
||||
{ n: 1, title: "The eyes", instruction: "Add two big eyes.", lines: [`<ellipse cx="172" cy="158" rx="7" ry="10" fill="none" stroke="#2b2440" stroke-width="4"/>`, `<ellipse cx="228" cy="158" rx="7" ry="10" fill="none" stroke="#2b2440" stroke-width="4"/>`] },
|
||||
{ n: 2, title: "Nose & mouth", instruction: "Add two nostril dots and a smile.", lines: [`<circle cx="188" cy="210" r="3" fill="#2b2440"/>`, `<circle cx="214" cy="210" r="3" fill="#2b2440"/>`, `<path d="M186 224 q14 12 28 0" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n: 3, title: "Horn swirls", instruction: "Add little swirl lines on the horn.", lines: [`<path d="M193 66 l14 -5" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M196 50 l11 -4" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M199 36 l8 -3" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n: 4, title: "Mane lines", instruction: "Add flowing lines in the mane.", lines: [`<path d="M268 130 C 300 150, 296 200, 262 232" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M258 140 C 286 160, 282 200, 256 226" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const UNICORN_OUTLINE_SVG = UNICORN_OUTLINE_STEPS.flatMap((s) => s.lines || []).join("\n");
|
||||
|
||||
const UNICORN_DETAIL_SVG = UNICORN_OUTLINE_SVG + "\n" + UNICORN_DETAIL_STEPS.flatMap((s) => s.lines || []).join("\n");
|
||||
|
||||
export const UNICORN_OUTLINE_LESSON: Lesson = { level:"early-beginner", sublevel:41, slug:"unicorn-outline", title:"Unicorn \u00b7 Outline", subject:"unicorn outline", emoji:"🦄", intro:"Let\u2019s draw Unicorn\u2019s outline, one line at a time. Watch each line, then trace it!", badgeKey:"", badgeName:"Unicorn Magic", phase:"outline", subjectKey:"unicorn", subjectName:"Unicorn", subjectEmoji:"🦄", order:4, steps: UNICORN_OUTLINE_STEPS };
|
||||
|
||||
export const UNICORN_DETAIL_LESSON: Lesson = { level:"early-beginner", sublevel:42, slug:"unicorn-detail", title:"Unicorn \u00b7 Details", subject:"unicorn details", emoji:"🦄", intro:"Now add the details that bring your unicorn to life.", badgeKey:"early-beginner-4-unicorn", badgeName:"Unicorn Magic", phase:"detail", baseSvg: UNICORN_OUTLINE_SVG, subjectKey:"unicorn", subjectName:"Unicorn", subjectEmoji:"🦄", order:4, steps: UNICORN_DETAIL_STEPS };
|
||||
|
||||
export const UNICORN_EXTRA_LESSON: Lesson = { level:"early-beginner", sublevel:43, slug:"unicorn-extra", title:"Unicorn \u00b7 Color it!", subject:"color the unicorn", emoji:"\u{1F3A8}", intro:"Bring your unicorn to life! Color it in, and try shading and layering.", badgeKey:"", badgeName:"Unicorn Magic", phase:"extra", baseSvg: UNICORN_DETAIL_SVG, subjectKey:"unicorn", subjectName:"Unicorn", subjectEmoji:"🦄", order:4, steps: [] };
|
||||
|
||||
const TREE_OUTLINE_STEPS: LessonStep[] = [
|
||||
{ n: 1, title: "The trunk", instruction: "Draw a tall trunk, wider at the bottom.", lines: [`<path d="M178 262 C 176 222, 184 200, 190 176 L210 176 C 216 200, 224 222, 222 262 C 214 266, 186 266, 178 262 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n: 2, title: "Branches", instruction: "Add two branches reaching up.", lines: [`<path d="M198 180 C 190 152, 168 144, 152 134" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M204 180 C 214 152, 236 146, 252 136" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n: 3, title: "The treetop", instruction: "Draw a big bumpy treetop.", tip: "Nice and lumpy!", lines: [`<path d="M120 150 C 100 122, 122 96, 150 100 C 152 74, 200 62, 216 88 C 246 72, 286 92, 274 122 C 300 134, 292 168, 262 166 C 250 188, 150 188, 138 166 C 110 170, 104 152, 120 150 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const TREE_DETAIL_STEPS: LessonStep[] = [
|
||||
{ n: 1, title: "Apples", instruction: "Add a few round apples in the leaves.", lines: [`<circle cx="168" cy="118" r="7" fill="none" stroke="#2b2440" stroke-width="3"/>`, `<circle cx="228" cy="104" r="7" fill="none" stroke="#2b2440" stroke-width="3"/>`, `<circle cx="204" cy="150" r="7" fill="none" stroke="#2b2440" stroke-width="3"/>`] },
|
||||
{ n: 2, title: "Grass", instruction: "Add wavy grass along the bottom.", lines: [`<path d="M120 262 q14 -16 28 0 q14 -16 28 0 q14 -16 28 0 q14 -16 28 0 q14 -16 28 0" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n: 3, title: "Bark", instruction: "Add a couple of bark lines on the trunk.", lines: [`<path d="M194 200 q4 24 0 56" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M206 200 q-3 24 0 54" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const TREE_OUTLINE_SVG = TREE_OUTLINE_STEPS.flatMap((s) => s.lines || []).join("\n");
|
||||
|
||||
const TREE_DETAIL_SVG = TREE_OUTLINE_SVG + "\n" + TREE_DETAIL_STEPS.flatMap((s) => s.lines || []).join("\n");
|
||||
|
||||
export const TREE_OUTLINE_LESSON: Lesson = { level:"early-beginner", sublevel:51, slug:"tree-outline", title:"Tree \u00b7 Outline", subject:"tree outline", emoji:"🌳", intro:"Let\u2019s draw Tree\u2019s outline, one line at a time. Watch each line, then trace it!", badgeKey:"", badgeName:"Tree Top", phase:"outline", subjectKey:"tree", subjectName:"Tree", subjectEmoji:"🌳", order:5, steps: TREE_OUTLINE_STEPS };
|
||||
|
||||
export const TREE_DETAIL_LESSON: Lesson = { level:"early-beginner", sublevel:52, slug:"tree-detail", title:"Tree \u00b7 Details", subject:"tree details", emoji:"🌳", intro:"Now add the details that bring your tree to life.", badgeKey:"early-beginner-5-tree", badgeName:"Tree Top", phase:"detail", baseSvg: TREE_OUTLINE_SVG, subjectKey:"tree", subjectName:"Tree", subjectEmoji:"🌳", order:5, steps: TREE_DETAIL_STEPS };
|
||||
|
||||
export const TREE_EXTRA_LESSON: Lesson = { level:"early-beginner", sublevel:53, slug:"tree-extra", title:"Tree \u00b7 Color it!", subject:"color the tree", emoji:"\u{1F3A8}", intro:"Bring your tree to life! Color it in, and try shading and layering.", badgeKey:"", badgeName:"Tree Top", phase:"extra", baseSvg: TREE_DETAIL_SVG, subjectKey:"tree", subjectName:"Tree", subjectEmoji:"🌳", order:5, steps: [] };
|
||||
|
||||
const DINO_OUTLINE_STEPS: LessonStep[] = [
|
||||
{ n: 1, title: "The body", instruction: "Draw a big round body.", lines: [`<path d="M118 200 C 118 166, 160 150, 210 150 C 270 150, 302 170, 302 200 C 302 230, 262 246, 205 246 C 155 246, 118 230, 118 200 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n: 2, title: "The neck", instruction: "Sweep a long neck up from the left.", lines: [`<path d="M150 168 C 122 122, 112 80, 124 55 C 130 44, 148 46, 152 58 C 146 86, 162 124, 180 160 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n: 3, title: "The head", instruction: "Add a small head on top of the neck.", lines: [`<path d="M100 46 C 100 32, 120 28, 138 34 C 152 39, 154 56, 142 64 C 126 72, 104 64, 98 54 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n: 4, title: "The tail", instruction: "Draw a long tail swooping to the right.", lines: [`<path d="M298 198 C 338 184, 372 176, 398 168 C 380 192, 360 206, 304 220 C 300 213, 298 206, 298 198 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n: 5, title: "The legs", instruction: "Add two sturdy legs underneath.", lines: [`<path d="M168 242 C 165 264, 166 278, 180 280 C 193 280, 192 264, 190 242 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M232 244 C 229 266, 230 280, 244 282 C 257 282, 256 266, 254 244 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const DINO_DETAIL_STEPS: LessonStep[] = [
|
||||
{ n: 1, title: "Back plates", instruction: "Add triangle plates along the back.", lines: [`<path d="M150 150 l10 -16 l12 16 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M186 146 l11 -17 l12 17 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M224 147 l11 -16 l12 16 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M262 152 l10 -15 l12 16 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n: 2, title: "Spots", instruction: "Add a few spots on the body.", lines: [`<circle cx="205" cy="188" r="9" fill="none" stroke="#2b2440" stroke-width="3"/>`, `<circle cx="240" cy="200" r="7" fill="none" stroke="#2b2440" stroke-width="3"/>`, `<circle cx="180" cy="212" r="6" fill="none" stroke="#2b2440" stroke-width="3"/>`] },
|
||||
{ n: 3, title: "The face", instruction: "Add an eye and a friendly smile.", lines: [`<circle cx="118" cy="46" r="4" fill="#2b2440"/>`, `<path d="M108 56 q12 10 24 3" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const DINO_OUTLINE_SVG = DINO_OUTLINE_STEPS.flatMap((s) => s.lines || []).join("\n");
|
||||
|
||||
const DINO_DETAIL_SVG = DINO_OUTLINE_SVG + "\n" + DINO_DETAIL_STEPS.flatMap((s) => s.lines || []).join("\n");
|
||||
|
||||
export const DINO_OUTLINE_LESSON: Lesson = { level:"early-beginner", sublevel:61, slug:"dinosaur-outline", title:"Dinosaur \u00b7 Outline", subject:"dinosaur outline", emoji:"🦕", intro:"Let\u2019s draw Dinosaur\u2019s outline, one line at a time. Watch each line, then trace it!", badgeKey:"", badgeName:"Dino Master", phase:"outline", subjectKey:"dinosaur", subjectName:"Dinosaur", subjectEmoji:"🦕", order:6, steps: DINO_OUTLINE_STEPS };
|
||||
|
||||
export const DINO_DETAIL_LESSON: Lesson = { level:"early-beginner", sublevel:62, slug:"dinosaur-detail", title:"Dinosaur \u00b7 Details", subject:"dinosaur details", emoji:"🦕", intro:"Now add the details that bring your dinosaur to life.", badgeKey:"early-beginner-6-dinosaur", badgeName:"Dino Master", phase:"detail", baseSvg: DINO_OUTLINE_SVG, subjectKey:"dinosaur", subjectName:"Dinosaur", subjectEmoji:"🦕", order:6, steps: DINO_DETAIL_STEPS };
|
||||
|
||||
export const DINO_EXTRA_LESSON: Lesson = { level:"early-beginner", sublevel:63, slug:"dinosaur-extra", title:"Dinosaur \u00b7 Color it!", subject:"color the dinosaur", emoji:"\u{1F3A8}", intro:"Bring your dinosaur to life! Color it in, and try shading and layering.", badgeKey:"", badgeName:"Dino Master", phase:"extra", baseSvg: DINO_DETAIL_SVG, subjectKey:"dinosaur", subjectName:"Dinosaur", subjectEmoji:"🦕", order:6, steps: [] };
|
||||
|
||||
const SHADE_STEPS: LessonStep[] = [
|
||||
{ n: 1, kind: "shade", title: "How to hold your pencil", instruction: "Watch the demo above. Near the tip + press hard = DARK; high up + soft = LIGHT.", tip: "Try it in the air first!", svg: `<g><circle cx="62" cy="46" r="20" fill="#ffd45e"/><g stroke="#ffd45e" stroke-width="4" stroke-linecap="round"><path d="M62 14v-8M62 86v8M30 46h-8M94 46h8M39 23l-6-6M85 23l6-6M39 69l-6 6M85 69l6 6"/></g></g><line x1="40" y1="234" x2="370" y2="234" stroke="#c9c4d6" stroke-width="3"/><circle cx="210" cy="150" r="84" fill="#f1f1f4" stroke="#2b2440" stroke-width="4"/>` },
|
||||
{ n: 2, kind: "shade", title: "Find the light", instruction: "The sun is up-left. The side facing it stays brightest — that bright spot is the highlight.", svg: `<g><circle cx="62" cy="46" r="20" fill="#ffd45e"/><g stroke="#ffd45e" stroke-width="4" stroke-linecap="round"><path d="M62 14v-8M62 86v8M30 46h-8M94 46h8M39 23l-6-6M85 23l6-6M39 69l-6 6M85 69l6 6"/></g></g><line x1="40" y1="234" x2="370" y2="234" stroke="#c9c4d6" stroke-width="3"/><circle cx="210" cy="150" r="84" fill="#f1f1f4" stroke="#2b2440" stroke-width="4"/><circle cx="178" cy="118" r="22" fill="none" stroke="#4f7cff" stroke-width="3" stroke-dasharray="6 6"/>` },
|
||||
{ n: 3, kind: "shade", title: "Soft shade all over", instruction: "Pick a light tone and softly shade the whole ball — leave the bright spot white.", tip: "Hold high and go easy.", svg: `<g><circle cx="62" cy="46" r="20" fill="#ffd45e"/><g stroke="#ffd45e" stroke-width="4" stroke-linecap="round"><path d="M62 14v-8M62 86v8M30 46h-8M94 46h8M39 23l-6-6M85 23l6-6M39 69l-6 6M85 69l6 6"/></g></g><line x1="40" y1="234" x2="370" y2="234" stroke="#c9c4d6" stroke-width="3"/><circle cx="210" cy="150" r="84" fill="#f1f1f4" stroke="#2b2440" stroke-width="4"/><circle cx="178" cy="118" r="22" fill="none" stroke="#4f7cff" stroke-width="3" stroke-dasharray="6 6"/>` },
|
||||
{ n: 4, kind: "shade", title: "Build up the shadow", instruction: "Pick a darker tone and press harder on the side away from the sun. Build it up slowly.", tip: "More passes = darker.", svg: `<g><circle cx="62" cy="46" r="20" fill="#ffd45e"/><g stroke="#ffd45e" stroke-width="4" stroke-linecap="round"><path d="M62 14v-8M62 86v8M30 46h-8M94 46h8M39 23l-6-6M85 23l6-6M39 69l-6 6M85 69l6 6"/></g></g><line x1="40" y1="234" x2="370" y2="234" stroke="#c9c4d6" stroke-width="3"/><circle cx="210" cy="150" r="84" fill="#f1f1f4" stroke="#2b2440" stroke-width="4"/><path d="M250 96 A84 84 0 0 1 250 204 A60 70 0 0 0 250 96 Z" fill="#2b2440" opacity="0.18"/>` },
|
||||
{ n: 5, kind: "shade", title: "Cast shadow & finish", instruction: "Shade the ground under the ball on the shadow side. Keep the highlight clean!", tip: "🌗", svg: `<g><circle cx="62" cy="46" r="20" fill="#ffd45e"/><g stroke="#ffd45e" stroke-width="4" stroke-linecap="round"><path d="M62 14v-8M62 86v8M30 46h-8M94 46h8M39 23l-6-6M85 23l6-6M39 69l-6 6M85 69l6 6"/></g></g><line x1="40" y1="234" x2="370" y2="234" stroke="#c9c4d6" stroke-width="3"/><circle cx="210" cy="150" r="84" fill="#f1f1f4" stroke="#2b2440" stroke-width="4"/><path d="M250 96 A84 84 0 0 1 250 204 A60 70 0 0 0 250 96 Z" fill="#2b2440" opacity="0.18"/><ellipse cx="262" cy="236" rx="78" ry="15" fill="#2b2440" opacity="0.18"/>` },
|
||||
];
|
||||
|
||||
export const SHADE_BALL_LESSON: Lesson = { level:"learner", sublevel:1, slug:"shade-ball", title:"Light & Shadow: Shade a Ball", subject:"a shaded ball", emoji:"\u{1F317}", intro:"Make a flat circle look like a round ball using light and shadow!", badgeKey:"learner-1-shading", badgeName:"Shading Star", mode:"shade", subjectKey:"shade-ball", subjectName:"Shade a Ball", subjectEmoji:"\u{1F317}", order:1, steps: SHADE_STEPS };
|
||||
|
||||
import { BEGINNER_LESSONS, BEGINNER_PACKS, type Pack } from "./curriculum.beginner";
|
||||
export { BEGINNER_PACKS, type Pack } from "./curriculum.beginner";
|
||||
export { LIGHT_HINT_SVG } from "./curriculum.beginner";
|
||||
|
||||
const EARLY_BEGINNER_LESSONS: Lesson[] = [FISH_OUTLINE_LESSON, FISH_DETAIL_LESSON, FISH_EXTRA_LESSON, PANDA_OUTLINE_LESSON, PANDA_DETAIL_LESSON, PANDA_EXTRA_LESSON, FLOWER_OUTLINE_LESSON, FLOWER_DETAIL_LESSON, FLOWER_EXTRA_LESSON, UNICORN_OUTLINE_LESSON, UNICORN_DETAIL_LESSON, UNICORN_EXTRA_LESSON, TREE_OUTLINE_LESSON, TREE_DETAIL_LESSON, TREE_EXTRA_LESSON, DINO_OUTLINE_LESSON, DINO_DETAIL_LESSON, DINO_EXTRA_LESSON];
|
||||
export const LESSONS: Lesson[] = [...EARLY_BEGINNER_LESSONS, ...BEGINNER_LESSONS, SHADE_BALL_LESSON];
|
||||
|
||||
export function getLesson(level: string, sublevel: number): Lesson | undefined { return LESSONS.find((l) => l.level === level && l.sublevel === sublevel); }
|
||||
export function getLessonBySlug(slug: string): Lesson | undefined { return LESSONS.find((l) => l.slug === slug); }
|
||||
export function cumulativeSvg(lesson: Lesson, upTo: number): string { return lesson.steps.filter((s) => s.n <= upTo).map((s) => s.svg || "").join("\n"); }
|
||||
export function cumulativeLines(lesson: Lesson, upTo: number): string { return lesson.steps.filter((s) => s.n <= upTo).flatMap((s) => s.lines || []).join("\n"); }
|
||||
export function getSubjects(level: string): { key: string; name: string; emoji: string; order: number; lessons: Lesson[] }[] {
|
||||
const inLevel = LESSONS.filter((l) => l.level === level);
|
||||
const map = new Map<string, { key: string; name: string; emoji: string; order: number; lessons: Lesson[] }>();
|
||||
for (const l of inLevel) {
|
||||
const key = l.subjectKey || l.slug;
|
||||
if (!map.has(key)) map.set(key, { key, name: l.subjectName || l.title, emoji: l.subjectEmoji || l.emoji, order: l.order ?? l.sublevel, lessons: [] });
|
||||
map.get(key)!.lessons.push(l);
|
||||
}
|
||||
// Covers both Early Beginner (outline/detail/extra) and Beginner (construct/outline/color/light).
|
||||
const rank: Record<string, number> = { construct: 0, outline: 1, detail: 2, color: 3, extra: 3, light: 4 };
|
||||
for (const s of map.values()) s.lessons.sort((a, b) => (rank[a.phase ?? ""] ?? 0) - (rank[b.phase ?? ""] ?? 0));
|
||||
return [...map.values()].sort((a, b) => a.order - b.order);
|
||||
}
|
||||
|
||||
/** All badge definitions: per-subject (Early Beginner) + per-pack (Beginner). */
|
||||
export interface BadgeDef { badgeKey: string; name: string; emoji: string; groupKey: string; }
|
||||
export function badgeDefs(): BadgeDef[] {
|
||||
const fromLessons = LESSONS.filter((l) => l.badgeKey).map((l) => ({ badgeKey: l.badgeKey, name: l.badgeName, emoji: l.emoji, groupKey: `${l.level}-${l.sublevel}` }));
|
||||
const fromPacks = BEGINNER_PACKS.map((p) => ({ badgeKey: p.badgeKey, name: p.badgeName, emoji: p.emoji, groupKey: "" }));
|
||||
return [...fromLessons, ...fromPacks];
|
||||
}
|
||||
export function getPackByBadge(badgeKey: string): Pack | undefined { return BEGINNER_PACKS.find((p) => p.badgeKey === badgeKey); }
|
||||
/** The pack a lesson belongs to (Beginner only), via its packKey. */
|
||||
export function getPackForLesson(lesson: Lesson): Pack | undefined { return lesson.packKey ? BEGINNER_PACKS.find((p) => p.key === lesson.packKey) : undefined; }
|
||||
/** Every lesson that belongs to a pack (all phases of all its subjects). */
|
||||
export function lessonsInPack(pack: Pack): Lesson[] { return LESSONS.filter((l) => l.level === pack.level && l.subjectKey && pack.subjectKeys.includes(l.subjectKey)); }
|
||||
/** Number of completed steps needed to count a lesson as done. */
|
||||
export function lessonStepCount(l: Lesson): number { return l.steps.length || 1; }
|
||||
/**
|
||||
* DrawIt curriculum. Each Early Beginner subject is a 3-lesson set: Outline -> Details -> Color it.
|
||||
* Outline/Detail steps use per-step `lines` (coloring-book strokes drawn one at a time). The Detail
|
||||
* lesson awards the subject badge (it is gated behind Outline). `mode "shade"` = shading studio.
|
||||
*/
|
||||
export interface LevelMeta { key: string; name: string; emoji: string; blurb: string; }
|
||||
export const LEVELS: LevelMeta[] = [
|
||||
{ key: "early-beginner", name: "Early Beginner", emoji: "\u{1F331}", blurb: "First lines and simple shapes. Perfect for brand-new artists." },
|
||||
{ key: "beginner", name: "Beginner", emoji: "\u270F\uFE0F", blurb: "Combine shapes into friendly characters and objects." },
|
||||
{ key: "learner", name: "Learner", emoji: "\u{1F3A8}", blurb: "Add details, shading, and your own ideas." },
|
||||
{ key: "advanced-learned", name: "Advanced Learner", emoji: "\u{1F680}", blurb: "Proportion, perspective, and more complex scenes." },
|
||||
{ key: "superb", name: "Superb", emoji: "\u{1F31F}", blurb: "Confident, expressive drawing in your own style." },
|
||||
];
|
||||
export function getLevel(key: string): LevelMeta | undefined { return LEVELS.find((l) => l.key === key); }
|
||||
export type StepKind = "construct" | "outline" | "color" | "shade" | "detail";
|
||||
// Early Beginner uses outline/detail/extra; Beginner uses construct/outline/color/light.
|
||||
export type Phase = "construct" | "outline" | "detail" | "color" | "light" | "extra";
|
||||
export interface LessonStep { n: number; title: string; instruction: string; tip?: string; kind?: StepKind; svg?: string; lines?: string[]; }
|
||||
export interface Lesson {
|
||||
level: string; sublevel: number; slug: string; title: string; subject: string; emoji: string; intro: string;
|
||||
badgeKey: string; badgeName: string; mode?: "draw" | "shade"; phase?: Phase; baseSvg?: string;
|
||||
subjectKey?: string; subjectName?: string; subjectEmoji?: string; order?: number; packKey?: string; steps: LessonStep[];
|
||||
}
|
||||
|
||||
|
||||
const FISH_OUTLINE_STEPS: LessonStep[] = [
|
||||
{ n: 1, title: "The body", instruction: "Draw the big rounded body — loop all the way around.", tip: "Slow and smooth, like an egg.", lines: [`<path d="M85 150 C 95 95, 180 78, 252 96 C 292 107, 306 128, 306 150 C 306 172, 292 193, 252 204 C 180 222, 95 205, 85 150 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n: 2, title: "The tail", instruction: "Add the fan-shaped tail at the back.", lines: [`<path d="M300 150 C 332 130, 360 112, 390 100 C 374 130, 374 170, 390 200 C 360 188, 332 170, 300 150 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n: 3, title: "The fins", instruction: "Draw a fin on the back, then one under the belly.", lines: [`<path d="M150 92 C 168 62, 212 60, 240 84" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M150 178 C 168 208, 206 210, 228 196" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const FISH_DETAIL_STEPS: LessonStep[] = [
|
||||
{ n: 1, title: "The eye", instruction: "Draw a round eye, then a little dot inside.", lines: [`<path d="M147 134 a15 15 0 1 0 0.1 0" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<circle cx="129" cy="136" r="6" fill="#2b2440"/>`] },
|
||||
{ n: 2, title: "The mouth", instruction: "Add a small smile at the front.", lines: [`<path d="M90 162 q15 13 32 4" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n: 3, title: "The gill", instruction: "Add a curved gill line behind the head.", lines: [`<path d="M160 104 q-16 46 0 92" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n: 4, title: "Stripes", instruction: "Add a couple of curved stripes across the body.", tip: "Now it's ready to color!", lines: [`<path d="M205 96 q-20 54 4 110" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M240 100 q-16 50 4 100" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const FISH_OUTLINE_SVG = FISH_OUTLINE_STEPS.flatMap((s) => s.lines || []).join("\n");
|
||||
|
||||
const FISH_DETAIL_SVG = FISH_OUTLINE_SVG + "\n" + FISH_DETAIL_STEPS.flatMap((s) => s.lines || []).join("\n");
|
||||
|
||||
export const FISH_OUTLINE_LESSON: Lesson = { level:"early-beginner", sublevel:11, slug:"fish-outline", title:"Fish \u00b7 Outline", subject:"fish outline", emoji:"🐟", intro:"Let\u2019s draw Fish\u2019s outline, one line at a time. Watch each line, then trace it!", badgeKey:"", badgeName:"Fish Friend", phase:"outline", subjectKey:"fish", subjectName:"Fish", subjectEmoji:"🐟", order:1, steps: FISH_OUTLINE_STEPS };
|
||||
|
||||
export const FISH_DETAIL_LESSON: Lesson = { level:"early-beginner", sublevel:12, slug:"fish-detail", title:"Fish \u00b7 Details", subject:"fish details", emoji:"🐟", intro:"Now add the details that bring your fish to life.", badgeKey:"early-beginner-1-fish", badgeName:"Fish Friend", phase:"detail", baseSvg: FISH_OUTLINE_SVG, subjectKey:"fish", subjectName:"Fish", subjectEmoji:"🐟", order:1, steps: FISH_DETAIL_STEPS };
|
||||
|
||||
export const FISH_EXTRA_LESSON: Lesson = { level:"early-beginner", sublevel:13, slug:"fish-extra", title:"Fish \u00b7 Color it!", subject:"color the fish", emoji:"\u{1F3A8}", intro:"Bring your fish to life! Color it in, and try shading and layering.", badgeKey:"", badgeName:"Fish Friend", phase:"extra", baseSvg: FISH_DETAIL_SVG, subjectKey:"fish", subjectName:"Fish", subjectEmoji:"🐟", order:1, steps: [] };
|
||||
|
||||
const PANDA_OUTLINE_STEPS: LessonStep[] = [
|
||||
{ n: 1, title: "The head", instruction: "Draw one big round face.", tip: "Nice and round.", lines: [`<ellipse cx="200" cy="162" rx="92" ry="84" fill="none" stroke="#2b2440" stroke-width="4"/>`] },
|
||||
{ n: 2, title: "The ears", instruction: "Add two round ears on top.", lines: [`<circle cx="142" cy="92" r="30" fill="none" stroke="#2b2440" stroke-width="4"/>`, `<circle cx="258" cy="92" r="30" fill="none" stroke="#2b2440" stroke-width="4"/>`] },
|
||||
{ n: 3, title: "Eye patches", instruction: "Draw a leaf-shaped patch for each eye.", lines: [`<path d="M150 132 C 136 148, 140 178, 164 184 C 186 188, 196 166, 189 146 C 183 132, 164 124, 150 132 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M250 132 C 264 148, 260 178, 236 184 C 214 188, 204 166, 211 146 C 217 132, 236 124, 250 132 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const PANDA_DETAIL_STEPS: LessonStep[] = [
|
||||
{ n: 1, title: "The eyes", instruction: "Add a circle and a dot inside each patch.", lines: [`<circle cx="168" cy="156" r="9" fill="none" stroke="#2b2440" stroke-width="3"/>`, `<circle cx="170" cy="158" r="4" fill="#2b2440"/>`, `<circle cx="232" cy="156" r="9" fill="none" stroke="#2b2440" stroke-width="3"/>`, `<circle cx="230" cy="158" r="4" fill="#2b2440"/>`] },
|
||||
{ n: 2, title: "The nose", instruction: "Draw a little rounded nose.", lines: [`<path d="M186 184 Q200 178 214 184 Q208 198 200 200 Q192 198 186 184 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n: 3, title: "The mouth", instruction: "Add a line down and two smile curves.", lines: [`<path d="M200 200 L200 210" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M200 210 q-13 12 -26 5" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M200 210 q13 12 26 5" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const PANDA_OUTLINE_SVG = PANDA_OUTLINE_STEPS.flatMap((s) => s.lines || []).join("\n");
|
||||
|
||||
const PANDA_DETAIL_SVG = PANDA_OUTLINE_SVG + "\n" + PANDA_DETAIL_STEPS.flatMap((s) => s.lines || []).join("\n");
|
||||
|
||||
export const PANDA_OUTLINE_LESSON: Lesson = { level:"early-beginner", sublevel:21, slug:"panda-outline", title:"Panda \u00b7 Outline", subject:"panda outline", emoji:"🐼", intro:"Let\u2019s draw Panda\u2019s outline, one line at a time. Watch each line, then trace it!", badgeKey:"", badgeName:"Panda Pal", phase:"outline", subjectKey:"panda", subjectName:"Panda", subjectEmoji:"🐼", order:2, steps: PANDA_OUTLINE_STEPS };
|
||||
|
||||
export const PANDA_DETAIL_LESSON: Lesson = { level:"early-beginner", sublevel:22, slug:"panda-detail", title:"Panda \u00b7 Details", subject:"panda details", emoji:"🐼", intro:"Now add the details that bring your panda to life.", badgeKey:"early-beginner-2-panda", badgeName:"Panda Pal", phase:"detail", baseSvg: PANDA_OUTLINE_SVG, subjectKey:"panda", subjectName:"Panda", subjectEmoji:"🐼", order:2, steps: PANDA_DETAIL_STEPS };
|
||||
|
||||
export const PANDA_EXTRA_LESSON: Lesson = { level:"early-beginner", sublevel:23, slug:"panda-extra", title:"Panda \u00b7 Color it!", subject:"color the panda", emoji:"\u{1F3A8}", intro:"Bring your panda to life! Color it in, and try shading and layering.", badgeKey:"", badgeName:"Panda Pal", phase:"extra", baseSvg: PANDA_DETAIL_SVG, subjectKey:"panda", subjectName:"Panda", subjectEmoji:"🐼", order:2, steps: [] };
|
||||
|
||||
const FLOWER_OUTLINE_STEPS: LessonStep[] = [
|
||||
{ n: 1, title: "The center", instruction: "Draw a circle in the middle.", tip: "Leave room for petals.", lines: [`<circle cx="200" cy="118" r="34" fill="none" stroke="#2b2440" stroke-width="4"/>`] },
|
||||
{ n: 2, title: "Petals", instruction: "Add petals on top, bottom, left, and right.", lines: [`<path d="M200 86 C 180 78, 176 52, 200 40 C 224 52, 220 78, 200 86 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linejoin="round" transform="rotate(0 200 118)"/>`, `<path d="M200 86 C 180 78, 176 52, 200 40 C 224 52, 220 78, 200 86 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linejoin="round" transform="rotate(90 200 118)"/>`, `<path d="M200 86 C 180 78, 176 52, 200 40 C 224 52, 220 78, 200 86 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linejoin="round" transform="rotate(180 200 118)"/>`, `<path d="M200 86 C 180 78, 176 52, 200 40 C 224 52, 220 78, 200 86 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linejoin="round" transform="rotate(270 200 118)"/>`] },
|
||||
{ n: 3, title: "More petals", instruction: "Tuck four more petals into the gaps.", lines: [`<path d="M200 86 C 180 78, 176 52, 200 40 C 224 52, 220 78, 200 86 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linejoin="round" transform="rotate(45 200 118)"/>`, `<path d="M200 86 C 180 78, 176 52, 200 40 C 224 52, 220 78, 200 86 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linejoin="round" transform="rotate(135 200 118)"/>`, `<path d="M200 86 C 180 78, 176 52, 200 40 C 224 52, 220 78, 200 86 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linejoin="round" transform="rotate(225 200 118)"/>`, `<path d="M200 86 C 180 78, 176 52, 200 40 C 224 52, 220 78, 200 86 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linejoin="round" transform="rotate(315 200 118)"/>`] },
|
||||
{ n: 4, title: "The stem", instruction: "Draw a stem going down from the flower.", lines: [`<path d="M195 152 C 191 186, 205 214, 197 256 L203 256 C 211 214, 199 186, 205 152 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n: 5, title: "The leaves", instruction: "Add a leaf on each side of the stem.", lines: [`<path d="M200 198 C 168 184, 138 192, 150 218 C 162 240, 196 226, 200 198 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M200 226 C 232 214, 262 222, 250 246 C 238 266, 204 252, 200 226 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const FLOWER_DETAIL_STEPS: LessonStep[] = [
|
||||
{ n: 1, title: "Seeds", instruction: "Dot some seeds in the middle.", lines: [`<circle cx="192" cy="112" r="4" fill="#2b2440"/>`, `<circle cx="208" cy="114" r="4" fill="#2b2440"/>`, `<circle cx="200" cy="124" r="4" fill="#2b2440"/>`, `<circle cx="190" cy="126" r="3" fill="#2b2440"/>`, `<circle cx="210" cy="126" r="3" fill="#2b2440"/>`] },
|
||||
{ n: 2, title: "Leaf veins", instruction: "Add a line down each leaf.", lines: [`<path d="M170 210 q20 4 28 14" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M232 240 q-18 2 -28 -10" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n: 3, title: "Center ring", instruction: "Draw a little ring inside the middle.", lines: [`<circle cx="200" cy="118" r="18" fill="none" stroke="#2b2440" stroke-width="3"/>`] },
|
||||
];
|
||||
|
||||
const FLOWER_OUTLINE_SVG = FLOWER_OUTLINE_STEPS.flatMap((s) => s.lines || []).join("\n");
|
||||
|
||||
const FLOWER_DETAIL_SVG = FLOWER_OUTLINE_SVG + "\n" + FLOWER_DETAIL_STEPS.flatMap((s) => s.lines || []).join("\n");
|
||||
|
||||
export const FLOWER_OUTLINE_LESSON: Lesson = { level:"early-beginner", sublevel:31, slug:"flower-outline", title:"Flower \u00b7 Outline", subject:"flower outline", emoji:"🌸", intro:"Let\u2019s draw Flower\u2019s outline, one line at a time. Watch each line, then trace it!", badgeKey:"", badgeName:"Flower Power", phase:"outline", subjectKey:"flower", subjectName:"Flower", subjectEmoji:"🌸", order:3, steps: FLOWER_OUTLINE_STEPS };
|
||||
|
||||
export const FLOWER_DETAIL_LESSON: Lesson = { level:"early-beginner", sublevel:32, slug:"flower-detail", title:"Flower \u00b7 Details", subject:"flower details", emoji:"🌸", intro:"Now add the details that bring your flower to life.", badgeKey:"early-beginner-3-flower", badgeName:"Flower Power", phase:"detail", baseSvg: FLOWER_OUTLINE_SVG, subjectKey:"flower", subjectName:"Flower", subjectEmoji:"🌸", order:3, steps: FLOWER_DETAIL_STEPS };
|
||||
|
||||
export const FLOWER_EXTRA_LESSON: Lesson = { level:"early-beginner", sublevel:33, slug:"flower-extra", title:"Flower \u00b7 Color it!", subject:"color the flower", emoji:"\u{1F3A8}", intro:"Bring your flower to life! Color it in, and try shading and layering.", badgeKey:"", badgeName:"Flower Power", phase:"extra", baseSvg: FLOWER_DETAIL_SVG, subjectKey:"flower", subjectName:"Flower", subjectEmoji:"🌸", order:3, steps: [] };
|
||||
|
||||
const UNICORN_OUTLINE_STEPS: LessonStep[] = [
|
||||
{ n: 1, title: "The head", instruction: "Draw a rounded head.", lines: [`<path d="M120 152 C 120 96, 165 72, 200 72 C 235 72, 280 96, 280 152 C 280 202, 246 236, 200 240 C 154 236, 120 202, 120 152 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n: 2, title: "The ears", instruction: "Add a pointy ear on each side.", lines: [`<path d="M150 98 C 138 72, 132 58, 150 60 C 166 64, 174 84, 180 96 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M250 98 C 262 72, 268 58, 250 60 C 234 64, 226 84, 220 96 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n: 3, title: "The horn", instruction: "Draw a tall horn on top.", lines: [`<path d="M200 80 L188 18 L212 80 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n: 4, title: "The mane", instruction: "Draw the flowing mane and a curl on the forehead.", lines: [`<path d="M256 112 C 320 122, 320 200, 268 250 C 292 196, 280 150, 250 142 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M176 92 C 165 110, 175 135, 196 130 C 184 118, 188 102, 200 96 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const UNICORN_DETAIL_STEPS: LessonStep[] = [
|
||||
{ n: 1, title: "The eyes", instruction: "Add two big eyes.", lines: [`<ellipse cx="172" cy="158" rx="7" ry="10" fill="none" stroke="#2b2440" stroke-width="4"/>`, `<ellipse cx="228" cy="158" rx="7" ry="10" fill="none" stroke="#2b2440" stroke-width="4"/>`] },
|
||||
{ n: 2, title: "Nose & mouth", instruction: "Add two nostril dots and a smile.", lines: [`<circle cx="188" cy="210" r="3" fill="#2b2440"/>`, `<circle cx="214" cy="210" r="3" fill="#2b2440"/>`, `<path d="M186 224 q14 12 28 0" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n: 3, title: "Horn swirls", instruction: "Add little swirl lines on the horn.", lines: [`<path d="M193 66 l14 -5" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M196 50 l11 -4" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M199 36 l8 -3" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n: 4, title: "Mane lines", instruction: "Add flowing lines in the mane.", lines: [`<path d="M268 130 C 300 150, 296 200, 262 232" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M258 140 C 286 160, 282 200, 256 226" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const UNICORN_OUTLINE_SVG = UNICORN_OUTLINE_STEPS.flatMap((s) => s.lines || []).join("\n");
|
||||
|
||||
const UNICORN_DETAIL_SVG = UNICORN_OUTLINE_SVG + "\n" + UNICORN_DETAIL_STEPS.flatMap((s) => s.lines || []).join("\n");
|
||||
|
||||
export const UNICORN_OUTLINE_LESSON: Lesson = { level:"early-beginner", sublevel:41, slug:"unicorn-outline", title:"Unicorn \u00b7 Outline", subject:"unicorn outline", emoji:"🦄", intro:"Let\u2019s draw Unicorn\u2019s outline, one line at a time. Watch each line, then trace it!", badgeKey:"", badgeName:"Unicorn Magic", phase:"outline", subjectKey:"unicorn", subjectName:"Unicorn", subjectEmoji:"🦄", order:4, steps: UNICORN_OUTLINE_STEPS };
|
||||
|
||||
export const UNICORN_DETAIL_LESSON: Lesson = { level:"early-beginner", sublevel:42, slug:"unicorn-detail", title:"Unicorn \u00b7 Details", subject:"unicorn details", emoji:"🦄", intro:"Now add the details that bring your unicorn to life.", badgeKey:"early-beginner-4-unicorn", badgeName:"Unicorn Magic", phase:"detail", baseSvg: UNICORN_OUTLINE_SVG, subjectKey:"unicorn", subjectName:"Unicorn", subjectEmoji:"🦄", order:4, steps: UNICORN_DETAIL_STEPS };
|
||||
|
||||
export const UNICORN_EXTRA_LESSON: Lesson = { level:"early-beginner", sublevel:43, slug:"unicorn-extra", title:"Unicorn \u00b7 Color it!", subject:"color the unicorn", emoji:"\u{1F3A8}", intro:"Bring your unicorn to life! Color it in, and try shading and layering.", badgeKey:"", badgeName:"Unicorn Magic", phase:"extra", baseSvg: UNICORN_DETAIL_SVG, subjectKey:"unicorn", subjectName:"Unicorn", subjectEmoji:"🦄", order:4, steps: [] };
|
||||
|
||||
const TREE_OUTLINE_STEPS: LessonStep[] = [
|
||||
{ n: 1, title: "The trunk", instruction: "Draw a tall trunk, wider at the bottom.", lines: [`<path d="M178 262 C 176 222, 184 200, 190 176 L210 176 C 216 200, 224 222, 222 262 C 214 266, 186 266, 178 262 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n: 2, title: "Branches", instruction: "Add two branches reaching up.", lines: [`<path d="M198 180 C 190 152, 168 144, 152 134" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M204 180 C 214 152, 236 146, 252 136" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n: 3, title: "The treetop", instruction: "Draw a big bumpy treetop.", tip: "Nice and lumpy!", lines: [`<path d="M120 150 C 100 122, 122 96, 150 100 C 152 74, 200 62, 216 88 C 246 72, 286 92, 274 122 C 300 134, 292 168, 262 166 C 250 188, 150 188, 138 166 C 110 170, 104 152, 120 150 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const TREE_DETAIL_STEPS: LessonStep[] = [
|
||||
{ n: 1, title: "Apples", instruction: "Add a few round apples in the leaves.", lines: [`<circle cx="168" cy="118" r="7" fill="none" stroke="#2b2440" stroke-width="3"/>`, `<circle cx="228" cy="104" r="7" fill="none" stroke="#2b2440" stroke-width="3"/>`, `<circle cx="204" cy="150" r="7" fill="none" stroke="#2b2440" stroke-width="3"/>`] },
|
||||
{ n: 2, title: "Grass", instruction: "Add wavy grass along the bottom.", lines: [`<path d="M120 262 q14 -16 28 0 q14 -16 28 0 q14 -16 28 0 q14 -16 28 0 q14 -16 28 0" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n: 3, title: "Bark", instruction: "Add a couple of bark lines on the trunk.", lines: [`<path d="M194 200 q4 24 0 56" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M206 200 q-3 24 0 54" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const TREE_OUTLINE_SVG = TREE_OUTLINE_STEPS.flatMap((s) => s.lines || []).join("\n");
|
||||
|
||||
const TREE_DETAIL_SVG = TREE_OUTLINE_SVG + "\n" + TREE_DETAIL_STEPS.flatMap((s) => s.lines || []).join("\n");
|
||||
|
||||
export const TREE_OUTLINE_LESSON: Lesson = { level:"early-beginner", sublevel:51, slug:"tree-outline", title:"Tree \u00b7 Outline", subject:"tree outline", emoji:"🌳", intro:"Let\u2019s draw Tree\u2019s outline, one line at a time. Watch each line, then trace it!", badgeKey:"", badgeName:"Tree Top", phase:"outline", subjectKey:"tree", subjectName:"Tree", subjectEmoji:"🌳", order:5, steps: TREE_OUTLINE_STEPS };
|
||||
|
||||
export const TREE_DETAIL_LESSON: Lesson = { level:"early-beginner", sublevel:52, slug:"tree-detail", title:"Tree \u00b7 Details", subject:"tree details", emoji:"🌳", intro:"Now add the details that bring your tree to life.", badgeKey:"early-beginner-5-tree", badgeName:"Tree Top", phase:"detail", baseSvg: TREE_OUTLINE_SVG, subjectKey:"tree", subjectName:"Tree", subjectEmoji:"🌳", order:5, steps: TREE_DETAIL_STEPS };
|
||||
|
||||
export const TREE_EXTRA_LESSON: Lesson = { level:"early-beginner", sublevel:53, slug:"tree-extra", title:"Tree \u00b7 Color it!", subject:"color the tree", emoji:"\u{1F3A8}", intro:"Bring your tree to life! Color it in, and try shading and layering.", badgeKey:"", badgeName:"Tree Top", phase:"extra", baseSvg: TREE_DETAIL_SVG, subjectKey:"tree", subjectName:"Tree", subjectEmoji:"🌳", order:5, steps: [] };
|
||||
|
||||
const DINO_OUTLINE_STEPS: LessonStep[] = [
|
||||
{ n: 1, title: "The body", instruction: "Draw a big round body.", lines: [`<path d="M118 200 C 118 166, 160 150, 210 150 C 270 150, 302 170, 302 200 C 302 230, 262 246, 205 246 C 155 246, 118 230, 118 200 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n: 2, title: "The neck", instruction: "Sweep a long neck up from the left.", lines: [`<path d="M150 168 C 122 122, 112 80, 124 55 C 130 44, 148 46, 152 58 C 146 86, 162 124, 180 160 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n: 3, title: "The head", instruction: "Add a small head on top of the neck.", lines: [`<path d="M100 46 C 100 32, 120 28, 138 34 C 152 39, 154 56, 142 64 C 126 72, 104 64, 98 54 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n: 4, title: "The tail", instruction: "Draw a long tail swooping to the right.", lines: [`<path d="M298 198 C 338 184, 372 176, 398 168 C 380 192, 360 206, 304 220 C 300 213, 298 206, 298 198 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n: 5, title: "The legs", instruction: "Add two sturdy legs underneath.", lines: [`<path d="M168 242 C 165 264, 166 278, 180 280 C 193 280, 192 264, 190 242 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M232 244 C 229 266, 230 280, 244 282 C 257 282, 256 266, 254 244 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const DINO_DETAIL_STEPS: LessonStep[] = [
|
||||
{ n: 1, title: "Back plates", instruction: "Add triangle plates along the back.", lines: [`<path d="M150 150 l10 -16 l12 16 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M186 146 l11 -17 l12 17 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M224 147 l11 -16 l12 16 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M262 152 l10 -15 l12 16 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
{ n: 2, title: "Spots", instruction: "Add a few spots on the body.", lines: [`<circle cx="205" cy="188" r="9" fill="none" stroke="#2b2440" stroke-width="3"/>`, `<circle cx="240" cy="200" r="7" fill="none" stroke="#2b2440" stroke-width="3"/>`, `<circle cx="180" cy="212" r="6" fill="none" stroke="#2b2440" stroke-width="3"/>`] },
|
||||
{ n: 3, title: "The face", instruction: "Add an eye and a friendly smile.", lines: [`<circle cx="118" cy="46" r="4" fill="#2b2440"/>`, `<path d="M108 56 q12 10 24 3" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
|
||||
];
|
||||
|
||||
const DINO_OUTLINE_SVG = DINO_OUTLINE_STEPS.flatMap((s) => s.lines || []).join("\n");
|
||||
|
||||
const DINO_DETAIL_SVG = DINO_OUTLINE_SVG + "\n" + DINO_DETAIL_STEPS.flatMap((s) => s.lines || []).join("\n");
|
||||
|
||||
export const DINO_OUTLINE_LESSON: Lesson = { level:"early-beginner", sublevel:61, slug:"dinosaur-outline", title:"Dinosaur \u00b7 Outline", subject:"dinosaur outline", emoji:"🦕", intro:"Let\u2019s draw Dinosaur\u2019s outline, one line at a time. Watch each line, then trace it!", badgeKey:"", badgeName:"Dino Master", phase:"outline", subjectKey:"dinosaur", subjectName:"Dinosaur", subjectEmoji:"🦕", order:6, steps: DINO_OUTLINE_STEPS };
|
||||
|
||||
export const DINO_DETAIL_LESSON: Lesson = { level:"early-beginner", sublevel:62, slug:"dinosaur-detail", title:"Dinosaur \u00b7 Details", subject:"dinosaur details", emoji:"🦕", intro:"Now add the details that bring your dinosaur to life.", badgeKey:"early-beginner-6-dinosaur", badgeName:"Dino Master", phase:"detail", baseSvg: DINO_OUTLINE_SVG, subjectKey:"dinosaur", subjectName:"Dinosaur", subjectEmoji:"🦕", order:6, steps: DINO_DETAIL_STEPS };
|
||||
|
||||
export const DINO_EXTRA_LESSON: Lesson = { level:"early-beginner", sublevel:63, slug:"dinosaur-extra", title:"Dinosaur \u00b7 Color it!", subject:"color the dinosaur", emoji:"\u{1F3A8}", intro:"Bring your dinosaur to life! Color it in, and try shading and layering.", badgeKey:"", badgeName:"Dino Master", phase:"extra", baseSvg: DINO_DETAIL_SVG, subjectKey:"dinosaur", subjectName:"Dinosaur", subjectEmoji:"🦕", order:6, steps: [] };
|
||||
|
||||
const SHADE_STEPS: LessonStep[] = [
|
||||
{ n: 1, kind: "shade", title: "How to hold your pencil", instruction: "Watch the demo above. Near the tip + press hard = DARK; high up + soft = LIGHT.", tip: "Try it in the air first!", svg: `<g><circle cx="62" cy="46" r="20" fill="#ffd45e"/><g stroke="#ffd45e" stroke-width="4" stroke-linecap="round"><path d="M62 14v-8M62 86v8M30 46h-8M94 46h8M39 23l-6-6M85 23l6-6M39 69l-6 6M85 69l6 6"/></g></g><line x1="40" y1="234" x2="370" y2="234" stroke="#c9c4d6" stroke-width="3"/><circle cx="210" cy="150" r="84" fill="#f1f1f4" stroke="#2b2440" stroke-width="4"/>` },
|
||||
{ n: 2, kind: "shade", title: "Find the light", instruction: "The sun is up-left. The side facing it stays brightest — that bright spot is the highlight.", svg: `<g><circle cx="62" cy="46" r="20" fill="#ffd45e"/><g stroke="#ffd45e" stroke-width="4" stroke-linecap="round"><path d="M62 14v-8M62 86v8M30 46h-8M94 46h8M39 23l-6-6M85 23l6-6M39 69l-6 6M85 69l6 6"/></g></g><line x1="40" y1="234" x2="370" y2="234" stroke="#c9c4d6" stroke-width="3"/><circle cx="210" cy="150" r="84" fill="#f1f1f4" stroke="#2b2440" stroke-width="4"/><circle cx="178" cy="118" r="22" fill="none" stroke="#4f7cff" stroke-width="3" stroke-dasharray="6 6"/>` },
|
||||
{ n: 3, kind: "shade", title: "Soft shade all over", instruction: "Pick a light tone and softly shade the whole ball — leave the bright spot white.", tip: "Hold high and go easy.", svg: `<g><circle cx="62" cy="46" r="20" fill="#ffd45e"/><g stroke="#ffd45e" stroke-width="4" stroke-linecap="round"><path d="M62 14v-8M62 86v8M30 46h-8M94 46h8M39 23l-6-6M85 23l6-6M39 69l-6 6M85 69l6 6"/></g></g><line x1="40" y1="234" x2="370" y2="234" stroke="#c9c4d6" stroke-width="3"/><circle cx="210" cy="150" r="84" fill="#f1f1f4" stroke="#2b2440" stroke-width="4"/><circle cx="178" cy="118" r="22" fill="none" stroke="#4f7cff" stroke-width="3" stroke-dasharray="6 6"/>` },
|
||||
{ n: 4, kind: "shade", title: "Build up the shadow", instruction: "Pick a darker tone and press harder on the side away from the sun. Build it up slowly.", tip: "More passes = darker.", svg: `<g><circle cx="62" cy="46" r="20" fill="#ffd45e"/><g stroke="#ffd45e" stroke-width="4" stroke-linecap="round"><path d="M62 14v-8M62 86v8M30 46h-8M94 46h8M39 23l-6-6M85 23l6-6M39 69l-6 6M85 69l6 6"/></g></g><line x1="40" y1="234" x2="370" y2="234" stroke="#c9c4d6" stroke-width="3"/><circle cx="210" cy="150" r="84" fill="#f1f1f4" stroke="#2b2440" stroke-width="4"/><path d="M250 96 A84 84 0 0 1 250 204 A60 70 0 0 0 250 96 Z" fill="#2b2440" opacity="0.18"/>` },
|
||||
{ n: 5, kind: "shade", title: "Cast shadow & finish", instruction: "Shade the ground under the ball on the shadow side. Keep the highlight clean!", tip: "🌗", svg: `<g><circle cx="62" cy="46" r="20" fill="#ffd45e"/><g stroke="#ffd45e" stroke-width="4" stroke-linecap="round"><path d="M62 14v-8M62 86v8M30 46h-8M94 46h8M39 23l-6-6M85 23l6-6M39 69l-6 6M85 69l6 6"/></g></g><line x1="40" y1="234" x2="370" y2="234" stroke="#c9c4d6" stroke-width="3"/><circle cx="210" cy="150" r="84" fill="#f1f1f4" stroke="#2b2440" stroke-width="4"/><path d="M250 96 A84 84 0 0 1 250 204 A60 70 0 0 0 250 96 Z" fill="#2b2440" opacity="0.18"/><ellipse cx="262" cy="236" rx="78" ry="15" fill="#2b2440" opacity="0.18"/>` },
|
||||
];
|
||||
|
||||
export const SHADE_BALL_LESSON: Lesson = { level:"learner", sublevel:1, slug:"shade-ball", title:"Light & Shadow: Shade a Ball", subject:"a shaded ball", emoji:"\u{1F317}", intro:"Make a flat circle look like a round ball using light and shadow!", badgeKey:"learner-1-shading", badgeName:"Shading Star", mode:"shade", subjectKey:"shade-ball", subjectName:"Shade a Ball", subjectEmoji:"\u{1F317}", order:1, steps: SHADE_STEPS };
|
||||
|
||||
import { BEGINNER_LESSONS, BEGINNER_PACKS, type Pack } from "./curriculum.beginner";
|
||||
import { EB2_LESSONS } from "./curriculum.eb2";
|
||||
import { ANIMAL_LESSONS } from "./curriculum.animals";
|
||||
export { BEGINNER_PACKS, type Pack } from "./curriculum.beginner";
|
||||
export { LIGHT_HINT_SVG } from "./curriculum.beginner";
|
||||
|
||||
const EARLY_BEGINNER_LESSONS: Lesson[] = [FISH_OUTLINE_LESSON, FISH_DETAIL_LESSON, FISH_EXTRA_LESSON, PANDA_OUTLINE_LESSON, PANDA_DETAIL_LESSON, PANDA_EXTRA_LESSON, FLOWER_OUTLINE_LESSON, FLOWER_DETAIL_LESSON, FLOWER_EXTRA_LESSON, UNICORN_OUTLINE_LESSON, UNICORN_DETAIL_LESSON, UNICORN_EXTRA_LESSON, TREE_OUTLINE_LESSON, TREE_DETAIL_LESSON, TREE_EXTRA_LESSON, DINO_OUTLINE_LESSON, DINO_DETAIL_LESSON, DINO_EXTRA_LESSON, ...EB2_LESSONS, ...ANIMAL_LESSONS];
|
||||
export const LESSONS: Lesson[] = [...EARLY_BEGINNER_LESSONS, ...BEGINNER_LESSONS, SHADE_BALL_LESSON];
|
||||
|
||||
export function getLesson(level: string, sublevel: number): Lesson | undefined { return LESSONS.find((l) => l.level === level && l.sublevel === sublevel); }
|
||||
export function getLessonBySlug(slug: string): Lesson | undefined { return LESSONS.find((l) => l.slug === slug); }
|
||||
export function cumulativeSvg(lesson: Lesson, upTo: number): string { return lesson.steps.filter((s) => s.n <= upTo).map((s) => s.svg || "").join("\n"); }
|
||||
export function cumulativeLines(lesson: Lesson, upTo: number): string { return lesson.steps.filter((s) => s.n <= upTo).flatMap((s) => s.lines || []).join("\n"); }
|
||||
export function getSubjects(level: string): { key: string; name: string; emoji: string; order: number; lessons: Lesson[] }[] {
|
||||
const inLevel = LESSONS.filter((l) => l.level === level);
|
||||
const map = new Map<string, { key: string; name: string; emoji: string; order: number; lessons: Lesson[] }>();
|
||||
for (const l of inLevel) {
|
||||
const key = l.subjectKey || l.slug;
|
||||
if (!map.has(key)) map.set(key, { key, name: l.subjectName || l.title, emoji: l.subjectEmoji || l.emoji, order: l.order ?? l.sublevel, lessons: [] });
|
||||
map.get(key)!.lessons.push(l);
|
||||
}
|
||||
// Covers both Early Beginner (outline/detail/extra) and Beginner (construct/outline/color/light).
|
||||
const rank: Record<string, number> = { construct: 0, outline: 1, detail: 2, color: 3, extra: 3, light: 4 };
|
||||
for (const s of map.values()) s.lessons.sort((a, b) => (rank[a.phase ?? ""] ?? 0) - (rank[b.phase ?? ""] ?? 0));
|
||||
return [...map.values()].sort((a, b) => a.order - b.order);
|
||||
}
|
||||
|
||||
/** Themed groups for browsing a level's subjects (Early Beginner). Keyed by subjectKey. */
|
||||
export interface SubjectGroup { key: string; name: string; emoji: string; subjectKeys: string[]; }
|
||||
export const EARLY_BEGINNER_GROUPS: SubjectGroup[] = [
|
||||
{ key: "shapes", name: "Simple Shapes", emoji: "⭐", subjectKeys: ["square", "triangle", "heart"] },
|
||||
{ key: "classics", name: "Animals & Nature", emoji: "\u{1F41F}", subjectKeys: ["fish", "panda", "flower", "unicorn", "tree", "dinosaur"] },
|
||||
{ key: "safari", name: "Safari Animals", emoji: "\u{1F981}", subjectKeys: ["lion", "elephant", "giraffe", "zebra", "rhino", "leopard", "tiger", "monkey", "crocodile"] },
|
||||
{ key: "woodland", name: "Woodland Critters", emoji: "\u{1F98A}", subjectKeys: ["fox", "bear", "deer", "rabbit", "hedgehog", "owl", "cat", "turtle"] },
|
||||
{ key: "air", name: "Up in the Air", emoji: "\u{1F98B}", subjectKeys: ["bird", "butterfly"] },
|
||||
{ key: "fun", name: "Fun Things", emoji: "\u{1F380}", subjectKeys: ["bow-tie", "football", "basketball", "cup", "trophy"] },
|
||||
{ key: "wear", name: "Things to Wear", emoji: "\u{1F455}", subjectKeys: ["shirt", "dress", "socks"] },
|
||||
{ key: "faces", name: "Faces & Features", emoji: "\u{1F642}", subjectKeys: ["eyes", "face"] },
|
||||
];
|
||||
|
||||
export type GroupedSubjects = { key: string; name: string; emoji: string; subjects: ReturnType<typeof getSubjects> }[];
|
||||
/** Bucket a level's subjects into ordered themed groups; anything unlisted falls into a "More" group. */
|
||||
export function getGroupedSubjects(level: string): GroupedSubjects {
|
||||
const subjects = getSubjects(level);
|
||||
const byKey = new Map(subjects.map((s) => [s.key, s]));
|
||||
const used = new Set<string>();
|
||||
const cfg = level === "early-beginner" ? EARLY_BEGINNER_GROUPS : [];
|
||||
const groups: GroupedSubjects = cfg
|
||||
.map((g) => {
|
||||
const subs = g.subjectKeys.map((k) => byKey.get(k)).filter(Boolean) as ReturnType<typeof getSubjects>;
|
||||
subs.forEach((s) => used.add(s.key));
|
||||
return { key: g.key, name: g.name, emoji: g.emoji, subjects: subs };
|
||||
})
|
||||
.filter((g) => g.subjects.length > 0);
|
||||
const rest = subjects.filter((s) => !used.has(s.key));
|
||||
if (rest.length) groups.push({ key: "more", name: "More", emoji: "✨", subjects: rest });
|
||||
return groups;
|
||||
}
|
||||
|
||||
/** All badge definitions: per-subject (Early Beginner) + per-pack (Beginner). */
|
||||
export interface BadgeDef { badgeKey: string; name: string; emoji: string; groupKey: string; }
|
||||
export function badgeDefs(): BadgeDef[] {
|
||||
const fromLessons = LESSONS.filter((l) => l.badgeKey).map((l) => ({ badgeKey: l.badgeKey, name: l.badgeName, emoji: l.emoji, groupKey: `${l.level}-${l.sublevel}` }));
|
||||
const fromPacks = BEGINNER_PACKS.map((p) => ({ badgeKey: p.badgeKey, name: p.badgeName, emoji: p.emoji, groupKey: "" }));
|
||||
return [...fromLessons, ...fromPacks];
|
||||
}
|
||||
export function getPackByBadge(badgeKey: string): Pack | undefined { return BEGINNER_PACKS.find((p) => p.badgeKey === badgeKey); }
|
||||
/** The pack a lesson belongs to (Beginner only), via its packKey. */
|
||||
export function getPackForLesson(lesson: Lesson): Pack | undefined { return lesson.packKey ? BEGINNER_PACKS.find((p) => p.key === lesson.packKey) : undefined; }
|
||||
/** Every lesson that belongs to a pack (all phases of all its subjects). */
|
||||
export function lessonsInPack(pack: Pack): Lesson[] { return LESSONS.filter((l) => l.level === pack.level && l.subjectKey && pack.subjectKeys.includes(l.subjectKey)); }
|
||||
/** Number of completed steps needed to count a lesson as done. */
|
||||
export function lessonStepCount(l: Lesson): number { return l.steps.length || 1; }
|
||||
|
||||
Reference in New Issue
Block a user