UNPKG

clitutor

Version:

Interactive CLI learning tool for beginners

1,077 lines (980 loc) โ€ข 32.3 kB
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 };