Event-Driven Programming - Build a Typing Game
## Introduction Here's something every developer knows but rarely talks about: typing fast is a superpower! ๐ Think about it - the faster you can get your ideas from your brain to your code editor, the more your creativity can flow. It's like having a direct pipeline between your thoughts and the screen. Want to know one of the best ways to level up this skill? You guessed it - we're going to build a game! Ready to put all those JavaScript, HTML, and CSS skills you've been learning to work? We're going to build a typing game that'll challenge you with random quotes from the legendary detective Sherlock Holmes. The game will track how fast and accurately you can type - and trust me, it's more addictive than you might think! ## What You'll Need to Know Before we dive in, make sure you're comfortable with these concepts (don't worry if you need a quick refresher - we've all been there!): - Creating text input and button controls - CSS and setting styles using classes - JavaScript basics - Creating an array - Creating a random number - Getting the current time If any of these feel a bit rusty, that's totally fine! Sometimes the best way to solidify your knowledge is by jumping into a project and figuring things out as you go. ### ๐ Pedagogical Check-in Foundation Assessment: Before starting development, ensure you understand: - โ How HTML forms and input elements work - โ CSS classes and dynamic styling - โ JavaScript event listeners and handlers - โ Array manipulation and random selection - โ Time measurement and calculations Quick Self-Test: Can you explain how these concepts work together in an interactive game? - Events trigger when users interact with elements - Handlers process those events and update game state - CSS provides visual feedback for user actions - Timing enables performance measurement and game progression ## Let's Build This Thing! Creating a typing game by using event driven programming ### โก What You Can Do in the Next 5 Minutes - [ ] Open your browser console and try listening for keyboard events with addEventListener - [ ] Create a simple HTML page with an input field and test typing detection - [ ] Practice string manipulation by comparing typed text with target text - [ ] Experiment with setTimeout to understand timing functions ### ๐ฏ What You Can Accomplish This Hour - [ ] Complete the post-lesson quiz and understand event-driven programming - [ ] Build a basic version of the typing game with word validation - [ ] Add visual feedback for correct and incorrect typing - [ ] Implement a simple scoring system based on speed and accuracy - [ ] Style your game with CSS to make it visually appealing ### ๐ Your Week-Long Game Development - [ ] Complete the full typing game with all features and polish - [ ] Add difficulty levels with varying word complexity - [ ] Implement user statistics tracking (WPM, accuracy over time) - [ ] Create sound effects and animations for better user experience - [ ] Make your game mobile-responsive for touch devices - [ ] Share your game online and gather feedback from users ### ๐ Your Month-Long Interactive Development - [ ] Build multiple games exploring different interaction patterns - [ ] Learn about game loops, state management, and performance optimization - [ ] Contribute to open source game development projects - [ ] Master advanced timing concepts and smooth animations - [ ] Create a portfolio showcasing various interactive applications - [ ] Mentor others interested in game development and user interaction ## ๐ฏ Your Typing Game Mastery Timeline ### ๐ ๏ธ Your Game Development Toolkit Summary After completing this project, you'll have mastered: - Event-Driven Programming: Responsive user interfaces that react to input - Real-Time Feedback: Instant visual and performance updates - Performance Measurement: Accurate timing and scoring systems - Game State Management: Controlling application flow and user experience - Interactive Design: Creating engaging, addictive user experiences - Modern Web APIs: Utilizing browser capabilities for rich interactions - Accessibility Patterns: Inclusive design for all users Real-World Applications: These skills directly apply to: - Web Applications: Any interactive interface or dashboard - Educational Software: Learning platforms and skill assessment tools - Productivity Tools: Text editors, IDEs, and collaboration software - Gaming Industry: Browser games and interactive entertainment - Mobile Development: Touch-based interfaces and gesture handling Next Level: You're ready to explore advanced game frameworks, real-time multiplayer systems, or complex interactive applications! ## Credits Written with โฅ๏ธ by Christopher Harrison
Creating a game using events
Have you ever wondered how websites know when you click a button or type in a text box? That's the magic of event-driven programming! What better way to learn this essential skill than by building something useful - a typing speed game that reacts to every keystroke you make. You're going to see firsthand how web browsers "talk" to your JavaScript code. Every time you click, type, or move your mouse, the browser is sending little messages (we call them events) to your code, and you get to decide how to respond! By the time we're done here, you'll have built a real typing game that tracks your speed and accuracy. More importantly, you'll understand the fundamental concepts that power every interactive website you've ever used. Let's dive in! ## Pre-Lecture Quiz Pre-lecture quiz ## Event driven programming Think about your favorite app or website - what makes it feel alive and responsive? It's all about how it reacts to what you do! Every tap, click, swipe, or keystroke creates what we call an "event," and that's where the real magic of web development happens. Here's what makes programming for the web so interesting: we never know when someone will click that button or start typing in a text box. They might click immediately, wait five minutes, or maybe never click at all! This unpredictability means we need to think differently about how we write our code. Instead of writing code that runs from top to bottom like a recipe, we write code that sits patiently waiting for something to happen. It's similar to how telegraph operators in the 1800s would sit by their machines, ready to respond the moment a message came through the wire. So what exactly is an "event"? Simply put, it's something that happens! When you click a button - that's an event. When you type a letter - that's an event. When you move your mouse - that's another event. Event-driven programming lets us set up our code to listen and respond. We create special functions called event listeners that wait patiently for specific things to happen, then spring into action when they do. Think of event listeners like having a doorbell for your code. You set up the doorbell (addEventListener()), tell it what sound to listen for (like a 'click' or 'keypress'), and then specify what should happen when someone rings it (your custom function). Here's how event listeners work: - Listens for specific user actions like clicks, keystrokes, or mouse movements - Executes your custom code when the specified event occurs - Responds immediately to user interactions, creating a seamless experience - Handles multiple events on the same element using different listeners ### Common events While web browsers offer dozens of different events you can listen for, most interactive applications rely on just a handful of essential events. Understanding these core events will give you the foundation to build sophisticated user interactions. There are dozens of events available for you to listen to when creating an application. Basically anything a user does on a page raises an event, which gives you a lot of power to ensure they get the experience you desire. Fortunately, you'll normally only need a small handful of events. Here's a few common ones (including the two we'll use when creating our game): Understanding these event types: - Triggers when users interact with specific elements on your page - Provides detailed information about the user's action through event objects - Enables you to create responsive, interactive web applications - Works consistently across different browsers and devices ## Creating the game Now that you understand how events work, let's put that knowledge into practice by building something useful. We'll create a typing speed game that demonstrates event handling while helping you develop an important developer skill. We're going to create a game to explore how events work in JavaScript. Our game will test a player's typing skill, which is one of the most underrated skills all developers should have. Fun fact: the QWERTY keyboard layout we use today was actually designed in the 1870s for typewriters - and good typing skills are still just as valuable for programmers today! The general flow of the game will look like this: Here's how our game will work: - Starts when the player clicks the start button and displays a random quote - Tracks the player's typing progress word by word in real-time - Highlights the current word to guide the player's focus - Provides immediate visual feedback for typing errors - Calculates and displays the total time when the quote is completed Let's build our game, and learn about events! ### File structure Before we start coding, let's get organized! Having a clean file structure from the beginning will save you headaches later and make your project more professional. ๐ We're going to keep things simple with just three files: index.html for our page structure, script.js for all our game logic, and style.css to make everything look great. This is the classic trio that powers most of the web! Create a new folder for your work by opening a console or terminal window and issuing the following command: Here's what these commands do: - Creates a new directory called typing-game for your project files - Navigates into the newly created directory automatically - Sets up a clean workspace for your game development Open Visual Studio Code: This command: - Launches Visual Studio Code in the current directory - Opens your project folder in the editor - Provides access to all the development tools you'll need Add three files to the folder in Visual Studio Code with the following names: - index.html - Contains the structure and content of your game - script.js - Handles all the game logic and event listeners - style.css - Defines the visual appearance and styling ## Create the user interface Now let's build the stage where all our game action will happen! Think of this like designing the control panel for a spaceship - we need to make sure everything our players need is right where they expect it to be. Let's figure out what our game actually needs. If you were playing a typing game, what would you want to see on the screen? Here's what we'll need: Understanding the UI structure: - Organizes content logically from top to bottom - Assigns unique IDs to elements for JavaScript targeting - Provides clear visual hierarchy for better user experience - Includes semantic HTML elements for accessibility Each of those will need IDs so we can work with them in our JavaScript. We will also add references to the CSS and JavaScript files we're going to create. Create a new file named index.html. Add the following HTML: Breaking down what this HTML structure accomplishes: - Links the CSS stylesheet in the <head> for styling - Creates a clear heading and instructions for users - Establishes placeholder paragraphs with specific IDs for dynamic content - Includes an input field with accessibility attributes - Provides a start button to trigger the game - Loads the JavaScript file at the end for optimal performance ### Launch the application Testing your application frequently during development helps you catch issues early and see your progress in real-time. Live Server is an invaluable tool that automatically refreshes your browser whenever you save changes, making development much more efficient. It's always best to develop iteratively to see how things look. Let's launch our application. There's a wonderful extension for Visual Studio Code called Live Server which will both host your application locally and refresh the browser each time you save. Install Live Server by following the link and clicking Install: Here's what happens during installation: - Prompts your browser to open Visual Studio Code - Guides you through the extension installation process - May require restarting Visual Studio Code to complete setup Once installed, in Visual Studio Code, click Ctrl-Shift-P (or Cmd-Shift-P) to open the command palette: Understanding the command palette: - Provides quick access to all VS Code commands - Searches commands as you type - Offers keyboard shortcuts for faster development Type "Live Server: Open with Live Server": What Live Server does: - Starts a local development server for your project - Automatically refreshes the browser when you save files - Serves your files from a local URL (typically localhost:5500) Open a browser and navigate to https://localhost:5500: You should now see the page you created! Let's add some functionality. ## Add the CSS Now let's make things look good! Visual feedback has been crucial for user interfaces since the early days of computing. In the 1980s, researchers discovered that immediate visual feedback dramatically improves user performance and reduces errors. That's exactly what we're going to create. Our game needs to be crystal clear about what's happening. Players should immediately know which word they're supposed to type, and if they make a mistake, they should see it right away. Let's create some simple but effective styling: Create a new file named style.css and add the following syntax. Understanding these CSS classes: - Highlights the current word with a yellow background for clear visual guidance - Signals typing errors with a light coral background color - Provides immediate feedback without disrupting the user's typing flow - Uses contrasting colors for accessibility and clear visual communication โ When it comes to CSS you can layout your page however you might like. Take a little time and make the page look more appealing: - Choose a different font - Colorize the headers - Resize items ## JavaScript Here's where things get interesting! ๐ We've got our HTML structure and our CSS styling, but right now our game is like a beautiful car without an engine. JavaScript is going to be that engine - it's what makes everything actually work and respond to what players do. This is where you'll see your creation come to life. We're going to tackle this step by step so nothing feels overwhelming: This structured approach helps you: - Organize your code into logical, manageable sections - Build functionality incrementally for easier debugging - Understand how different parts of your application work together - Create reusable patterns for future projects But first, create a new file named script.js. ### Add the constants Before we dive into the action, let's gather all our resources! Just like how NASA mission control sets up all their monitoring systems before launch, it's much easier when you have everything prepared and ready to go. This saves us from hunting around for things later and helps prevent typos. Here's what we need to set up first: We'll also need references to our UI elements: Breaking down what this setup code accomplishes: - Stores an array of Sherlock Holmes quotes using const since the quotes won't change - Initializes tracking variables with let since these values will update during gameplay - Captures references to DOM elements using document.getElementById() for efficient access - Sets up the foundation for all game functionality with clear, descriptive variable names - Organizes related data and elements logically for easier code maintenance โ Go ahead and add more quotes to your game Here's why this approach works so well: - Prevents spelling errors when referencing elements multiple times - Improves code readability with descriptive constant names - Enables better IDE support with autocomplete and error checking - Makes refactoring easier if element IDs change later Take a minute to watch a video on using const, let and var [](https://youtube.com/watch?v=JNIXfGiDWM8 "Types of variables") ### Add start logic This is where everything clicks into place! ๐ You're about to write your first real event listener, and there's something quite satisfying about seeing your code respond to a button click. Think about it: somewhere out there, a player is going to click that "Start" button, and your code needs to be ready for them. We have no idea when they'll click it - could be immediately, could be after they grab a coffee - but when they do, your game springs to life. When the user clicks start, we need to select a quote, setup the user interface, and setup tracking for the current word and timing. Below is the JavaScript you'll need to add; we discuss it just after the script block. Let's break down the code into logical sections: ๐ Word Tracking Setup: - Selects a random quote using Math.floor() and Math.random() for variety - Converts the quote into an array of individual words using split(' ') - Resets the wordIndex to 0 since players start with the first word - Prepares the game state for a fresh round ๐จ UI Setup and Display: - Creates an array of <span> elements, wrapping each word for individual styling - Joins the span elements into a single string for efficient DOM updating - Highlights the first word by adding the highlight CSS class - Clears any previous game messages to provide a clean slate โจ๏ธ Textbox Preparation: - Clears any existing text in the input field - Sets focus to the textbox so players can start typing immediately - Prepares the input area for the new game session โฑ๏ธ Timer Initialization: - Captures the current timestamp using new Date().getTime() - Enables accurate calculation of typing speed and completion time - Starts the performance tracking for the game session ### Add typing logic Here's where we tackle the heart of our game! Don't worry if this seems like a lot at first - we'll walk through every piece, and by the end, you'll see how logical it all is. What we're building here is quite sophisticated: every single time someone types a letter, our code is going to check what they typed, give them feedback, and decide what should happen next. It's similar to how early word processors like WordStar in the 1970s provided real-time feedback to typists. Understanding the typing logic flow: This function uses a waterfall approach, checking conditions from most specific to most general. Let's break down each scenario: ๐ Quote Complete (Scenario 1): - Checks if typed value matches current word AND we're on the last word - Calculates elapsed time by subtracting start time from current time - Converts milliseconds to seconds by dividing by 1,000 - Displays congratulatory message with completion time โ Word Complete (Scenario 2): - Detects word completion when input ends with a space - Validates that trimmed input matches the current word exactly - Clears the input field for the next word - Advances to the next word by incrementing wordIndex - Updates visual highlighting by removing all classes and highlighting the new word ๐ Typing in Progress (Scenario 3): - Verifies that the current word starts with what's been typed so far - Removes any error styling to show the input is correct - Allows continued typing without interruption โ Error State (Scenario 4): - Triggers when typed text doesn't match the expected word beginning - Applies error CSS class to provide immediate visual feedback - Helps players quickly identify and correct mistakes ## Test your application Look what you've accomplished! ๐ You just built a real, working typing game from scratch using event-driven programming. Take a moment to appreciate that - this is no small feat! Now comes the testing phase! Will it work as expected? Did we miss something? Here's the thing: if something doesn't work perfectly right away, that's completely normal. Even experienced developers find bugs in their code regularly. It's all part of the development process! Click on start, and start typing away! It should look a little like the animation we saw before. What to test in your application: - Verifies that clicking Start displays a random quote - Confirms that typing highlights the current word correctly - Checks that error styling appears for incorrect typing - Ensures that completing words advances the highlight properly - Tests that finishing the quote shows the completion message with timing Common debugging tips: - Check the browser console (F12) for JavaScript errors - Verify that all file names match exactly (case-sensitive) - Ensure Live Server is running and refreshing properly - Test different quotes to verify the random selection works --- ## GitHub Copilot Agent Challenge ๐ฎ Use the Agent mode to complete the following challenge: Description: Extend the typing game by implementing a difficulty system that adjusts the game based on player performance. This challenge will help you practice advanced event handling, data analysis, and dynamic UI updates. Prompt: Create a difficulty adjustment system for the typing game that: 1. Tracks the player's typing speed (words per minute) and accuracy percentage 2. Automatically adjusts to three difficulty levels: Easy (simple quotes), Medium (current quotes), Hard (complex quotes with punctuation) 3. Displays the current difficulty level and player statistics on the UI 4. Implements a streak counter that increases difficulty after 3 consecutive good performances 5. Adds visual feedback (colors, animations) to indicate difficulty changes Add the necessary HTML elements, CSS styles, and JavaScript functions to implement this feature. Include proper error handling and ensure the game remains accessible with appropriate ARIA labels. Learn more about agent mode here. ## ๐ Challenge Ready to take your typing game to the next level? Try implementing these advanced features to deepen your understanding of event handling and DOM manipulation: Add more functionality: Implementation tips: - Research localStorage.setItem() and localStorage.getItem() for persistent storage - Practice adding and removing event listeners dynamically - Explore HTML dialog elements or CSS modal patterns - Consider accessibility when disabling and enabling form controls ## Post-Lecture Quiz Post-lecture quiz --- ## ๐ Your Typing Game Mastery Timeline ### โก What You Can Do in the Next 5 Minutes - [ ] Test your typing game with different quotes to ensure it works smoothly - [ ] Experiment with the CSS styling - try changing the highlight and error colors - [ ] Open your browser's DevTools (F12) and watch the Console while playing - [ ] Challenge yourself to complete a quote as fast as possible ### โฐ What You Can Accomplish This Hour - [ ] Add more quotes to the array (maybe from your favorite books or movies) - [ ] Implement the localStorage high score system from the challenge section - [ ] Create a words-per-minute calculator that displays after each game - [ ] Add sound effects for correct typing, errors, and completion ### ๐ Your Week-Long Adventure - [ ] Build a multiplayer version where friends can compete side-by-side - [ ] Create different difficulty levels with varying quote complexity - [ ] Add a progress bar showing how much of the quote is complete - [ ] Implement user accounts with personal statistics tracking - [ ] Design custom themes and let users choose their preferred styling ### ๐๏ธ Your Month-Long Transformation - [ ] Create a typing course with lessons that progressively teach proper finger placement - [ ] Build analytics that show which letters or words cause the most errors - [ ] Add support for different languages and keyboard layouts - [ ] Integrate with educational APIs to pull quotes from literature databases - [ ] Publish your enhanced typing game for others to use and enjoy ### ๐ฏ Final Reflection Check-in Before you move on, take a moment to celebrate: - What was the most satisfying moment while building this game? - How do you feel about event-driven programming now compared to when you started? - What's one feature you're excited to add to make this game uniquely yours? - How might you apply event handling concepts to other projects? --- ## Review & Self Study Read up on all the events available to the developer via the web browser, and consider the scenarios in which you would use each one. ## Assignment Create a new keyboard game
Build a Chat Assistant with AI
Remember in Star Trek when the crew would casually chat with the ship's computer, asking it complex questions and getting thoughtful responses? What seemed like pure science fiction in the 1960s is now something you can build using web technologies you already know. In this lesson, we'll create an AI chat assistant using HTML, CSS, JavaScript, and some backend integration. You'll discover how the same skills you've been learning can connect to powerful AI services that can understand context and generate meaningful responses. Think of AI like having access to a vast library that can not only find information but also synthesize it into coherent answers tailored to your specific questions. Instead of searching through thousands of pages, you get direct, contextual responses. The integration happens through familiar web technologies working together. HTML creates the chat interface, CSS handles the visual design, JavaScript manages user interactions, and a backend API connects everything to AI services. It's similar to how different sections of an orchestra work together to create a symphony. We're essentially building a bridge between natural human communication and machine processing. You'll learn both the technical implementation of AI service integration and the design patterns that make interactions feel intuitive. By the end of this lesson, AI integration will feel less like a mysterious process and more like another API you can work with. You'll understand the foundational patterns that power applications like ChatGPT and Claude, using the same web development principles you've been learning. ## โก What You Can Do in the Next 5 Minutes Quick Start Pathway for Busy Developers - Minute 1: Visit GitHub Models Playground and create a personal access token - Minute 2: Test AI interactions directly in the playground interface - Minute 3: Click "Code" tab and copy the Python snippet - Minute 4: Run the code locally with your token: GITHUB_TOKEN=your_token python test.py - Minute 5: Watch your first AI response generate from your own code Quick Test Code: Why This Matters: In 5 minutes, you'll experience the magic of programmatic AI interaction. This represents the fundamental building block that powers every AI application you use. Here's what your finished project will look like: ## ๐บ๏ธ Your Learning Journey Through AI Application Development Your Journey Destination: By the end of this lesson, you'll have built a complete AI-powered application using the same technologies and patterns that power modern AI assistants like ChatGPT, Claude, and Google Bard. ## Understanding AI: From Mystery to Mastery Before diving into the code, let's understand what we're working with. If you've used APIs before, you know the basic pattern: send a request, receive a response. AI APIs follow a similar structure, but instead of retrieving pre-stored data from a database, they generate new responses based on patterns learned from vast amounts of text. Think of it like the difference between a library catalog system and a knowledgeable librarian who can synthesize information from multiple sources. ### What is "Generative AI" Really? Consider how the Rosetta Stone allowed scholars to understand Egyptian hieroglyphics by finding patterns between known and unknown languages. AI models work similarly โ they find patterns in vast amounts of text to understand how language works, then use those patterns to generate appropriate responses to new questions. Let me break this down with a simple comparison: - Traditional database: Like asking for your birth certificate โ you get the exact same document every time - Search engine: Like asking a librarian to find books about cats โ they show you what's available - Generative AI: Like asking a knowledgeable friend about cats โ they tell you interesting things in their own words, tailored to what you want to know ### How AI Models Learn (The Simple Version) AI models learn through exposure to enormous datasets containing text from books, articles, and conversations. Through this process, they identify patterns in: - How thoughts are structured in written communication - Which words commonly appear together - How conversations typically flow - Contextual differences between formal and informal communication It's similar to how archaeologists decode ancient languages: they analyze thousands of examples to understand grammar, vocabulary, and cultural context, eventually becoming able to interpret new texts using those learned patterns. ### Why GitHub Models? We're using GitHub Models for a pretty practical reason โ it gives us access to enterprise-level AI without having to set up our own AI infrastructure (which, trust me, you don't want to do right now!). Think of it like using a weather API instead of trying to predict the weather yourself by setting up weather stations everywhere. It's basically "AI-as-a-Service," and the best part? It's free to get started, so you can experiment without worrying about running up a huge bill. We'll use GitHub Models for our backend integration, which provides access to professional-grade AI capabilities through a developer-friendly interface. The GitHub Models Playground serves as a testing environment where you can experiment with different AI models and understand their capabilities before implementing them in code. ## ๐ง AI Application Development Ecosystem Core Principle: AI application development combines traditional web development skills with AI service integration, creating intelligent applications that feel natural and responsive to users. Here's what makes the playground so useful: - Try out different AI models like GPT-4o-mini, Claude, and others (all free!) - Test your ideas and prompts before you write any code - Get ready-to-use code snippets in your favorite programming language - Tweak settings like creativity level and response length to see how they affect the output Once you've played around a bit, just click the "Code" tab and pick your programming language to get the implementation code you'll need. ## Setting Up the Python Backend Integration Now let's implement the AI integration using Python. Python is excellent for AI applications because of its simple syntax and powerful libraries. We'll start with the code from GitHub Models playground and then refactor it into a reusable, production-ready function. ### Understanding the Base Implementation When you grab the Python code from the playground, you'll get something that looks like this. Don't worry if it seems like a lot at first โ let's walk through it piece by piece: Here's what's happening in this code: - We import the tools we need: os for reading environment variables and OpenAI for talking to the AI - We set up the OpenAI client to point to GitHub's AI servers instead of OpenAI directly - We authenticate using a special GitHub token (more on that in a minute!) - We structure our conversation with different "roles" โ think of it like setting the scene for a play - We send our request to the AI with some fine-tuning parameters - We extract the actual response text from all the data that comes back ### Understanding Message Roles: The AI Conversation Framework AI conversations use a specific structure with different "roles" that serve distinct purposes: Think of it like directing a play: - System role: Like stage directions for an actor โ it tells the AI how to behave, what personality to have, and how to respond - User role: The actual question or message from the person using your application - Assistant role: The AI's response (you don't send this, but it appears in conversation history) Real-world analogy: Imagine you're introducing a friend to someone at a party: - System message: "This is my friend Sarah, she's a doctor who's great at explaining medical concepts in simple terms" - User message: "Can you explain how vaccines work?" - Assistant response: Sarah responds as a friendly doctor, not as a lawyer or a chef ### Understanding AI Parameters: Fine-Tuning Response Behavior The numerical parameters in AI API calls control how the model generates responses. These settings allow you to adjust the AI's behavior for different use cases: #### Temperature (0.0 to 2.0): The Creativity Dial What it does: Controls how creative or predictable the AI's responses will be. Think of it like a jazz musician's improvisation level: - Temperature = 0.1: Playing the exact same melody every time (highly predictable) - Temperature = 0.7: Adding some tasteful variations while staying recognizable (balanced creativity) - Temperature = 1.5: Full experimental jazz with unexpected turns (highly unpredictable) #### Max Tokens (1 to 4096+): The Response Length Controller What it does: Sets a limit on how long the AI's response can be. Think of tokens as roughly equivalent to words (about 1 token = 0.75 words in English): - max_tokens=50: Short and sweet (like a text message) - max_tokens=500: A nice paragraph or two - max_tokens=2000: A detailed explanation with examples #### Top_p (0.0 to 1.0): The Focus Parameter What it does: Controls how focused the AI stays on the most likely responses. Picture the AI having a huge vocabulary, ranked by how likely each word is: - top_p=0.1: Only considers the top 10% most likely words (very focused) - top_p=0.9: Considers 90% of possible words (more creative) - top_p=1.0: Considers everything (maximum variety) For example: If you ask "The sky is usually..." - Low top_p: Almost definitely says "blue" - High top_p: Might say "blue", "cloudy", "vast", "changing", "beautiful", etc. ### Putting It All Together: Parameter Combinations for Different Use Cases Understanding why these parameters matter: Different applications need different types of responses. A customer service bot should be consistent and factual (low temperature), while a creative writing assistant should be imaginative and varied (high temperature). Understanding these parameters gives you control over your AI's personality and response style. import asyncio from openai import AsyncOpenAI # Use AsyncOpenAI for better performance client = AsyncOpenAI( base_url="https://models.github.ai/inference", api_key=os.environ["GITHUB_TOKEN"], async def call_llm_async(prompt: str, system_message: str = "You are a helpful assistant."): """ Sends a prompt to the AI model asynchronously and returns the response. Args: prompt: The user's question or message system_message: Instructions that define the AI's behavior and personality Returns: str: The AI's response to the prompt """ try: response = await client.chat.completions.create( messages=[ "role": "system", "content": system_message, "role": "user", "content": prompt, model="openai/gpt-4o-mini", temperature=1, max_tokens=4096, top_p=1 return response.choices[0].message.content except Exception as e: logger.error(f"AI API error: {str(e)}") return "I'm sorry, I'm having trouble processing your request right now." # Backward compatibility function for synchronous calls def call_llm(prompt: str, system_message: str = "You are a helpful assistant."): """Synchronous wrapper for async AI calls.""" return asyncio.run(call_llm_async(prompt, system_message)) # โ Vague system prompt "You are helpful." # โ Detailed, effective system prompt "You are Dr. Sarah Chen, a senior software engineer with 15 years of experience at major tech companies. You explain programming concepts using real-world analogies and always provide practical examples. You're patient with beginners and enthusiastic about helping them understand complex topics." # Example 1: The Patient Teacher teacher_prompt = """ You are an experienced programming instructor who has taught thousands of students. You break down complex concepts into simple steps, use analogies from everyday life, and always check if the student understands before moving on. You're encouraging and never make students feel bad for not knowing something. """ # Example 2: The Creative Collaborator creative_prompt = """ You are a creative writing partner who loves brainstorming wild ideas. You're enthusiastic, imaginative, and always build on the user's ideas rather than replacing them. You ask thought-provoking questions to spark creativity and offer unexpected perspectives that make stories more interesting. """ # Example 3: The Strategic Business Advisor business_prompt = """ You are a strategic business consultant with an MBA and 20 years of experience helping startups scale. You think in frameworks, provide structured advice, and always consider both short-term tactics and long-term strategy. You ask probing questions to understand the full business context before giving advice. """ # With teacher prompt: teacher_response = call_llm( "How do I handle user authentication in my web app?", teacher_prompt # Typical response: "Great question! Let's break authentication down into simple steps. # Think of it like a nightclub bouncer checking IDs..." # With business prompt: business_response = call_llm( "How do I handle user authentication in my web app?", business_prompt # Typical response: "From a strategic perspective, authentication is crucial for user # trust and regulatory compliance. Let me outline a framework considering security, # user experience, and scalability..." system_prompt = """ You are helping a junior developer who just started their first job at a startup. They know basic HTML/CSS/JavaScript but are new to backend development and databases. Be encouraging and explain things step-by-step without being condescending. """ system_prompt = """ You are a technical mentor. Always structure your responses as: 1. Quick Answer (1-2 sentences) 2. Detailed Explanation 3. Code Example 4. Common Pitfalls to Avoid 5. Next Steps for Learning """ system_prompt = """ You are a coding tutor focused on teaching best practices. Never write complete solutions for the user - instead, guide them with hints and questions so they learn by doing. Always explain the 'why' behind coding decisions. """ sequenceDiagram participant User as ๐ค User participant Frontend as ๐ Frontend participant API as ๐ง FastAPI Server participant AI as ๐ค AI Service User->>Frontend: Types "Hello AI!" Frontend->>API: POST /hello {"message": "Hello AI!"} Note over API: Validates request<br/>Adds system prompt API->>AI: Sends formatted request AI->>API: Returns AI response Note over API: Processes response<br/>Logs conversation API->>Frontend: {"response": "Hello! How can I help?"} Frontend->>User: Displays AI message sequenceDiagram participant Frontend participant FastAPI participant AI Function participant GitHub Models Frontend->>FastAPI: POST /hello {"message": "Hello AI!"} FastAPI->>AI Function: call_llm(message, system_prompt) AI Function->>GitHub Models: API request GitHub Models->>AI Function: AI response AI Function->>FastAPI: response text FastAPI->>Frontend: {"response": "Hello! How can I help?"} flowchart TD A[User Input] --> B[Frontend Validation] B --> C[HTTP POST Request] C --> D[FastAPI Router] D --> E[Pydantic Validation] E --> F[AI Function Call] F --> G[GitHub Models API] G --> H[Response Processing] H --> I[JSON Response] I --> J[Frontend Update] subgraph "Security Layer" K[CORS Middleware] L[Environment Variables] M[Error Handling] end D --> K F --> L H --> M # api.py from fastapi import FastAPI, HTTPException from fastapi.middleware.cors import CORSMiddleware from pydantic import BaseModel from llm import call_llm import logging # Configure logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) # Create FastAPI application app = FastAPI( title="AI Chat API", description="A high-performance API for AI-powered chat applications", version="1.0.0" # Configure CORS app.add_middleware( CORSMiddleware, allow_origins=["*"], # Configure appropriately for production allow_credentials=True, allow_methods=["*"], allow_headers=["*"], # Pydantic models for request/response validation class ChatMessage(BaseModel): message: str class ChatResponse(BaseModel): response: str @app.get("/") async def root(): """Root endpoint providing API information.""" return { "message": "Welcome to the AI Chat API", "docs": "/docs", "health": "/health" @app.get("/health") async def health_check(): """Health check endpoint.""" return {"status": "healthy", "service": "ai-chat-api"} @app.post("/hello", response_model=ChatResponse) async def chat_endpoint(chat_message: ChatMessage): """Main chat endpoint that processes messages and returns AI responses.""" try: # Extract and validate message message = chat_message.message.strip() if not message: raise HTTPException(status_code=400, detail="Message cannot be empty") logger.info(f"Processing message: {message[:50]}...") # Call AI service (note: call_llm should be made async for better performance) ai_response = await call_llm_async(message, "You are a helpful and friendly assistant.") logger.info("AI response generated successfully") return ChatResponse(response=ai_response) except HTTPException: raise except Exception as e: logger.error(f"Error processing chat message: {str(e)}") raise HTTPException(status_code=500, detail="Internal server error") if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=5000, reload=True) from fastapi.middleware.cors import CORSMiddleware app = FastAPI(__name__) CORS(app) # This tells browsers: "It's okay for other origins to make requests to this API" # ๐จ Development: Allows ALL origins (convenient but insecure) CORS(app) # โ Production: Only allow your specific frontend domain CORS(app, origins=["https://yourdomain.com", "https://www.yourdomain.com"]) # ๐ Advanced: Different origins for different environments if app.debug: # Development mode CORS(app, origins=["http://localhost:3000", "http://127.0.0.1:3000"]) else: # Production mode CORS(app, origins=["https://yourdomain.com"]) # Validate that we received a message if not message: return jsonify({"error": "Message field is required"}), 400 # Navigate to your backend directory cd backend # Create a virtual environment (like creating a clean room for your project) python -m venv venv # Activate it (Linux/Mac) source ./venv/bin/activate # On Windows, use: # venv\Scripts\activate # Install the good stuff pip install openai fastapi uvicorn python-dotenv # ๐จ NEVER DO THIS - API key visible to everyone client = OpenAI( api_key="ghp_1234567890abcdef...", # Anyone can steal this! base_url="https://models.github.ai/inference" # โ DO THIS - API key stored securely client = OpenAI( api_key=os.environ["GITHUB_TOKEN"], # Only your app can access this base_url="https://models.github.ai/inference" # .env file - This should NEVER be committed to Git GITHUB_TOKEN=your_github_personal_access_token_here FASTAPI_DEBUG=True ENVIRONMENT=development # Example of what your token looks like (this is fake!) GITHUB_TOKEN=ghp_1A2B3C4D5E6F7G8H9I0J1K2L3M4N5O6P7Q8R import os from dotenv import load_dotenv # Load environment variables from .env file load_dotenv() # Now you can access them securely api_key = os.environ.get("GITHUB_TOKEN") if not api_key: raise ValueError("GITHUB_TOKEN not found in environment variables!") client = OpenAI( api_key=api_key, base_url="https://models.github.ai/inference" # .gitignore - Add these lines .env *.env .env.local .env.production __pycache__/ venv/ .vscode/ # .env.development GITHUB_TOKEN=your_development_token DEBUG=True # .env.production GITHUB_TOKEN=your_production_token DEBUG=False # Method 1: Direct Python execution (includes auto-reload) python api.py # Method 2: Using Uvicorn directly (more control) uvicorn api:app --host 0.0.0.0 --port 5000 --reload $ python api.py INFO: Will watch for changes in these directories: ['/your/project/path'] INFO: Uvicorn running on http://0.0.0.0:5000 (Press CTRL+C to quit) INFO: Started reloader process [12345] using WatchFiles INFO: Started server process [12346] INFO: Waiting for application startup. INFO: Application startup complete. # Test with curl (if available) curl -X POST http://localhost:5000/hello \ -H "Content-Type: application/json" \ -d '{"message": "Hello AI!"}' # Expected response: # {"response": "Hello! I'm your AI assistant. How can I help you today?"} # test_api.py - Create this file to test your API import requests import json # Test the API endpoint url = "http://localhost:5000/hello" data = {"message": "Tell me a joke about programming"} response = requests.post(url, json=data) if response.status_code == 200: result = response.json() print("AI Response:", result['response']) else: print("Error:", response.status_code, response.text) # Enable hot reloading explicitly if __name__ == "__main__": app.run(host="0.0.0.0", port=5000, debug=True) # debug=True enables hot reload import logging # Set up logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) @app.route("/hello", methods=["POST"]) def hello(): data = request.get_json() message = data.get("message", "") logger.info(f"Received message: {message}") if not message: logger.warning("Empty message received") return jsonify({"error": "Message field is required"}), 400 try: response = call_llm(message, "You are a helpful and friendly assistant.") logger.info(f"AI response generated successfully") return jsonify({"response": response}) except Exception as e: logger.error(f"AI API error: {str(e)}") return jsonify({"error": "AI service temporarily unavailable"}), 500 cd backend python api.py https://your-codespace-name-5000.app.github.dev // In your frontend app.js, update the BASE_URL: this.BASE_URL = "https://your-codespace-name-5000.app.github.dev"; https://[codespace-name]-[port].app.github.dev Welcome to the AI Chat API. Send POST requests to /hello with JSON payload containing 'message' field. // Open browser console and test your API fetch('https://your-codespace-name-5000.app.github.dev/hello', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({message: 'Hello from Codespaces!'}) .then(response => response.json()) .then(data => console.log(data)); # Set environment variable for the current session export GITHUB_TOKEN="your_token_here" # Or add to your .bashrc for persistence echo 'export GITHUB_TOKEN="your_token_here"' >> ~/.bashrc graph TD A[User Types Message] --> B[JavaScript Captures Input] B --> C[Validate & Format Data] C --> D[Send to Backend API] D --> E[Display Loading State] E --> F[Receive AI Response] F --> G[Update Chat Interface] G --> H[Ready for Next Message] classDiagram class ChatApp { +messages: HTMLElement +form: HTMLElement +input: HTMLElement +sendButton: HTMLElement +BASE_URL: string +API_ENDPOINT: string +constructor() +initializeEventListeners() +handleSubmit(event) +callAPI(message) +appendMessage(text, role) +escapeHtml(text) +scrollToBottom() +setLoading(isLoading) ChatApp --> DOM : manipulates ChatApp --> FastAPI : sends requests frontend/ โโโ index.html # Main HTML structure โโโ app.js # JavaScript functionality โโโ styles.css # Visual styling <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>AI Chat Assistant</title> <link rel="stylesheet" href="styles.css"> </head> <body> <div class="chat-container"> <header class="chat-header"> <h1>AI Chat Assistant</h1> <p>Ask me anything!</p> </header> <main class="chat-messages" id="messages" role="log" aria-live="polite"> <!-- Messages will be dynamically added here --> </main> <form class="chat-form" id="chatForm"> <div class="input-group"> <input type="text" id="messageInput" placeholder="Type your message here..." required aria-label="Chat message input" <button type="submit" id="sendBtn" aria-label="Send message"> Send </button> </div> </form> </div> <script src="app.js"></script> </body> </html> // app.js - Modern chat application logic class ChatApp { constructor() { // Get references to DOM elements we'll need to manipulate this.messages = document.getElementById("messages"); this.form = document.getElementById("chatForm"); this.input = document.getElementById("messageInput"); this.sendButton = document.getElementById("sendBtn"); // Configure your backend URL here this.BASE_URL = "http://localhost:5000"; // Update this for your environment this.API_ENDPOINT = ${this.BASE_URL}/hello; // Set up event listeners when the chat app is created this.initializeEventListeners(); initializeEventListeners() { // Listen for form submission (when user clicks Send or presses Enter) this.form.addEventListener("submit", (e) => this.handleSubmit(e)); // Also listen for Enter key in the input field (better UX) this.input.addEventListener("keypress", (e) => { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); this.handleSubmit(e); }); async handleSubmit(event) { event.preventDefault(); // Prevent form from refreshing the page const messageText = this.input.value.trim(); if (!messageText) return; // Don't send empty messages // Provide user feedback that something is happening this.setLoading(true); // Add user message to chat immediately (optimistic UI) this.appendMessage(messageText, "user"); // Clear input field so user can type next message this.input.value = ''; try { // Call the AI API and wait for response const reply = await this.callAPI(messageText); // Add AI response to chat this.appendMessage(reply, "assistant"); } catch (error) { console.error('API Error:', error); this.appendMessage("Sorry, I'm having trouble connecting right now. Please try again.", "error"); } finally { // Re-enable the interface regardless of success or failure this.setLoading(false); async callAPI(message) { const response = await fetch(this.API_ENDPOINT, { method: "POST", headers: { "Content-Type": "application/json" body: JSON.stringify({ message }) }); if (!response.ok) { throw new Error(HTTP error! status: ${response.status}); const data = await response.json(); return data.response; appendMessage(text, role) { const messageElement = document.createElement("div"); messageElement.className = message ${role}; messageElement.innerHTML = ` <div class="message-content"> <span class="message-text">${this.escapeHtml(text)}</span> <span class="message-time">${new Date().toLocaleTimeString()}</span> </div> this.messages.appendChild(messageElement); this.scrollToBottom(); escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; scrollToBottom() { this.messages.scrollTop = this.messages.scrollHeight; setLoading(isLoading) { this.sendButton.disabled = isLoading; this.input.disabled = isLoading; this.sendButton.textContent = isLoading ? "Sending..." : "Send"; // Initialize the chat application when the page loads document.addEventListener("DOMContentLoaded", () => { new ChatApp(); }); class ChatApp { constructor() { // This runs when you create a new ChatApp instance // It's like the "setup" function for your chat methodName() { // Methods are functions that belong to the class // They can access class properties using "this" // Old way (callback hell): fetch(url) .then(response => response.json()) .then(data => console.log(data)) .catch(error => console.error(error)); // Modern way (async/await): try { const response = await fetch(url); const data = await response.json(); console.log(data); } catch (error) { console.error(error); // When form is submitted, run handleSubmit this.form.addEventListener("submit", (e) => this.handleSubmit(e)); // When Enter key is pressed, also run handleSubmit this.input.addEventListener("keypress", (e) => { / ... / }); // Create new elements const messageElement = document.createElement("div"); // Modify their properties messageElement.className = "message user"; messageElement.innerHTML = "Hello world!"; // Add to the page this.messages.appendChild(messageElement); escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; // This automatically escapes HTML return div.innerHTML; try { const reply = await this.callAPI(messageText); this.appendMessage(reply, "assistant"); } catch (error) { // Show user-friendly error instead of breaking the app this.appendMessage("Sorry, I'm having trouble...", "error"); / styles.css - Modern chat interface styling / :root { --primary-color: #2563eb; --secondary-color: #f1f5f9; --user-color: #3b82f6; --assistant-color: #6b7280; --error-color: #ef4444; --text-primary: #1e293b; --text-secondary: #64748b; --border-radius: 12px; --shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); * { margin: 0; padding: 0; box-sizing: border-box; body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 20px; .chat-container { width: 100%; max-width: 800px; height: 600px; background: white; border-radius: var(--border-radius); box-shadow: var(--shadow); display: flex; flex-direction: column; overflow: hidden; .chat-header { background: var(--primary-color); color: white; padding: 20px; text-align: center; .chat-header h1 { font-size: 1.5rem; margin-bottom: 5px; .chat-header p { opacity: 0.9; font-size: 0.9rem; .chat-messages { flex: 1; padding: 20px; overflow-y: auto; display: flex; flex-direction: column; gap: 15px; background: var(--secondary-color); .message { display: flex; max-width: 80%; animation: slideIn 0.3s ease-out; .message.user { align-self: flex-end; .message.user .message-content { background: var(--user-color); color: white; border-radius: var(--border-radius) var(--border-radius) 4px var(--border-radius); .message.assistant { align-self: flex-start; .message.assistant .message-content { background: white; color: var(--text-primary); border-radius: var(--border-radius) var(--border-radius) var(--border-radius) 4px; border: 1px solid #e2e8f0; .message.error .message-content { background: var(--error-color); color: white; border-radius: var(--border-radius); .message-content { padding: 12px 16px; box-shadow: var(--shadow); position: relative; .message-text { display: block; line-height: 1.5; word-wrap: break-word; .message-time { display: block; font-size: 0.75rem; opacity: 0.7; margin-top: 5px; .chat-form { padding: 20px; border-top: 1px solid #e2e8f0; background: white; .input-group { display: flex; gap: 10px; align-items: center; #messageInput { flex: 1; padding: 12px 16px; border: 2px solid #e2e8f0; border-radius: var(--border-radius); font-size: 1rem; outline: none; transition: border-color 0.2s ease; #messageInput:focus { border-color: var(--primary-color); #messageInput:disabled { background: #f8fafc; opacity: 0.6; cursor: not-allowed; #sendBtn { padding: 12px 24px; background: var(--primary-color); color: white; border: none; border-radius: var(--border-radius); font-size: 1rem; font-weight: 600; cursor: pointer; transition: background-color 0.2s ease; min-width: 80px; #sendBtn:hover:not(:disabled) { background: #1d4ed8; #sendBtn:disabled { background: #94a3b8; cursor: not-allowed; @keyframes slideIn { from { opacity: 0; transform: translateY(10px); to { opacity: 1; transform: translateY(0); / Responsive design for mobile devices / @media (max-width: 768px) { body { padding: 10px; .chat-container { height: calc(100vh - 20px); border-radius: 8px; .message { max-width: 90%; .input-group { flex-direction: column; gap: 10px; #messageInput { width: 100%; #sendBtn { width: 100%; / Accessibility improvements / @media (prefers-reduced-motion: reduce) { .message { animation: none; * { transition: none !important; / Dark mode support / @media (prefers-color-scheme: dark) { .chat-container { background: #1e293b; color: #f1f5f9; .chat-messages { background: #0f172a; .message.assistant .message-content { background: #334155; color: #f1f5f9; border-color: #475569; .chat-form { background: #1e293b; border-color: #475569; #messageInput { background: #334155; color: #f1f5f9; border-color: #475569; // For local development this.BASE_URL = "http://localhost:5000"; // For GitHub Codespaces (replace with your actual URL) this.BASE_URL = "https://your-codespace-name-5000.app.github.dev"; graph TD A[Start Backend Server] --> B[Configure Environment Variables] B --> C[Test API Endpoints] C --> D[Open Frontend in Browser] D --> E[Test Chat Functionality] E --> F[Debug Any Issues] timeline title Complete AI Application Development Journey section AI Foundations Understanding Generative AI : Grasp pattern recognition concepts : Master AI parameter control : Learn prompt engineering techniques GitHub Models Integration : Navigate AI service platforms : Handle authentication securely : Optimize model parameters section Backend Development Python API Architecture : Build FastAPI applications : Implement async operations : Create secure endpoints AI Service Integration : Connect to external AI APIs : Handle rate limiting : Implement error boundaries section Frontend Mastery Modern JavaScript Patterns : Master ES6 class architecture : Implement async/await flows : Build responsive interfaces Real-time User Experience : Create dynamic chat interfaces : Handle loading states : Optimize user interactions section Production Readiness Security & Performance : Implement secure token management : Prevent XSS vulnerabilities : Optimize API performance Professional Deployment : Build scalable architectures : Create maintainable code : Document development processes my-ai-assistant/ โโโ backend/ โ โโโ api.py # Your FastAPI server โ โโโ llm.py # AI integration functions โ โโโ .env # Your secrets (keep this safe!) โ โโโ requirements.txt # Python dependencies โโโ frontend/ โ โโโ index.html # Your chat interface โ โโโ app.js # The JavaScript magic โ โโโ styles.css # Make it look amazing โโโ README.md # Tell the world about your creation call_llm(message, "You are a professional business consultant with 20 years of experience. Provide structured, actionable advice with specific steps and considerations.") call_llm(message, "You are an enthusiastic creative writing coach. Help users develop their storytelling skills with imaginative prompts and constructive feedback.") call_llm(message, "You are a patient senior developer who explains complex programming concepts using simple analogies and practical examples.") mindmap root((AI Chat App Skills)) API Integration Authentication Error Handling Async Programming Web Development HTML5 Semantics Modern CSS ES6+ JavaScript User Experience Responsive Design Accessibility Real-time Interaction AI Understanding Prompt Engineering Model Parameters Conversation Flow
Run solution
1. Start up the backend 1. Now start the fronten
Run code
## Set up Create virtual environment ## Install dependencies ## Run API ## Test API Visit the interactive API documentation at: http://localhost:5000/docs ## Run frontend Make sure you stand in the frontend folder Locate app.js, change BASE_URL to that of your backend URL Run it Try typing a message in the chat, you should see a response (providing you're running this in a Codespace or have set up a access token). ## Set up access token (if you don't run this in a Codespace) See Set up PAT
Run the code
Locate the BASE_URL in app.js and change it match the URL of the backend.
Quiz app
These quizzes are the pre- and post-lecture quizzes for the data science curriculum at https://aka.ms/webdev-beginners ## Adding a translated quiz set Add a quiz translation by creating matching quiz structures in the assets/translations folders. The canonical quizzes are in assets/translations/en. The quizzes are broken into several groupings. Make sure to align the numbering with the proper quiz section. There are 40 quizzes total in this curriculum, with the count starting at 0. <details> <summary>Here's the shape of a translation file:</summary> </details> After editing the translations, edit the index.js file in the translation folder to import all the files following the conventions in en. Edit the index.js file in assets/translations to import the new translated files. For example, if your translation JSON is in ex.json, make 'ex' the localization key, then enter it as shown below to import it <details> <summary>index.js</summary> </details> ## Run the Quiz App locally ### Prerequisites - A GitHub account - Node.js and Git ### Install & Setup 1. Create a repository from this template 1. Clone your new repository, and navigate to the quiz-app ```bash git clone https://github.com/your-github-organization/repo-name cd repo-name/quiz-app ``` 1. Install the npm packages & dependencies ```bash npm install ``` ### Build the app 1. To build the solution, run: ```bash npm run build ``` ### Start the App 1. To run the solution, run: ```bash npm run dev ``` ### [Optional] Linting 1. To ensure the code is linted, run: ```bash npm run lint ``` ## Deploy the Quiz-app to Azure ### Prerequisites - An Azure Subscription. Sign up for one for free here. _Cost Estimate to deploy this quiz-app: FREE_ [](https://portal.azure.com/#create/Microsoft.StaticApp) Once you are signed in on Azure through the link above, select a subscription and resource group then: - Static Web App details: Provide a name and select a hosting plan - GitHub Login: Set your deployment source as GitHub then log in and fill in the required fields on the form: - Organization โ Choose your organization. - Repository โ Select the Web Dev for Beginners curriculum repository. - Branch - Select a branch (main) - Build Presets: Azure Static Web Apps uses a detection algorithm to detect the framework used in your application. - App location - ./quiz-app - Api location - - Output location - dist - Deployment: Click 'Review + Create', then 'Create' Once deployed, a workflow file will be created in the .github directory of your repo. This workflow file contains instructions of events that will trigger a re-deployment of the app to Azure, for example, _a push on branch main_ etc. <details> <summary>Example Workflow File</summary> Hereโs an example of what the GitHub Actions workflow file might look like: name: Azure Static Web Apps CI/CD ``` on: push: branches: - main pull_request: types: [opened, synchronize, reopened, closed] branches: - main jobs: build_and_deploy_job: runs-on: ubuntu-latest name: Build and Deploy Job steps: - uses: actions/checkout@v2 - name: Build And Deploy id: builddeploy uses: Azure/static-web-apps-deploy@v1 with: azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }} repo_token: ${{ secrets.GITHUB_TOKEN }} action: "upload" app_location: "quiz-app" # App source code path api_location: ""API source code path optional output_location: "dist" #Built app content directory - optional ``` </details> - Post-Deployment: After deployment is complete, click on 'Go to Deployment' then 'View app in browser'. Once your GitHub Action (workflow) is executed successfully, refresh the live page to view your application.