Upgraded typing game, chat backend, and main index files

pull/1516/head
Tanmay Srivastava 4 months ago
parent 2f458c2267
commit 6b4769e15b

@ -198,6 +198,9 @@ Compare some programming languages. What are some of the unique traits of JavaSc
Study a bit on the different languages available to the programmer. Try to write a line in one language, and then rewrite it in two others. What did you learn?
## Assignment
[Reading the Docs](assignment.md)
> Note: When selecting tools for your assignment, do not choose editors, browsers, or command line tools already listed above.

@ -1,11 +1,17 @@
# Reading the Docs
## Instructions
There are many tools that a web developer may need that are on the [MDN documentation for client-side tooling](https://developer.mozilla.org/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Overview). Select 3 tools not covered in the lesson, explain why a web developer would use it, and search for a tool that falls under this category and share its documentation. Do not use the same tool example on MDN docs.
There are many tools that a web developer may need that are listed on the [MDN documentation for client-side tooling](https://developer.mozilla.org/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Overview). Select **three tools** that are **not covered in this lesson** (excluding [list specific tools or refer to lesson content]), explain **why** a web developer would use each tool, and find a tool that fits each category. For each, share a link to its official documentation (not the example used on MDN).
**Format:**
- Tool name
- Why a web developer would use it (2-3 sentences)
- Link to documentation
**Length:**
- Each explanation should be 2-3 sentences.
## Rubric
Exemplary | Adequate | Needs Improvement
--- | --- | -- |
|Explained why web developer would use tool| Explained how, but not why developer would use tool| Did not mention how or why a developer would use tool |
Explained why web developer would use tool | Explained how, but not why developer would use tool | Did not mention how or why a developer would use tool |

@ -2,10 +2,10 @@
## Instructions
Imagine you are building a shopping cart. Write some documentation on the data types that you would need to complete your shopping experience. How did you arrive at your choices?
Imagine you are building a shopping cart. Write documentation on the data types you would need to complete your shopping experience. For each data type, explain how and why you would use it, and provide an example. The six JavaScript data types are: String, Number, Boolean, Null, Undefined, and Object.
## Rubric
Criteria | Exemplary | Adequate | Needs Improvement
--- | --- | --- | -- |
||The six data types are listed and explored in detail, documenting their use|Four datatypes are explored|Two data types are explored|
Data Types | All six data types are listed, explored in detail, and documented with examples | Four data types are explored with some explanation | Two data types are explored with minimal explanation |

@ -1,11 +1,39 @@
# Practice your HTML: Build a blog mockup
# HTML Practice Assignment: Build a Blog Mockup
## Objective
Design and hand-code the HTML structure for a personal blog homepage. This exercise will help you practice semantic HTML, layout planning, and code organization.
## Instructions
Imagine you are designing, or redesigning, your personal web site. Create a graphical mockup of your site, and then write down the HTML markup you would use to build out the various elements of the site. You can do this on paper, and scan it, or use software of your choice, just make sure to hand-code the HTML markup.
1. **Design Your Blog Mockup**
- Sketch a visual mockup of your blog homepage. Include key sections such as header, navigation, main content, sidebar, and footer.
- You may use paper and scan your sketch, or use digital tools (e.g., Figma, Adobe XD, Canva, or even PowerPoint).
2. **Identify HTML Elements**
- List the HTML elements you plan to use for each section (e.g., `<header>`, `<nav>`, `<main>`, `<article>`, `<aside>`, `<footer>`, `<section>`, `<h1>``<h6>`, `<p>`, `<img>`, `<ul>`, `<li>`, `<a>`, etc.).
3. **Write the HTML Markup**
- Hand-code the HTML for your mockup. Focus on semantic structure and best practices.
- Include at least 10 distinct HTML elements.
- Add comments to explain your choices and structure.
4. **Submit Your Work**
- Upload your sketch/mockup and your HTML file.
- Optionally, provide a brief reflection (23 sentences) on your design decisions.
## Rubric
| Criteria | Exemplary | Adequate | Needs Improvement |
| -------- | ----------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
| | A blog layout is represented visually with at least 10 elements of markup displayed | A blog layout is represented visually with around 5 elements of markup displayed | A blog layout is represented visually with at most 3 elements of markup displayed |
| Criteria | Exemplary | Adequate | Needs Improvement |
|------------------|--------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------|---------------------------------------------------------------------------------|
| Visual Mockup | Clear, detailed mockup with labeled sections and thoughtful layout | Basic mockup with some labeled sections | Minimal or unclear mockup; lacks section labels |
| HTML Elements | Uses 10+ semantic HTML elements; demonstrates understanding of structure and best practices | Uses 59 HTML elements; some semantic structure | Uses fewer than 5 elements; lacks semantic structure |
| Code Quality | Well-organized, readable code with comments; follows HTML standards | Mostly organized code; few comments | Disorganized code; lacks comments |
| Reflection | Insightful reflection on design choices and challenges | Basic reflection | No reflection or lacks relevance |
## Tips
- Use semantic HTML tags for better accessibility and SEO.
- Organize your code with indentation and comments.
- Refer to [MDN HTML Elements Reference](https://developer.mozilla.org/en-US/docs/Web/HTML/Element) for guidance.
- Think about how your layout could be extended or styled in future assignments.

@ -1,11 +1,27 @@
# CSS Refactoring
# CSS Refactoring Assignment
## Objective
Refactor the terrarium project to use **Flexbox** or **CSS Grid** for layout. Update the HTML and CSS as needed to achieve a modern, responsive design. You do not need to implement draggable elements—focus on layout and styling only.
## Instructions
Restyle the terrarium using either Flexbox or CSS Grid, and take screenshots to show that you have tested it on several browsers. You might need to change the markup so create a new version of the app with the art in place for your refactor. Don't worry about making the elements draggable; only refactor the HTML and CSS for now.
1. **Create a new version** of the terrarium app. Update the markup and CSS to use Flexbox or CSS Grid for layout.
2. **Ensure the art and elements are in place** as in the original version.
3. **Test your design** in at least two different browsers (e.g., Chrome, Firefox, Edge).
4. **Take screenshots** of your terrarium in each browser to demonstrate cross-browser compatibility.
5. **Submit** your updated code and screenshots.
## Rubric
| Criteria | Exemplary | Adequate | Needs Improvement |
| -------- | ----------------------------------------------------------------- | ----------------------------- | ------------------------------------ |
| | Present a completely restyled terrarium using Flexbox or CSS Grid | Restyle a few of the elements | Fail to restyle the terrarium at all |
| Criteria | Exemplary | Adequate | Needs Improvement |
|------------|--------------------------------------------------------------------------|---------------------------------------|----------------------------------------|
| Layout | Fully refactored using Flexbox or CSS Grid; visually appealing and responsive | Some elements refactored; partial use of Flexbox or Grid | Little or no use of Flexbox or Grid; layout unchanged |
| Cross-Browser | Screenshots provided for multiple browsers; consistent appearance | Screenshots for one browser; minor inconsistencies | No screenshots or major inconsistencies |
| Code Quality | Clean, well-organized HTML/CSS; clear comments | Some organization; few comments | Disorganized code; lacks comments |
## Tips
- Review [Flexbox](https://css-tricks.com/snippets/css/a-guide-to-flexbox/) and [CSS Grid](https://css-tricks.com/snippets/css/complete-guide-grid/) guides.
- Use browser developer tools to test responsiveness.
- Comment your code for clarity.

@ -1,76 +1,109 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Welcome to my Virtual Terrarium</title>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!-- import the webpage's stylesheet -->
<link rel="stylesheet" href="./style.css" />
<!-- import the webpage's JavaScript file -->
<script src="./script.js" defer></script>
</head>
<head>
<title>Welcome to my Virtual Terrarium</title>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!-- Font Awesome CDN for icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css" />
<!-- import the webpage's stylesheet -->
<link rel="stylesheet" href="./style.css" />
<!-- import the webpage's JavaScript file -->
<script src="./script.js" defer></script>
</head>
<body>
<div id="page">
<!-- Navigation Bar -->
<nav>
<ul>
<li><a href="#"><i class="fas fa-home"></i> Home</a></li>
<li><a href="#"><i class="fas fa-leaf"></i> Plants</a></li>
<li><a href="#"><i class="fas fa-seedling"></i> Care Tips</a></li>
<li><a href="#"><i class="fas fa-info-circle"></i> About</a></li>
<li><a href="#"><i class="fas fa-envelope"></i> Contact</a></li>
</ul>
</nav>
<body>
<div id="page">
<h1>My Terrarium</h1>
<header>
<h1><i class="fas fa-jar"></i> My Terrarium</h1>
<p>Welcome to your virtual plant paradise! <i class="fas fa-smile-beam"></i></p>
</header>
<div id="left-container" class="container">
<div class="plant-holder">
<img class="plant" alt="plant" id="plant1" src="./images/plant1.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant2" src="./images/plant2.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant3" src="./images/plant3.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant4" src="./images/plant4.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant5" src="./images/plant5.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant6" src="./images/plant6.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant7" src="./images/plant7.png" />
</div>
</div>
<div id="right-container" class="container">
<div class="plant-holder">
<img class="plant" alt="plant" id="plant8" src="./images/plant8.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant9" src="./images/plant9.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant10" src="./images/plant10.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant11" src="./images/plant11.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant12" src="./images/plant12.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant13" src="./images/plant13.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant14" src="./images/plant14.png" />
</div>
</div>
<main>
<section id="plant-selection">
<div id="left-container" class="container">
<!-- Left-side plants -->
<div class="plant-holder">
<img class="plant" alt="plant" id="plant1" src="./images/plant1.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant2" src="./images/plant2.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant3" src="./images/plant3.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant4" src="./images/plant4.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant5" src="./images/plant5.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant6" src="./images/plant6.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant7" src="./images/plant7.png" />
</div>
</div>
<div id="right-container" class="container">
<!-- Right-side plants -->
<div class="plant-holder">
<img class="plant" alt="plant" id="plant8" src="./images/plant8.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant9" src="./images/plant9.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant10" src="./images/plant10.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant11" src="./images/plant11.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant12" src="./images/plant12.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant13" src="./images/plant13.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant14" src="./images/plant14.png" />
</div>
</div>
</section>
<div id="terrarium">
<div class="jar-top"></div>
<div class="jar-walls">
<div class="jar-glossy-long"></div>
<div class="jar-glossy-short"></div>
</div>
<div class="dirt"></div>
<div class="jar-bottom"></div>
</div>
</div>
</body>
</html>
<section id="terrarium-section">
<div id="terrarium">
<div class="jar-top"></div>
<div class="jar-walls">
<div class="jar-glossy-long"></div>
<div class="jar-glossy-short"></div>
</div>
<div class="dirt"></div>
<div class="jar-bottom"></div>
</div>
</section>
</main>
<footer>
<p>
<i class="fas fa-copyright"></i> 2025 My Terrarium. All rights reserved.
&nbsp;|&nbsp;
<a href="https://github.com/" target="_blank"><i class="fab fa-github"></i> GitHub</a>
&nbsp;|&nbsp;
<a href="mailto:info@myterrarium.com"><i class="fas fa-envelope"></i> Email</a>
</p>
</footer>
</div>
</body>
</html>

@ -103,3 +103,108 @@ h1 {
bottom: 45%;
left: 5%;
}
/* ...existing code... */
/* Navigation Bar Styles */
nav {
background: linear-gradient(90deg, #d1e1df 60%, #b2c9c6 100%);
box-shadow: 0 2px 8px rgba(58,36,29,0.08);
padding: 0.5rem 0;
margin-bottom: 1rem;
}
nav ul {
display: flex;
justify-content: center;
align-items: center;
gap: 2rem;
list-style: none;
margin: 0;
padding: 0;
}
nav li a {
color: #3a241d;
text-decoration: none;
font-size: 1.1rem;
font-weight: 600;
transition: color 0.2s;
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 1rem;
border-radius: 0.5rem;
}
nav li a:hover, nav li a:focus {
background: #e6f2f0;
color: #2d7c6f;
box-shadow: 0 2px 8px rgba(45,124,111,0.08);
}
/* Font Awesome Icon Styles */
.fas, .fab {
color: #2d7c6f;
font-size: 1.2em;
vertical-align: middle;
transition: color 0.2s;
}
nav li a:hover .fas,
nav li a:hover .fab {
color: #3a241d;
}
header h1 .fas {
color: #3a241d;
margin-right: 0.5rem;
font-size: 1.3em;
}
header p .fas {
color: #2d7c6f;
margin-left: 0.3rem;
}
footer {
background: #d1e1df;
text-align: center;
padding: 1rem 0;
margin-top: 2rem;
font-size: 1rem;
color: #3a241d;
border-top: 2px solid #b2c9c6;
}
footer a {
color: #2d7c6f;
text-decoration: none;
margin: 0 0.5rem;
font-weight: 600;
transition: color 0.2s;
}
footer a:hover, footer a:focus {
color: #3a241d;
text-decoration: underline;
}
footer .fab, footer .fas {
margin-right: 0.3rem;
font-size: 1.1em;
vertical-align: middle;
}
/* Responsive adjustments for navigation */
@media (max-width: 700px) {
nav ul {
flex-direction: column;
gap: 1rem;
}
nav li a {
font-size: 1rem;
padding: 0.5rem 0.5rem;
}
}
/* ...existing code... */

@ -6,3 +6,151 @@
background-color: lightcoral;
border-color: red;
}
/* Global Styles */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: "Segoe UI", Roboto, Arial, sans-serif;
}
body {
background: linear-gradient(135deg, #f9fafb, #e3f2ef);
color: #2d3436;
display: flex;
flex-direction: column;
align-items: center;
padding: 2rem;
min-height: 100vh;
}
/* Title */
h1 {
font-size: 2.5rem;
font-weight: 700;
color: #2d7c6f;
display: flex;
align-items: center;
gap: 0.6rem;
margin-bottom: 1rem;
text-shadow: 1px 2px 4px rgba(0, 0, 0, 0.1);
}
/* Info */
div i.fa-info-circle {
color: #00796b;
margin-right: 0.4rem;
}
div {
font-size: 1rem;
margin-bottom: 1rem;
text-align: center;
max-width: 500px;
color: #555;
}
/* Quote Display */
#quote {
font-size: 1.2rem;
font-weight: 500;
color: #333;
margin: 1rem auto;
padding: 1rem;
border-left: 4px solid #2d7c6f;
background: #f1fdfb;
border-radius: 8px;
max-width: 600px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
}
/* Message */
#message {
font-size: 1.1rem;
font-weight: 600;
margin: 0.8rem;
color: #ff7043;
text-align: center;
}
/* Input Box */
input#typed-value {
padding: 0.8rem 1rem;
font-size: 1rem;
width: 280px;
border: 2px solid #2d7c6f;
border-radius: 12px;
outline: none;
transition: 0.3s ease;
margin-right: 0.4rem;
}
input#typed-value:focus {
border-color: #00796b;
box-shadow: 0 0 8px rgba(0, 121, 107, 0.3);
}
/* Pen Icon */
.fa-pen {
color: #2d7c6f;
font-size: 1.2rem;
vertical-align: middle;
}
/* Start Button */
button#start {
background: linear-gradient(135deg, #2d7c6f, #26a69a);
color: white;
font-size: 1rem;
padding: 0.8rem 1.6rem;
border: none;
border-radius: 12px;
margin-top: 1.5rem;
cursor: pointer;
transition: transform 0.2s ease, box-shadow 0.3s ease;
}
button#start:hover {
transform: translateY(-2px);
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);
}
button#start i {
margin-right: 0.4rem;
}
/* Date & Time */
#datetime {
margin-top: 1.5rem;
font-size: 1rem;
font-weight: 600;
color: #37474f;
display: flex;
align-items: center;
justify-content: center;
gap: 0.6rem;
}
#datetime .fas {
color: #2d7c6f;
}
/* Responsive */
@media (max-width: 600px) {
h1 {
font-size: 2rem;
}
#quote {
font-size: 1rem;
padding: 0.8rem;
}
input#typed-value {
width: 200px;
}
button#start {
width: 100%;
}
}

