Skip to content

Commit d23cfed

Browse files
Merge pull request #2 from QTechDevelopment/copilot/add-gui-and-website-for-users
Add interactive GUI and website for learning and testing JavaScript concepts
2 parents 31e3102 + a52fc17 commit d23cfed

File tree

8 files changed

+2607
-0
lines changed

8 files changed

+2607
-0
lines changed

QUICKSTART.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Quick Start Guide
2+
3+
## 🚀 Get Started in 3 Steps
4+
5+
### Step 1: Clone the Repository
6+
```bash
7+
git clone https://github.com/leonardomso/33-js-concepts.git
8+
cd 33-js-concepts
9+
```
10+
11+
### Step 2: Start the Server
12+
```bash
13+
node server.js
14+
```
15+
16+
Or using npm:
17+
```bash
18+
npm start
19+
```
20+
21+
### Step 3: Open Your Browser
22+
Navigate to: **http://localhost:3000**
23+
24+
That's it! 🎉
25+
26+
## 🎯 What You Can Do
27+
28+
- **Browse Concepts**: Click on any of the 33 JavaScript concepts in the sidebar
29+
- **Search**: Use the search box to quickly find specific topics
30+
- **Write Code**: Modify the example code in the editor
31+
- **Run Code**: Click the "▶ Run Code" button (or press Ctrl/Cmd + Enter)
32+
- **See Results**: View the console output below the editor
33+
- **Reset**: Click "↻ Reset" to restore the original example
34+
- **Clear**: Click "🗑️ Clear" to start with a blank editor
35+
36+
## 🎓 Learning Tips
37+
38+
1. Start with **Concept #1 (Call Stack)** and work your way through
39+
2. Modify the example code to experiment
40+
3. Try breaking things to understand error messages
41+
4. Create your own examples for each concept
42+
5. Use the console output to debug and learn
43+
44+
## ⚡ Keyboard Shortcuts
45+
46+
- `Ctrl/Cmd + Enter` - Run the code
47+
- `Tab` - Insert 4 spaces (in the editor)
48+
49+
## 🔧 Requirements
50+
51+
- Node.js installed on your system
52+
- A modern web browser (Chrome, Firefox, Safari, Edge)
53+
54+
## 📱 Mobile Friendly
55+
56+
The interface is fully responsive and works on mobile devices too!
57+
58+
## 🆘 Need Help?
59+
60+
- Check the [README.md](README.md) for more detailed information
61+
- Visit the [main repository](https://github.com/leonardomso/33-js-concepts) for resources
62+
- Open an issue if you encounter any problems
63+
64+
Happy Learning! 🎉

README.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
<div align="center">
1111
<p>
1212
<a href="#introduction">Introduction</a> •
13+
<a href="#interactive-learning">Interactive Learning</a> •
1314
<a href="#community">Community</a> •
1415
<a href="#table-of-contents">Table of Contents</a> •
1516
<a href="#license">License</a>
@@ -24,6 +25,53 @@
2425

2526
This repository was created with the intention of helping developers master their concepts in JavaScript. It is not a requirement, but a guide for future studies. It is based on an article written by Stephen Curtis and you can read it [here](https://medium.com/@stephenthecurt/33-fundamentals-every-javascript-developer-should-know-13dd720a90d1).
2627

28+
## 🎮 Interactive Learning
29+
30+
**NEW!** Now you can learn and test JavaScript concepts interactively! We've added a plug-and-play GUI that lets you:
31+
32+
- 📚 Browse all 33 JavaScript concepts in an organized list
33+
- 💻 Write and test code in a live editor
34+
- 🔍 Search for specific concepts
35+
- ▶️ Execute JavaScript code directly in your browser
36+
- 📋 See real-time console output
37+
- 🎯 Learn by doing with pre-loaded examples
38+
39+
### Getting Started with the Interactive GUI
40+
41+
1. **Clone this repository:**
42+
```bash
43+
git clone https://github.com/leonardomso/33-js-concepts.git
44+
cd 33-js-concepts
45+
```
46+
47+
2. **Start the local server:**
48+
```bash
49+
node server.js
50+
```
51+
52+
Or using npm:
53+
```bash
54+
npm start
55+
```
56+
57+
3. **Open your browser:**
58+
Navigate to `http://localhost:3000` to start learning!
59+
60+
### Features
61+
62+
-**Clean, Modern Interface**: Beautiful UI with smooth interactions
63+
- 🔥 **Live Code Editor**: Write and test JavaScript code instantly
64+
- 📱 **Responsive Design**: Works on desktop, tablet, and mobile
65+
- 🎨 **Syntax-Friendly**: Comfortable code editing experience
66+
- 🚀 **Zero Dependencies**: Pure HTML, CSS, and JavaScript - no build tools needed
67+
-**Instant Feedback**: See your code results immediately
68+
- 🎓 **Educational Examples**: Each concept comes with working code examples
69+
70+
### Keyboard Shortcuts
71+
72+
- `Ctrl/Cmd + Enter` - Run code
73+
- `Tab` - Insert 4 spaces in editor
74+
2775
## Community
2876

2977
Feel free to submit a PR by adding a link to your own recaps or reviews. If you want to translate the repo into your native language, please feel free to do so.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
},
1414
"homepage": "https://github.com/leonardomso/33#readme",
1515
"scripts": {
16+
"start": "node server.js",
17+
"dev": "node server.js",
1618
"test": "echo \"Error: no test specified\" && exit 1"
1719
},
1820
"repository": {

public/app.js

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
// State management
2+
let currentConcept = null;
3+
let originalCode = '';
4+
5+
// DOM elements
6+
const conceptsList = document.getElementById('conceptsList');
7+
const conceptTitle = document.getElementById('conceptTitle');
8+
const conceptDescription = document.getElementById('conceptDescription');
9+
const codeEditor = document.getElementById('codeEditor');
10+
const output = document.getElementById('output');
11+
const searchInput = document.getElementById('searchInput');
12+
const runBtn = document.getElementById('runBtn');
13+
const clearBtn = document.getElementById('clearBtn');
14+
const resetBtn = document.getElementById('resetBtn');
15+
const clearOutputBtn = document.getElementById('clearOutputBtn');
16+
17+
// Initialize the application
18+
function init() {
19+
renderConceptsList();
20+
setupEventListeners();
21+
22+
// Load first concept by default
23+
if (conceptsData.length > 0) {
24+
selectConcept(conceptsData[0].id);
25+
}
26+
}
27+
28+
// Render the concepts list
29+
function renderConceptsList(filter = '') {
30+
conceptsList.innerHTML = '';
31+
32+
const filteredConcepts = conceptsData.filter(concept =>
33+
concept.title.toLowerCase().includes(filter.toLowerCase())
34+
);
35+
36+
filteredConcepts.forEach(concept => {
37+
const li = document.createElement('li');
38+
li.innerHTML = `
39+
<span class="concept-number">${concept.id}</span>
40+
${concept.title}
41+
`;
42+
li.addEventListener('click', () => selectConcept(concept.id));
43+
44+
if (currentConcept && currentConcept.id === concept.id) {
45+
li.classList.add('active');
46+
}
47+
48+
conceptsList.appendChild(li);
49+
});
50+
}
51+
52+
// Select and load a concept
53+
function selectConcept(conceptId) {
54+
const concept = conceptsData.find(c => c.id === conceptId);
55+
if (!concept) return;
56+
57+
currentConcept = concept;
58+
originalCode = concept.example;
59+
60+
// Update UI
61+
conceptTitle.textContent = `${concept.id}. ${concept.title}`;
62+
conceptDescription.textContent = concept.description;
63+
codeEditor.value = concept.example;
64+
65+
// Clear output
66+
clearOutput();
67+
68+
// Update active state in list
69+
renderConceptsList(searchInput.value);
70+
71+
// Scroll to top of content
72+
window.scrollTo({ top: 0, behavior: 'smooth' });
73+
}
74+
75+
// Setup event listeners
76+
function setupEventListeners() {
77+
// Search functionality
78+
searchInput.addEventListener('input', (e) => {
79+
renderConceptsList(e.target.value);
80+
});
81+
82+
// Run code button
83+
runBtn.addEventListener('click', runCode);
84+
85+
// Clear editor button
86+
clearBtn.addEventListener('click', () => {
87+
codeEditor.value = '';
88+
clearOutput();
89+
});
90+
91+
// Reset to original code
92+
resetBtn.addEventListener('click', () => {
93+
if (originalCode) {
94+
codeEditor.value = originalCode;
95+
clearOutput();
96+
}
97+
});
98+
99+
// Clear output button
100+
clearOutputBtn.addEventListener('click', clearOutput);
101+
102+
// Keyboard shortcuts
103+
document.addEventListener('keydown', (e) => {
104+
// Ctrl/Cmd + Enter to run code
105+
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
106+
e.preventDefault();
107+
runCode();
108+
}
109+
});
110+
111+
// Allow Tab key in textarea
112+
codeEditor.addEventListener('keydown', (e) => {
113+
if (e.key === 'Tab') {
114+
e.preventDefault();
115+
const start = codeEditor.selectionStart;
116+
const end = codeEditor.selectionEnd;
117+
118+
codeEditor.value = codeEditor.value.substring(0, start) +
119+
' ' +
120+
codeEditor.value.substring(end);
121+
122+
codeEditor.selectionStart = codeEditor.selectionEnd = start + 4;
123+
}
124+
});
125+
}
126+
127+
// Run the code in the editor
128+
function runCode() {
129+
const code = codeEditor.value.trim();
130+
131+
if (!code) {
132+
addOutput('No code to execute', 'error');
133+
return;
134+
}
135+
136+
clearOutput();
137+
138+
// Capture console output
139+
const originalConsole = {
140+
log: console.log,
141+
error: console.error,
142+
warn: console.warn,
143+
info: console.info
144+
};
145+
146+
const logs = [];
147+
148+
// Override console methods
149+
console.log = (...args) => {
150+
const message = args.map(arg => formatValue(arg)).join(' ');
151+
logs.push({ type: 'log', message });
152+
originalConsole.log(...args);
153+
};
154+
155+
console.error = (...args) => {
156+
const message = args.map(arg => formatValue(arg)).join(' ');
157+
logs.push({ type: 'error', message });
158+
originalConsole.error(...args);
159+
};
160+
161+
console.warn = (...args) => {
162+
const message = args.map(arg => formatValue(arg)).join(' ');
163+
logs.push({ type: 'warn', message });
164+
originalConsole.warn(...args);
165+
};
166+
167+
console.info = (...args) => {
168+
const message = args.map(arg => formatValue(arg)).join(' ');
169+
logs.push({ type: 'info', message });
170+
originalConsole.info(...args);
171+
};
172+
173+
try {
174+
// Execute the code
175+
const result = eval(code);
176+
177+
// Display logs
178+
logs.forEach(log => {
179+
addOutput(log.message, log.type);
180+
});
181+
182+
// If there's a return value and no logs, display it
183+
if (result !== undefined && logs.length === 0) {
184+
addOutput(`=> ${formatValue(result)}`, 'success');
185+
}
186+
187+
// If no output at all
188+
if (logs.length === 0 && result === undefined) {
189+
addOutput('Code executed successfully (no output)', 'success');
190+
}
191+
192+
} catch (error) {
193+
addOutput(`Error: ${error.message}`, 'error');
194+
console.error(error);
195+
} finally {
196+
// Restore console methods
197+
console.log = originalConsole.log;
198+
console.error = originalConsole.error;
199+
console.warn = originalConsole.warn;
200+
console.info = originalConsole.info;
201+
}
202+
}
203+
204+
// Format values for display
205+
function formatValue(value) {
206+
if (value === null) return 'null';
207+
if (value === undefined) return 'undefined';
208+
if (typeof value === 'string') return value;
209+
if (typeof value === 'function') return value.toString();
210+
if (typeof value === 'symbol') return value.toString();
211+
212+
try {
213+
return JSON.stringify(value, null, 2);
214+
} catch (e) {
215+
return String(value);
216+
}
217+
}
218+
219+
// Add output to the console
220+
function addOutput(message, type = 'log') {
221+
const outputDiv = document.getElementById('output');
222+
223+
// Remove empty state
224+
outputDiv.classList.remove('empty');
225+
226+
const logEntry = document.createElement('div');
227+
logEntry.className = `log ${type}`;
228+
logEntry.textContent = message;
229+
230+
outputDiv.appendChild(logEntry);
231+
232+
// Auto-scroll to bottom
233+
outputDiv.scrollTop = outputDiv.scrollHeight;
234+
}
235+
236+
// Clear the output console
237+
function clearOutput() {
238+
output.innerHTML = '';
239+
output.classList.add('empty');
240+
output.textContent = 'Run your code to see output here...';
241+
}
242+
243+
// Initialize the application when DOM is ready
244+
if (document.readyState === 'loading') {
245+
document.addEventListener('DOMContentLoaded', init);
246+
} else {
247+
init();
248+
}

0 commit comments

Comments
 (0)