clitutor
Version:
Interactive CLI learning tool for beginners
1,077 lines (980 loc) โข 32.3 kB
JavaScript
const inquirer = require("inquirer");
const chalk = require("chalk");
const boxen = require("boxen");
const ora = require("ora");
const {
clearScreen,
sleep,
printWithTypingEffect,
validateCommand,
showHint,
} = require("../utils/helpers");
const green = chalk.hex("#00FF00");
const dim = chalk.dim;
const bold = chalk.bold;
const yellow = chalk.yellow;
const cyan = chalk.cyan;
const magenta = chalk.magenta;
const lessons = [
{
id: "intro",
title: "Welcome to Terminal Navigation!",
content: async () => {
clearScreen();
console.log(
boxen(green("Chapter 2: Mastering Terminal Navigation"), {
padding: 1,
borderStyle: "double",
borderColor: "green",
})
);
await printWithTypingEffect(
"\nIn Chapter 1, you learned how to build commands. Now let's learn how to navigate the terminal like a pro!"
);
await sleep(500);
await printWithTypingEffect(
"\nThink of the terminal as a text document where you can't use your mouse to click around."
);
await sleep(500);
await printWithTypingEffect(
"\nInstead, you'll use keyboard shortcuts - it's like having superpowers at your fingertips! โก"
);
await sleep(1000);
console.log(
boxen(
"๐ป " + bold("Terminal says:") + "\n\n" +
"\"I know it feels weird not using your mouse, but trust me!\n" +
"Once you learn these shortcuts, you'll be FLYING through\n" +
"commands faster than you ever could with clicking.\n\n" +
"It's like learning to touch-type - awkward at first,\n" +
"but soon it becomes second nature! ๐\"",
{
padding: 1,
margin: { top: 1 },
borderColor: "cyan",
borderStyle: "round",
}
)
);
await sleep(1000);
await printWithTypingEffect(
"\nLet's start with the most important skill: moving your cursor!"
);
},
},
{
id: "cursor_basics",
title: "The Cursor Challenge",
content: async () => {
clearScreen();
console.log(
boxen(green("Why Can't I Click?"), {
padding: 1,
borderStyle: "round",
borderColor: "green",
})
);
await printWithTypingEffect(
"\nHere's something that surprises many beginners:"
);
await sleep(700);
await printWithTypingEffect(
"\n" + bold("You can't click to move your cursor in the terminal!") + " ๐ฑ๏ธโ"
);
await sleep(1000);
console.log(
boxen(
bold("The Terminal vs Text Editor:\n\n") +
"๐ Text Editor: Click anywhere to move cursor\n" +
"๐ป Terminal: Use keyboard shortcuts only\n\n" +
dim("Why? The terminal was designed before mice were common!\n" +
"It's optimized for keyboard-only control."),
{
padding: 1,
margin: { top: 1, bottom: 1 },
borderColor: "yellow",
}
)
);
await sleep(1000);
await printWithTypingEffect(
"\nLet's see why this matters with a real example:"
);
await sleep(700);
console.log(
boxen(
"Imagine you typed this long command:\n\n" +
green("git commit -m") + " " + bold('"Fix login bug that prevented users from accessing dashboard"') + "\n\n" +
"But you realize you forgot to add a file first! ๐ฑ\n\n" +
"You need to go back to the beginning to change " + green("commit") + " to " + green("add") + ".\n" +
"Without shortcuts, you'd have to hold โ for ages!",
{
padding: 1,
margin: { top: 1 },
borderColor: "gray",
}
)
);
await sleep(1000);
await printWithTypingEffect(
"\nThat's where cursor shortcuts save the day! Let's learn them..."
);
},
},
{
id: "cursor_movement",
title: "Cursor Movement Shortcuts",
content: async () => {
clearScreen();
console.log(
boxen(green("Moving Like a Ninja ๐ฅท"), {
padding: 1,
borderStyle: "round",
borderColor: "green",
})
);
await printWithTypingEffect(
"\nThink of these shortcuts as " + bold("teleportation") + " for your cursor!"
);
await sleep(700);
console.log(
boxen(
bold("Essential Cursor Movements:\n\n") +
cyan("Ctrl + A") + " โ Jump to " + bold("A") + "ll the way to the beginning\n" +
cyan("Ctrl + E") + " โ Jump to the " + bold("E") + "nd of the line\n\n" +
dim("Memory tip: A = start of Alphabet, E = End"),
{
padding: 1,
margin: { top: 1, bottom: 1 },
borderColor: "cyan",
}
)
);
await sleep(1000);
// Visual demonstration
console.log(
boxen(
bold("Visual Demo:\n\n") +
"Command: git commit -m \"Fix bug\"\n" +
" โ โ\n" +
" " + cyan("Ctrl+A") + " " + cyan("Ctrl+E") + "\n\n" +
"Your cursor can jump instantly between these points!",
{
padding: 1,
borderColor: "gray",
}
)
);
await sleep(1000);
await printWithTypingEffect(
"\nBut what if you need more precision? Let's see..."
);
await sleep(700);
console.log(
boxen(
bold("Character by Character:\n\n") +
cyan("Ctrl + F") + " โ Move " + bold("F") + "orward one character (โ)\n" +
cyan("Ctrl + B") + " โ Move " + bold("B") + "ackward one character (โ)\n\n" +
bold("Word by Word (Power Mode!):\n\n") +
magenta("Alt + F") + " โ Jump " + bold("F") + "orward by one word\n" +
magenta("Alt + B") + " โ Jump " + bold("B") + "ackward by one word\n\n" +
dim("Word jumping is perfect for long commands!"),
{
padding: 1,
margin: { top: 1 },
borderColor: "magenta",
}
)
);
await sleep(1000);
// Visual word jumping demo
console.log(
boxen(
bold("Word Jumping Demo:\n\n") +
"git add --all ./src/components/Header.js\n" +
"โ โ โ โ โ โ\n" +
dim("Each โ is where Alt+F would land"),
{
padding: 1,
margin: { top: 1 },
borderColor: "gray",
}
)
);
await sleep(1000);
console.log(
boxen(
"๐ป " + bold("Terminal says:") + "\n\n" +
"\"Think of it like this:\n" +
"โข Ctrl shortcuts = Precise surgical tools ๐ฌ\n" +
"โข Alt shortcuts = Jumping boots ๐ฆ\n\n" +
"I use Ctrl+A and Ctrl+E hundreds of times a day!\n" +
"They're the bread and butter of terminal navigation.\"",
{
padding: 1,
margin: { top: 1 },
borderColor: "cyan",
borderStyle: "round",
}
)
);
},
},
{
id: "deleting_text",
title: "Deleting Text Like a Pro",
content: async () => {
clearScreen();
console.log(
boxen(green("The Art of Deletion ๐ฏ"), {
padding: 1,
borderStyle: "round",
borderColor: "green",
})
);
await printWithTypingEffect(
"\nMade a typo? Need to clear things out? These shortcuts are your erasers!"
);
await sleep(700);
console.log(
boxen(
bold("The Nuclear Options (Delete Lots!):\n\n") +
cyan("Ctrl + U") + " โ " + bold("U") + "ndo everything before cursor\n" +
cyan("Ctrl + K") + " โ " + bold("K") + "ill everything after cursor\n\n" +
dim("These are perfect for starting over or cleaning up!"),
{
padding: 1,
margin: { top: 1, bottom: 1 },
borderColor: "cyan",
}
)
);
await sleep(1000);
// Visual demonstration of Ctrl+U and Ctrl+K
console.log(
boxen(
bold("Visual Example:\n\n") +
"Original: git commit -m \"Fix login bug\"\n" +
" โ (cursor here)\n\n" +
cyan("Ctrl + U") + " result: \"Fix login bug\"\n" +
dim("(Deleted everything before cursor)\n\n") +
cyan("Ctrl + K") + " result: git commit -m \n" +
dim("(Deleted everything after cursor)"),
{
padding: 1,
borderColor: "gray",
}
)
);
await sleep(1000);
console.log(
boxen(
bold("Precision Deletion:\n\n") +
cyan("Ctrl + W") + " โ Delete the " + bold("W") + "ord before cursor\n" +
cyan("Backspace") + " โ Delete one character before cursor\n\n" +
"Example: git comit -m \"Fix\"\n" +
" โ (cursor here)\n" +
cyan("Ctrl + W") + " โ Deletes 'comit'\n" +
dim("Perfect for fixing typos in command names!"),
{
padding: 1,
margin: { top: 1 },
borderColor: "yellow",
}
)
);
await sleep(1000);
await printWithTypingEffect(
"\n๐ก Pro tip: Combine movement and deletion for maximum efficiency!"
);
await sleep(700);
console.log(
boxen(
bold("Power Combo Example:\n") +
"1. " + cyan("Ctrl + A") + " (jump to start)\n" +
"2. " + cyan("Ctrl + K") + " (delete everything)\n" +
"= Instant command clear! ๐ฏ",
{
padding: 1,
margin: { top: 1 },
borderColor: "green",
}
)
);
},
},
{
id: "autocompletion",
title: "Tab: Your Best Friend",
content: async () => {
clearScreen();
console.log(
boxen(green("The Magic of Tab Completion โจ"), {
padding: 1,
borderStyle: "round",
borderColor: "green",
})
);
await printWithTypingEffect(
"\nHere's the shortcut that will save you " + bold("TONS") + " of typing..."
);
await sleep(700);
await printWithTypingEffect(
"\n\nThe " + cyan("Tab") + " key is like autocomplete for the terminal!"
);
await sleep(1000);
console.log(
boxen(
bold("How Tab Works:\n\n") +
"1. Start typing a file or folder name\n" +
"2. Press " + cyan("Tab") + "\n" +
"3. Terminal completes it for you!\n\n" +
dim("It's like the terminal is reading your mind! ๐ฎ"),
{
padding: 1,
margin: { top: 1, bottom: 1 },
borderColor: "cyan",
}
)
);
await sleep(1000);
console.log(
boxen(
bold("Real Example:\n\n") +
"You want to open: " + bold("Documents/ProjectReports/2024/\n\n") +
"Without Tab: " + dim("cd Documents/ProjectReports/2024/") + "\n" +
"With Tab: " + green("cd Doc") + cyan("[Tab]") + green("/Proj") + cyan("[Tab]") + green("/20") + cyan("[Tab]") + "\n\n" +
"You only typed 11 characters instead of 32! ๐",
{
padding: 1,
borderColor: "yellow",
}
)
);
await sleep(1000);
console.log(
boxen(
bold("Multiple Matches?\n\n") +
"If multiple files start the same way:\n" +
"โข Press " + cyan("Tab") + " once = Nothing happens\n" +
"โข Press " + cyan("Tab") + " twice = See all options!\n\n" +
"Example: " + green("cd D") + cyan("[Tab][Tab]") + "\n" +
"Shows: Desktop/ Documents/ Downloads/",
{
padding: 1,
margin: { top: 1 },
borderColor: "gray",
}
)
);
await sleep(1000);
console.log(
boxen(
"๐ป " + bold("Terminal says:") + "\n\n" +
"\"Tab completion is like having a helpful assistant!\n\n" +
"I LOVE when people use Tab - it means:\n" +
"โข Fewer typos for me to deal with ๐
\n" +
"โข Faster commands\n" +
"โข Less frustration for everyone!\n\n" +
"Seriously, Tab is your new best friend. Use it!\"",
{
padding: 1,
margin: { top: 1 },
borderColor: "cyan",
borderStyle: "round",
}
)
);
},
},
{
id: "screen_management",
title: "Keeping Things Clean",
content: async () => {
clearScreen();
console.log(
boxen(green("Screen Management ๐งน"), {
padding: 1,
borderStyle: "round",
borderColor: "green",
})
);
await printWithTypingEffect(
"\nAfter running many commands, your screen can get cluttered..."
);
await sleep(700);
await printWithTypingEffect(
"\nLet's learn how to keep things tidy!"
);
await sleep(1000);
console.log(
boxen(
bold("Clear the Screen:\n\n") +
cyan("Ctrl + L") + " โ " + bold("L") + "ightning clear! โก\n" +
green("clear") + " โ Type this command\n\n" +
"Both do the same thing, but " + cyan("Ctrl+L") + " is faster!\n\n" +
dim("Note: This doesn't delete history, just cleans the view"),
{
padding: 1,
margin: { top: 1, bottom: 1 },
borderColor: "cyan",
}
)
);
await sleep(1000);
console.log(
boxen(
bold("Visual Before & After:\n\n") +
"BEFORE (Messy screen):\n" +
dim("$ ls -la\n" +
"total 64\n" +
"drwxr-xr-x 10 user staff 320 Jan 15 10:30 .\n" +
"$ git status\n" +
"On branch main\n" +
"$ npm install\n" +
"added 234 packages...\n") +
"$ " + cyan("[Ctrl + L]") + "\n\n" +
"AFTER (Clean slate!):\n" +
"$ _",
{
padding: 1,
borderColor: "gray",
}
)
);
await sleep(1000);
await printWithTypingEffect(
"\n๐ฏ When to clear: Whenever you feel overwhelmed by too much text!"
);
},
},
{
id: "exiting",
title: "Making Your Exit",
content: async () => {
clearScreen();
console.log(
boxen(green("Exiting Gracefully ๐"), {
padding: 1,
borderStyle: "round",
borderColor: "green",
})
);
await printWithTypingEffect(
"\nAll good terminal sessions must come to an end..."
);
await sleep(700);
await printWithTypingEffect(
"\nLet's learn the proper way to say goodbye!"
);
await sleep(1000);
console.log(
boxen(
bold("Two Ways to Exit:\n\n") +
"1. " + green("exit") + " โ Type this command\n" +
"2. " + cyan("Ctrl + D") + " โ " + bold("D") + "one shortcut\n\n" +
"Both close your terminal session safely.\n\n" +
dim("Think of Ctrl+D as saying 'Done!' to the terminal"),
{
padding: 1,
margin: { top: 1, bottom: 1 },
borderColor: "cyan",
}
)
);
await sleep(1000);
console.log(
boxen(
yellow("โ ๏ธ Important Notes:\n\n") +
"โข Always exit properly - don't just close the window!\n" +
"โข Some programs need their own exit commands\n" +
"โข If stuck in a program, try:\n" +
" - " + cyan("Ctrl + C") + " to cancel/interrupt\n" +
" - " + cyan("Ctrl + D") + " to signal end-of-input\n" +
" - Type " + green("exit") + " or " + green("quit"),
{
padding: 1,
borderColor: "yellow",
}
)
);
await sleep(1000);
console.log(
boxen(
"๐ป " + bold("Terminal says:") + "\n\n" +
"\"Please exit properly! When you just close my window,\n" +
"it's like hanging up on someone mid-conversation. ๐\n\n" +
"A proper 'exit' or Ctrl+D lets me:\n" +
"โข Save your command history\n" +
"โข Clean up temporary files\n" +
"โข Say goodbye properly! ๐\"",
{
padding: 1,
margin: { top: 1 },
borderColor: "cyan",
borderStyle: "round",
}
)
);
},
},
{
id: "summary",
title: "Your Navigation Toolkit",
content: async () => {
clearScreen();
console.log(
boxen(green("Quick Reference Card ๐"), {
padding: 1,
borderStyle: "round",
borderColor: "green",
})
);
await printWithTypingEffect(
"\nHere's your complete navigation toolkit in one place:"
);
await sleep(700);
console.log(
boxen(
bold("๐ฏ Cursor Movement\n") +
cyan("Ctrl + A") + " / " + cyan("E") + " โ Beginning / End of line\n" +
cyan("Ctrl + F") + " / " + cyan("B") + " โ Forward / Backward (character)\n" +
magenta("Alt + F") + " / " + magenta("B") + " โ Forward / Backward (word)\n\n" +
bold("๐๏ธ Deletion\n") +
cyan("Ctrl + U") + " โ Delete to beginning\n" +
cyan("Ctrl + K") + " โ Delete to end\n" +
cyan("Ctrl + W") + " โ Delete previous word\n\n" +
bold("โจ Helpers\n") +
cyan("Tab") + " โ Autocomplete\n" +
cyan("Ctrl + L") + " โ Clear screen\n" +
cyan("Ctrl + D") + " โ Exit terminal",
{
padding: 1,
margin: { top: 1 },
borderColor: "yellow",
}
)
);
await sleep(1000);
await printWithTypingEffect(
"\n๐ก Practice tip: Try using these shortcuts in your next terminal session!"
);
await sleep(700);
await printWithTypingEffect(
"\nRemember: It might feel awkward at first, but soon you'll be navigating without even thinking about it!"
);
},
},
];
// Exercises for Chapter 2
const exercises = [
{
id: "cursor_jump",
type: "multiple_choice",
title: "๐ฏ Quick Navigation",
question:
"You typed: " + green("git commit -m") + " " + bold('"Fixed the login bug that was annoying everyone"') + "\n\n" +
"You realize you need to change 'commit' to 'add'. What's the FASTEST way to get your cursor to the word 'commit'?",
choices: [
"Hold the left arrow key until you get there",
cyan("Ctrl + A") + " to jump to the beginning of the line",
cyan("Alt + B") + " several times to jump back by words",
"Delete everything and start over",
],
answer: 1,
explanation:
cyan("Ctrl + A") + " instantly jumps to the beginning of the line - much faster than any other method! From there, you can use " + cyan("Alt + F") + " to jump forward to 'commit'.",
wrongAnswerHints: {
0: "This works but it's the slowest option - shortcuts are faster!",
2: "This could work but requires multiple presses - there's a quicker way!",
3: "That's the frustration talking! There's a much easier way.",
},
},
{
id: "word_deletion",
type: "multiple_choice",
title: "๐๏ธ Smart Deletion",
question:
"You accidentally typed: " + green("git commmmmit") + " -m \"Update\"\n" +
dim(" โ (cursor here)\n") +
"What's the best shortcut to fix the typo in 'commmmmit'?",
choices: [
"Backspace 9 times to delete each 'm'",
cyan("Ctrl + W") + " to delete the whole word",
cyan("Ctrl + U") + " to delete everything and start over",
cyan("Ctrl + K") + " to delete from cursor to end",
],
answer: 1,
explanation:
cyan("Ctrl + W") + " deletes the previous word - perfect for fixing typos in command names! After deleting, just retype 'commit' correctly.",
wrongAnswerHints: {
0: "This works but takes forever! There's a shortcut for this.",
2: "This deletes too much - you'd lose your whole command!",
3: "This deletes the wrong direction - it would keep the typo!",
},
},
{
id: "tab_completion",
type: "multiple_choice",
title: "โจ Tab Magic",
scenario:
"You have these folders:\n" +
"โข Documents/\n" +
"โข Downloads/\n" +
"โข Desktop/\n" +
"โข Development/",
question:
"You type: " + green("cd D") + " and press Tab. What happens?",
choices: [
"It completes to 'cd Documents/' automatically",
"Nothing happens because there are multiple matches",
"It shows an error message",
"It completes to the first alphabetical match",
],
answer: 1,
explanation:
"When multiple items start with the same letters, Tab won't complete anything. Press Tab TWICE to see all options that start with 'D'!",
wrongAnswerHints: {
0: "Tab doesn't guess - with multiple matches, it waits for more info!",
2: "Tab never shows errors - it just helps with completion!",
3: "Tab doesn't pick favorites - it needs a unique match!",
},
},
{
id: "screen_clear",
type: "multiple_choice",
title: "๐งน Clean Slate",
question:
"Your terminal is full of old command output and it's getting hard to read. What's the QUICKEST way to clear the screen?",
choices: [
"Type the " + green("clear") + " command",
"Scroll up until you can't see the old stuff",
cyan("Ctrl + L") + " for lightning-fast clear",
"Close and reopen the terminal",
],
answer: 2,
explanation:
cyan("Ctrl + L") + " instantly clears the screen - faster than typing 'clear'! It's one of the most-used shortcuts by terminal pros.",
wrongAnswerHints: {
0: "This works but requires typing - there's a faster shortcut!",
1: "The old stuff is still there, just hidden - not a real solution!",
3: "That's overkill! Plus you'd lose your command history.",
},
},
{
id: "navigation_practice",
type: "type_command",
title: "๐ Speed Challenge",
scenario:
"Let's practice cursor movement! I'll give you a command with a typo, and you tell me the TWO shortcuts you'd use to fix it efficiently.\n\n" +
"Command with typo: " + green("gti") + " add index.html\n" +
dim("(That should be 'git', not 'gti')\n\n") +
"Strategy: Jump to start, then delete the typo word.",
question: "Type the two shortcuts separated by a space:",
answer: "Ctrl+A Ctrl+W",
alternates: ["ctrl+a ctrl+w", "^A ^W", "Ctrl+a Ctrl+w"],
hints: [
"First shortcut: How do you jump to the beginning?",
"Second shortcut: How do you delete a word?",
"Think: Ctrl+? for beginning, Ctrl+? for word deletion",
],
explanation:
"Perfect! " + cyan("Ctrl+A") + " jumps to the start, then " + cyan("Ctrl+W") + " deletes the typo word 'gti'. After that, you'd just type 'git' correctly. This two-shortcut combo is incredibly useful for fixing command typos!",
showBuilder: true,
},
];
// Exercise runner functions (similar to chapter1.js)
async function runExercise(exercise, index, total) {
clearScreen();
const progress = `[${index + 1}/${total}]`;
console.log(
boxen(green(progress + " " + exercise.title), {
padding: 1,
borderStyle: "round",
borderColor: "green",
})
);
// Show navigation builder for the last exercise
if (exercise.showBuilder) {
console.log("\n" + bold("Let's visualize this fix:"));
await sleep(1000);
console.log(
boxen(
green("NAVIGATION STRATEGY ๐ฏ") + "\n\n" +
dim("โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ") + "\n\n" +
"Original: gti add index.html\n" +
" โ (cursor at end)\n\n" +
"Step 1: " + cyan("Ctrl+A") + " โ Jump to start\n" +
" โgti add index.html\n\n" +
"Step 2: " + cyan("Ctrl+W") + " โ Delete 'gti'\n" +
" โ add index.html\n\n" +
"Step 3: Type 'git'\n" +
" git add index.html โ",
{
padding: 1,
margin: { top: 1, bottom: 1 },
borderStyle: "bold",
borderColor: "yellow",
title: "๐ฏ Interactive Builder",
titleAlignment: "center",
}
)
);
await sleep(2000);
await printWithTypingEffect("\nNow show me those shortcuts!");
await sleep(1000);
}
if (exercise.scenario) {
console.log("\n" + bold("Scenario:"));
await printWithTypingEffect(exercise.scenario);
await sleep(1000);
}
if (exercise.hint) {
console.log("\n" + yellow("๐ก Hint: ") + dim(exercise.hint));
await sleep(1000);
}
if (exercise.type === "multiple_choice") {
return await runMultipleChoice(exercise);
} else if (exercise.type === "type_command") {
return await runTypeCommand(exercise);
}
}
async function runMultipleChoice(exercise) {
if (exercise.command) {
console.log("\n" + exercise.command);
}
console.log("\n" + exercise.question);
const { choice } = await inquirer.prompt([
{
type: "list",
name: "choice",
message: green(">"),
prefix: "",
choices: exercise.choices,
},
]);
const choiceIndex = exercise.choices.indexOf(choice);
const isCorrect = choiceIndex === exercise.answer;
if (isCorrect) {
const spinner = ora({
text: "Checking...",
color: "green",
}).start();
await sleep(800);
spinner.succeed(green("Correct! โจ"));
} else {
console.log(chalk.red("\nNot quite right."));
if (exercise.wrongAnswerHints && exercise.wrongAnswerHints[choiceIndex]) {
console.log(yellow("\n๐ก Hint: ") + dim(exercise.wrongAnswerHints[choiceIndex]));
}
}
if (exercise.explanation) {
console.log(dim("\n" + exercise.explanation));
}
await sleep(2000);
return isCorrect;
}
async function runTypeCommand(exercise) {
console.log("\n" + exercise.question);
let attempts = 0;
let correct = false;
while (!correct && attempts < 3) {
const { answer } = await inquirer.prompt([
{
type: "input",
name: "answer",
message: green(">"),
prefix: "",
validate: (input) => input.trim().length > 0 || "Please type the shortcuts",
},
]);
const normalizedAnswer = answer.trim().toLowerCase();
const normalizedCorrect = exercise.answer.toLowerCase();
const isCorrect =
normalizedAnswer === normalizedCorrect ||
(exercise.alternates && exercise.alternates.some(alt => normalizedAnswer === alt.toLowerCase()));
if (isCorrect) {
correct = true;
const spinner = ora({
text: "Checking...",
color: "green",
}).start();
await sleep(800);
spinner.succeed(green("Perfect! โจ"));
if (exercise.explanation) {
console.log(dim("\n" + exercise.explanation));
}
} else {
attempts++;
if (attempts < 3) {
console.log(chalk.red("Not quite. Try again!"));
if (exercise.hints && exercise.hints[attempts - 1]) {
showHint(exercise.hints[attempts - 1]);
}
}
}
}
if (!correct) {
console.log(
chalk.yellow(`\nThe correct answer was: ${green(exercise.answer)}`)
);
console.log(dim("Don't worry! These shortcuts take practice. ๐ช"));
}
await sleep(2000);
return correct;
}
async function start() {
clearScreen();
// Run through lessons
for (const lesson of lessons) {
await lesson.content();
console.log(); // Add spacing before the prompt
const { ready } = await inquirer.prompt([
{
type: "confirm",
name: "ready",
message: "Ready to continue?",
default: true,
},
]);
if (!ready) {
return;
}
}
// Practice exercises intro
clearScreen();
console.log(
boxen(
green("Time to Practice! ๐๏ธ") +
"\n\n" +
"Let's test your navigation skills with " +
bold("5 quick exercises") +
"\n\n" +
dim("Remember: These shortcuts will feel natural with practice!"),
{
padding: 1,
borderStyle: "double",
borderColor: "green",
}
)
);
await sleep(2000);
// Quick shortcut review
clearScreen();
console.log(
boxen(green("๐ฏ Last-Minute Review"), {
padding: 1,
borderStyle: "round",
borderColor: "green",
})
);
await printWithTypingEffect(
"\nRemember these power shortcuts:"
);
await sleep(700);
console.log(
boxen(
bold("Your Navigation Arsenal:\n\n") +
"๐ฏ " + cyan("Ctrl + A/E") + " = Beginning/End teleport\n" +
"๐ฆ " + magenta("Alt + F/B") + " = Word jumping\n" +
"๐๏ธ " + cyan("Ctrl + W") + " = Delete word\n" +
"โจ " + cyan("Tab") + " = Autocomplete magic\n" +
"๐งน " + cyan("Ctrl + L") + " = Clean screen\n\n" +
dim("You've got this! ๐ช"),
{
padding: 1,
margin: { top: 1, bottom: 1 },
borderColor: "yellow",
}
)
);
await sleep(1500);
const { ready } = await inquirer.prompt([
{
type: "confirm",
name: "ready",
message: "Ready to test your skills?",
default: true,
},
]);
if (!ready) {
return;
}
// Run exercises
let score = 0;
const results = [];
for (let i = 0; i < exercises.length; i++) {
const result = await runExercise(exercises[i], i, exercises.length);
results.push({
exercise: exercises[i].title,
passed: result,
});
if (result) score++;
if (i < exercises.length - 1) {
const { ready } = await inquirer.prompt([
{
type: "confirm",
name: "ready",
message: "Ready for the next one?",
default: true,
},
]);
if (!ready) break;
}
}
// Chapter completion
clearScreen();
const percentage = Math.round((score / exercises.length) * 100);
let message = "";
if (percentage === 100) {
message = "๐ PERFECT! You're a navigation ninja!";
} else if (percentage >= 80) {
message = "๐ Excellent! Your fingers are learning the way!";
} else if (percentage >= 60) {
message = "๐ Good job! Keep practicing those shortcuts!";
} else {
message = "๐ช Nice try! These shortcuts need muscle memory!";
}
console.log(
boxen(
green("๐ Chapter 2 Complete! ๐") +
"\n\n" +
bold(`Your Score: ${score}/${exercises.length} (${percentage}%)`) +
"\n\n" +
message +
"\n\n" +
"Navigation skills unlocked:\n" +
"โ
Move cursor without a mouse\n" +
"โ
Jump by characters and words\n" +
"โ
Delete text efficiently\n" +
"โ
Use Tab autocomplete\n" +
"โ
Clear screen instantly\n" +
"โ
Exit terminal properly\n\n" +
yellow("๐ฏ Practice Challenge:") + "\n" +
"Use these shortcuts in your next terminal session!\n" +
"Try to avoid using arrow keys for a whole day.\n\n" +
dim("Ready for Chapter 3: Essential Commands? Coming soon..."),
{
padding: 1,
borderStyle: "double",
borderColor: "green",
margin: 1,
}
)
);
// Show exercise summary
console.log("\n" + bold("Exercise Summary:"));
results.forEach((result) => {
const icon = result.passed ? green("โ") : chalk.red("โ");
console.log(`${icon} ${result.exercise}`);
});
await inquirer.prompt([
{
type: "input",
name: "continue",
message: "\nPress Enter to return to the main menu...",
prefix: "",
},
]);
}
module.exports = { start };