@ -1,23 +1,110 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Typing</title>
<link rel="stylesheet" href="./index.css" />
</head>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Typing</title>
<!-- Font Awesome CDN for icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css" />
<link rel="stylesheet" href="./index.css" />
<style>
#datetime {
text-align: center;
margin-bottom: 1rem;
font-size: 1.1rem;
color: #2d7c6f;
}
#datetime .fas {
margin-right: 0.3rem;
color: #3a241d;
font-size: 1.1em;
vertical-align: middle;
}
/* Hide label visually but keep for accessibility */
.hidden-label {
position: absolute;
left: -10000px;
top: auto;
width: 1px;
height: 1px;
overflow: hidden;
}
/* ...existing code... */
</style>
</head>
<body>
<h1>Practice your typing</h1>
<div>Click start to have a quote displayed. Type the quote as fast as you can!</div>
<body>
<h1>
<i class="fas fa-keyboard"></i> Practice your typing
</h1>
<div>
<i class="fas fa-info-circle"></i>
Click start to have a quote displayed. Type the quote as fast as you can!
</div>
<p id="quote"></p>
<p id="message"></p>
<div>
<input type="text" aria-label="current word" id="typed-value" />
</div>
<div>
<button id="start" type="button">Start</button>
</div>
<script src="./index.js"></script>
</body>
<p id="quote"></p>
<p id="message"></p>
<div>
<label for="typed-value" class="hidden-label">Current word</label>
<input type="text" aria-label="current word" id="typed-value" placeholder="Type here..." />
<i class="fas fa-pen"></i>
</div>
<div>
<button id="start" type="button">
<i class="fas fa-play"></i> Start
</button>
</div>
<script src="./index.js"></script>
<!-- ...existing code... -->
<body>
<h1>
<i class="fas fa-keyboard"></i> Practice your typing
</h1>
<div id="datetime">
<i class="fas fa-calendar-day"></i>
<span id="current-date"></span>
&nbsp;|&nbsp;
<i class="fas fa-clock"></i>
<span id="current-time"></span>
</div>
<!-- ...existing code... -->
</body>
<!-- ...existing code... -->
<script>
// ...existing code...
function updateDateTime() {
const now = new Date();
// Format date as DD/MM/YYYY
const day = String(now.getDate()).padStart(2, '0');
const month = String(now.getMonth() + 1).padStart(2, '0');
const year = now.getFullYear();
const formattedDate = `${day}/${month}/${year}`;
// Format time as HH:MM AM/PM (12-hour)
let hours = now.getHours();
const minutes = String(now.getMinutes()).padStart(2, '0');
const ampm = hours >= 12 ? 'PM' : 'AM';
hours = hours % 12;
hours = hours ? hours : 12; // 0 should be 12
const formattedTime = `${hours}:${minutes} ${ampm}`;
document.getElementById('current-date').textContent = formattedDate;
document.getElementById('current-time').textContent = formattedTime;
}
// Initial call and update every minute
updateDateTime();
setInterval(updateDateTime, 60000);
// ...existing code...
</script>
</body>
</html>

@ -1,94 +1,94 @@
// Typing Game - Modern ES6+ Version
// Quotes pool
const quotes = [
'When you have eliminated the impossible, whatever remains, however improbable, must be the truth.',
'There is nothing more deceptive than an obvious fact.',
'I ought to know by this time that when a fact appears to be opposed to a long train of deductions it invariably proves to be capable of bearing some other interpretation.',
'I never make exceptions. An exception disproves the rule.',
'What one man can invent another can discover.',
'Nothing clears up a case so much as stating it to another person.',
'Education never ends, Watson. It is a series of lessons, with the greatest for the last.',
"When you have eliminated the impossible, whatever remains, however improbable, must be the truth.",
"There is nothing more deceptive than an obvious fact.",
"I ought to know by this time that when a fact appears to be opposed to a long train of deductions it invariably proves to be capable of bearing some other interpretation.",
"I never make exceptions. An exception disproves the rule.",
"What one man can invent another can discover.",
"Nothing clears up a case so much as stating it to another person.",
"Education never ends, Watson. It is a series of lessons, with the greatest for the last."
];
// array for storing the words of the current challenge
// State
let words = [];
// stores the index of the word the player is currently typing
let wordIndex = 0;
// default value for startTime (will be set on start)
let startTime = Date.now();
let startTime = 0;
// UI Elements
const quoteElement = document.querySelector("#quote");
const messageElement = document.querySelector("#message");
const typedValueElement = document.querySelector("#typed-value");
const startButton = document.querySelector("#start");
// Messages system
const messages = {
success: (seconds) => `🎉 Congratulations! You finished in ${seconds.toFixed(2)} seconds.`,
error: "⚠️ Oops! There's a mistake.",
start: "⌨️ Start typing to begin the game."
};
// grab UI items
const quoteElement = document.getElementById('quote');
const messageElement = document.getElementById('message')
const typedValueElement = document.getElementById('typed-value');
// Utility: pick random quote
const getRandomQuote = () => quotes[Math.floor(Math.random() * quotes.length)];
document.getElementById('start').addEventListener('click', function () {
// get a quote
const quoteIndex = Math.floor(Math.random() * quotes.length);
const quote = quotes[quoteIndex];
// Put the quote into an array of words
words = quote.split(' ');
// reset the word index for tracking
wordIndex = 0;
// Utility: render quote as spans
const renderQuote = (quote) => {
quoteElement.innerHTML = quote
.split(" ")
.map((word, i) => `<span ${i === 0 ? 'class="highlight"' : ""}>${word}</span>`)
.join(" ");
};
// UI updates
// Create an array of span elements so we can set a class
const spanWords = words.map(function (word) { return `<span>${word} </span>` });
// Convert into string and set as innerHTML on quote display
quoteElement.innerHTML = spanWords.join('');
// Highlight the first word
quoteElement.childNodes[0].className = 'highlight';
// Clear any prior messages
messageElement.innerText = '';
// Utility: highlight current word
const highlightWord = (index) => {
[...quoteElement.children].forEach((el, i) => {
el.classList.toggle("highlight", i === index);
});
};
// Setup the textbox
// Clear the textbox
typedValueElement.value = '';
// set focus
typedValueElement.focus();
// set the event handler
// Game start
const startGame = () => {
const quote = getRandomQuote();
words = quote.split(" ");
wordIndex = 0;
renderQuote(quote);
// Start the timer
startTime = new Date().getTime();
});
messageElement.textContent = messages.start;
typedValueElement.value = "";
typedValueElement.focus();
typedValueElement.addEventListener('input', (e) => {
// Get the current word
const currentWord = words[wordIndex];
// get the current value
const typedValue = typedValueElement.value;
startTime = Date.now();
};
if (typedValue === currentWord && wordIndex === words.length - 1) {
// end of quote
// Display success
const elapsedTime = new Date().getTime() - startTime;
const message = `CONGRATULATIONS! You finished in ${elapsedTime / 1000} seconds.`;
messageElement.innerText = message;
} else if (typedValue.endsWith(' ') && typedValue.trim() === currentWord) {
// end of word
// clear the typedValueElement for the new word
typedValueElement.value = '';
// move to the next word
wordIndex++;
// reset the class name for all elements in quote
for (const wordElement of quoteElement.childNodes) {
wordElement.className = '';
}
// highlight the new word
quoteElement.childNodes[wordIndex].className = 'highlight';
} else if (currentWord.startsWith(typedValue)) {
// currently correct
// highlight the next word
typedValueElement.className = '';
} else {
// error state
typedValueElement.className = 'error';
}
});
// Typing logic
const handleTyping = () => {
const currentWord = words[wordIndex];
const typedValue = typedValueElement.value;
// Add this at the end of the file
const messages = {
success: "CONGRATULATIONS! You finished in {seconds} seconds.",
error: "Oops! There's a mistake.",
start: "Start typing to begin the game."
if (typedValue === currentWord && wordIndex === words.length - 1) {
// Game finished
const elapsedTime = (Date.now() - startTime) / 1000;
messageElement.textContent = messages.success(elapsedTime);
typedValueElement.disabled = true;
} else if (typedValue.endsWith(" ") && typedValue.trim() === currentWord) {
// Word completed
typedValueElement.value = "";
wordIndex++;
highlightWord(wordIndex);
} else if (currentWord.startsWith(typedValue)) {
// Correct so far
typedValueElement.classList.remove("error");
} else {
// Error
typedValueElement.classList.add("error");
messageElement.textContent = messages.error;
}
};
export default messages;
// Event Listeners
startButton.addEventListener("click", startGame);
typedValueElement.addEventListener("input", handleTyping);
// Default state
messageElement.textContent = "👉 Click Start to begin!";

@ -1,30 +1,56 @@
# api
from flask import Flask, request, jsonify
from llm import call_llm
from flask_cors import CORS
import logging
app = Flask(__name__)
CORS(app) # * example.com
# Configure CORS (allow all origins for development; restrict in production)
CORS(app, resources={r"/*": {"origins": "*"}})
# Set up logging
logging.basicConfig(level=logging.INFO)
@app.route("/", methods=["GET"])
def index():
return "Welcome to this lesson"
"""Root endpoint for API status."""
return jsonify({
"status": "ok",
"message": "Welcome to the Chat Project API"
})
@app.route("/health", methods=["GET"])
def health():
"""Health check endpoint."""
return jsonify({"status": "healthy"}), 200
@app.route("/test", methods=["GET"])
def test():
return "Test"
"""Simple test endpoint."""
return jsonify({"result": "Test successful"}), 200
@app.route("/hello", methods=["POST"])
def hello():
# get message from request body { "message": "do this taks for me" }
data = request.get_json()
message = data.get("message", "")
"""
Chat endpoint.
Expects JSON: { "message": "your message" }
Returns: { "response": "LLM response" }
"""
try:
data = request.get_json(force=True)
message = data.get("message", "").strip()
if not message:
logging.warning("No message provided in request.")
return jsonify({"error": "No message provided."}), 400
response = call_llm(message, "You are a helpful assistant.")
return jsonify({
"response": response
})
logging.info(f"Received message: {message}")
response = call_llm(message, "You are a helpful assistant.")
return jsonify({"response": response}), 200
except Exception as e:
logging.error(f"Error in /hello endpoint: {e}")
return jsonify({"error": "Internal server error."}), 500
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)
# Run the app with debug mode for development
app.run(host="0.0.0.0", port=5000, debug=True)

@ -1,29 +1,53 @@
import os
import logging
from openai import OpenAI
# To authenticate with the model you will need to generate a personal access token (PAT) in your GitHub settings.
# Create your PAT token by following instructions here: https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens
# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Environment variable check for GitHub token
GITHUB_TOKEN = os.environ.get("GITHUB_TOKEN")
if not GITHUB_TOKEN:
logger.error("GITHUB_TOKEN environment variable not set.")
raise EnvironmentError("GITHUB_TOKEN environment variable not set.")
# Model and endpoint configuration
MODEL_NAME = os.environ.get("LLM_MODEL", "openai/gpt-4o-mini")
BASE_URL = os.environ.get("LLM_BASE_URL", "https://models.github.ai/inference")
# Initialize OpenAI client
client = OpenAI(
base_url="https://models.github.ai/inference",
api_key=os.environ["GITHUB_TOKEN"],
base_url=BASE_URL,
api_key=GITHUB_TOKEN,
)
def call_llm(prompt: str, system_message: str):
response = 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
)
def call_llm(prompt: str, system_message: str, temperature: float = 1.0, max_tokens: int = 4096, top_p: float = 1.0) -> str:
"""
Calls the LLM with the given prompt and system message.
Returns the model's response as a string.
"""
try:
logger.info(f"Calling LLM model '{MODEL_NAME}' with prompt: {prompt}")
response = client.chat.completions.create(
messages=[
{"role": "system", "content": system_message},
{"role": "user", "content": prompt}
],
model=MODEL_NAME,
temperature=temperature,
max_tokens=max_tokens,
top_p=top_p
)
content = response.choices[0].message.content
logger.info("LLM response received successfully.")
return content
except Exception as e:
logger.error(f"Error calling LLM: {e}")
return "Sorry, there was an error processing your request."
return response.choices[0].message.content
# Example usage (for testing)
if __name__ == "__main__":
test_prompt = "Hello, how are you?"
test_system = "You are a friendly assistant."
print(call_llm(test_prompt, test_system))

@ -1,3 +1,108 @@
.app-nav ul li {
width: max-content;
}
body {
font-family: 'Inter', Arial, sans-serif;
}
/* Body and Typography */
body {
font-family: 'Inter', Arial, sans-serif;
background: #f7f8fa;
color: #222;
margin: 0;
padding: 0;
}
/* Header Styling */
h1, h2, h3 {
font-weight: 700;
margin-top: 1.5em;
margin-bottom: 0.5em;
letter-spacing: 0.02em;
}
h1 i, h2 i, h3 i {
color: #0078d4;
margin-right: 0.5em;
}
/* Navigation Bar */
.app-nav {
background: #fff;
box-shadow: 0 2px 8px rgba(0,0,0,0.04);
padding: 1em 2em;
}
.app-nav ul {
list-style: none;
display: flex;
gap: 2em;
margin: 0;
padding: 0;
}
.app-nav ul li {
width: max-content;
}
.app-nav a {
text-decoration: none;
color: #0078d4;
font-weight: 500;
transition: color 0.2s;
}
.app-nav a:hover {
color: #005fa3;
}
/* Card Component */
.card {
background: #fff;
border-radius: 12px;
box-shadow: 0 4px 16px rgba(0,0,0,0.07);
padding: 2em;
margin: 2em 0;
transition: box-shadow 0.2s;
}
.card:hover {
box-shadow: 0 8px 32px rgba(0,0,0,0.12);
}
/* Button Styling */
.button {
background: #0078d4;
color: #fff;
border: none;
border-radius: 6px;
padding: 0.75em 1.5em;
font-size: 1em;
font-weight: 600;
cursor: pointer;
transition: background 0.2s;
display: inline-flex;
align-items: center;
gap: 0.5em;
}
.button:hover {
background: #005fa3;
}
/* Font Awesome Icon Styling */
.fa {
margin-right: 0.5em;
vertical-align: middle;
}
/* Responsive Layout */
@media (max-width: 600px) {
.app-nav ul {
flex-direction: column;
gap: 1em;
}
.card {
padding: 1em;
}
}

@ -1,4 +1,5 @@
<!DOCTYPE html>
<!-- Add Google Fonts -->
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet"><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
@ -13,7 +14,8 @@
<body>
<div id="app"></div>
<script type="text/javascript">
(function(c,l,a,r,i,t,y){
(function(c,l,a,r,i,t,y){ <!-- Add Font Awesome -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css">
c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)};
t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i;
y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y);

Loading…
Cancel
Save