Skip to content

Commit 2ef0d9c

Browse files
committed
Enhance security, global distraction tracking, and user profiles
1 parent b57623a commit 2ef0d9c

8 files changed

Lines changed: 1024 additions & 92 deletions

File tree

README.md

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,19 @@
22

33
Welcome to Subway Scholars, a gamified productivity application that transforms your study schedule into an engaging runner experience.
44

5-
Take control of your focus time by automatically generating dynamic study sprints using AI, and conquer distractions with an OS-level blocker that forces you to answer academic quizzes if you try to open social media.
5+
Take control of your focus time by automatically generating dynamic study sprints using AI, and conquer distractions with an OS-level blocker that forces you to answer academic quizzes if you try to slack off.
66

77
---
88

99
## Features
1010

1111
- **AI-Powered Missions**: Upload your `.ics` calendar file, course syllabus, or simply type your study goals. The AI integration automatically maps out your available time and generates focused study sprints.
1212
- **Gamified Interface**: An arcade-inspired aesthetic with active mission banners and animated timers.
13-
- **The Guard Blocker**: When an active mission is running, the Python backend continuously monitors your system for distractions (like YouTube or Instagram). If you open them, an un-closable overlay pops up on your screen.
13+
- **Universal Global Blocker**: When an active mission is running, the Python backend continuously monitors *all* of your active OS windows. If you switch to an application (like YouTube or a video game), the AI evaluates if it is relevant to your sprint. If it determines you are getting distracted for more than 15 seconds, an un-closable overlay pops up on your screen.
1414
- **AI Penalty Quizzes**: To unlock your screen from the blocker, you must correctly answer 3 AI-generated multiple-choice questions tailored to the specific subject you are currently studying.
15-
- **Emergency Exit**: If you have a legitimate emergency, you can use the Emergency Exit button in the web app. It requires you to type a randomly generated safety phrase to break your focus streak and unlock the system immediately.
15+
- **Sprint Management**: You can pause and resume your active sprints (e.g., for a 5, 10, 15, or 20-minute break), during which time the global blocker will safely disengage. Sprints will also automatically end upon completion, displaying a congratulatory message and migrating to your historical "Completed Quests" list.
16+
- **User Profiles**: Create an account to log in and securely track your Total Points, completed sprints, and study history.
17+
- **Emergency Exit**: If you have a legitimate emergency while blocked, you can use the Emergency Exit button in the web app. It requires you to type a randomly generated safety phrase to break your focus streak and unlock the system immediately.
1618

1719
---
1820

@@ -39,7 +41,7 @@ To run Subway Scholars locally, you'll need:
3941

4042
2. **Install all Python dependencies:**
4143
```bash
42-
pip install fastapi uvicorn pydantic python-multipart pygetwindow icalendar python-dotenv groq
44+
pip install fastapi uvicorn pydantic python-multipart pygetwindow icalendar python-dotenv groq bcrypt
4345
```
4446

4547
3. **Configure your Environment:**
@@ -63,15 +65,10 @@ python main.py
6365
Leave this terminal window open. The Python guard is now monitoring for API requests and system distractions.
6466

6567
### 2. Launch the Web App
66-
Open your file explorer, navigate to `Subway-Scholars/app/`, and double-click `index.html` to open it in your browser.
68+
Open your file explorer, navigate to `Subway-Scholars/app/`, and double-click `auth.html` to register an account or log in. From there, you will be redirected to the main hub.
6769

6870
### 3. Start a Mission
6971
- Type what you want to study into the Mission Board or upload a calendar syllabus.
7072
- Click **Generate Sprints**.
7173
- Click on an active sprint card to begin your focus timer.
72-
73-
---
74-
75-
## Notes on the OS Blocker
76-
- By default, the blocker looks for window titles containing "youtube" or "instagram". You can add more restricted apps to the `BLACKLIST` variable at the top of `bouncer/main.py`.
77-
- The pop-up quiz will pause the OS monitoring for 15 seconds after a successful unlock, giving you enough time to close the distracting window before it catches you again.
74+
- The Global Blocker will now continually assess whatever application you have focused against your active topic.

app/auth.html

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="UTF-8">
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
7+
<title>Subway Scholars - Authentication</title>
8+
<link rel="stylesheet" href="./style.css">
9+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
10+
<style>
11+
.auth-container {
12+
display: flex;
13+
justify-content: center;
14+
align-items: center;
15+
height: calc(100vh - 120px);
16+
}
17+
18+
.auth-panel {
19+
width: 400px;
20+
text-align: center;
21+
}
22+
23+
.auth-panel input {
24+
width: 100%;
25+
padding: 12px;
26+
margin: 10px 0;
27+
background: rgba(255, 255, 255, 0.05);
28+
border: 1px solid rgba(255, 255, 255, 0.2);
29+
color: #fff;
30+
border-radius: 8px;
31+
}
32+
33+
.auth-panel input:focus {
34+
outline: none;
35+
border-color: #00D4FF;
36+
box-shadow: 0 0 10px rgba(0, 212, 255, 0.5);
37+
}
38+
39+
.auth-switch {
40+
margin-top: 15px;
41+
font-size: 0.9em;
42+
color: #aaa;
43+
cursor: pointer;
44+
}
45+
46+
.auth-switch:hover {
47+
color: #00D4FF;
48+
}
49+
50+
.error-msg {
51+
color: #ff3b3b;
52+
margin-bottom: 10px;
53+
font-size: 0.9em;
54+
display: none;
55+
}
56+
</style>
57+
</head>
58+
59+
<body>
60+
<div class="bg-lines"></div>
61+
62+
<header style="justify-content: center;">
63+
<h1 class="logo">Subway <span>Scholars</span></h1>
64+
</header>
65+
66+
<main class="auth-container">
67+
<div class="glass-panel auth-panel" id="loginBox">
68+
<h2><i class="fa-solid fa-right-to-bracket"></i> Login</h2>
69+
<div id="loginError" class="error-msg"></div>
70+
<input type="text" id="loginUsername" placeholder="Username" autocomplete="off">
71+
<input type="password" id="loginPassword" placeholder="Password">
72+
<button class="btn-primary" id="btnLogin" style="width: 100%; margin-top: 15px;">Login</button>
73+
<div class="auth-switch" id="showRegister">Need an account? Register</div>
74+
</div>
75+
76+
<div class="glass-panel auth-panel" id="registerBox" style="display: none;">
77+
<h2><i class="fa-solid fa-user-plus"></i> Register</h2>
78+
<div id="registerError" class="error-msg"></div>
79+
<input type="text" id="regUsername" placeholder="Username" autocomplete="off">
80+
<input type="password" id="regPassword" placeholder="Password">
81+
<input type="password" id="regConfirm" placeholder="Confirm Password">
82+
<button class="btn-primary" id="btnRegister" style="width: 100%; margin-top: 15px;">Register</button>
83+
<div class="auth-switch" id="showLogin">Already have an account? Login</div>
84+
</div>
85+
</main>
86+
87+
<script>
88+
const API_BASE = window.location.origin.includes("127.0.0.1:8000") || window.location.origin.includes("localhost:8000") ? "" : "http://127.0.0.1:8000";
89+
90+
document.getElementById("showRegister").onclick = () => {
91+
document.getElementById("loginBox").style.display = "none";
92+
document.getElementById("registerBox").style.display = "block";
93+
};
94+
95+
document.getElementById("showLogin").onclick = () => {
96+
document.getElementById("registerBox").style.display = "none";
97+
document.getElementById("loginBox").style.display = "block";
98+
};
99+
100+
async function handleAuth(url, body, errorElementId) {
101+
const errEl = document.getElementById(errorElementId);
102+
errEl.style.display = "none";
103+
try {
104+
const res = await fetch(`${API_BASE}${url}`, {
105+
method: "POST",
106+
headers: { "Content-Type": "application/json" },
107+
body: JSON.stringify(body)
108+
});
109+
const data = await res.json();
110+
if (res.ok) {
111+
localStorage.setItem("token", data.access_token);
112+
localStorage.setItem("username", data.username);
113+
window.location.href = "index.html";
114+
} else {
115+
errEl.textContent = data.detail || "Authentication failed.";
116+
errEl.style.display = "block";
117+
}
118+
} catch (e) {
119+
errEl.textContent = "Network error.";
120+
errEl.style.display = "block";
121+
}
122+
}
123+
124+
document.getElementById("btnLogin").onclick = () => {
125+
const u = document.getElementById("loginUsername").value.trim();
126+
const p = document.getElementById("loginPassword").value;
127+
if (!u || !p) return;
128+
handleAuth("/api/auth/login", { username: u, password: p }, "loginError");
129+
};
130+
131+
document.getElementById("btnRegister").onclick = () => {
132+
const u = document.getElementById("regUsername").value.trim();
133+
const p = document.getElementById("regPassword").value;
134+
const c = document.getElementById("regConfirm").value;
135+
if (!u || !p) return;
136+
if (p !== c) {
137+
const err = document.getElementById("registerError");
138+
err.textContent = "Passwords do not match.";
139+
err.style.display = "block";
140+
return;
141+
}
142+
handleAuth("/api/auth/register", { username: u, password: p }, "registerError");
143+
};
144+
</script>
145+
</body>
146+
147+
</html>

app/index.html

Lines changed: 63 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,59 @@
1515
<div class="bg-lines"></div>
1616

1717
<header>
18-
<h1 class="logo">Subway <span>Scholars</span></h1>
19-
<div class="subtitle">Hack Your Focus. Run Your Sprints.</div>
18+
<div style="flex: 1;"></div>
19+
<div style="flex: 2; text-align: center;">
20+
<h1 class="logo">Subway <span>Scholars</span></h1>
21+
<div class="subtitle">Hack Your Focus. Run Your Sprints.</div>
22+
</div>
23+
<div style="flex: 1; text-align: right;">
24+
</div>
25+
26+
<div class="profile-corner">
27+
<button class="btn-primary" onclick="window.location.href='profile.html'"><i class="fa-solid fa-user"></i>
28+
Profile</button>
29+
</div>
2030

2131
<div id="activeMissionBanner" class="active-mission-banner" style="display: none;">
2232
<div class="active-info">
2333
<span class="pulse-dot"></span>
2434
<strong>Active Quest:</strong> <span id="activeSprintTopic">German</span>
2535
</div>
26-
<button id="btnEndSession" class="btn-end-session"><i class="fa-solid fa-flag-checkered"></i> End Session</button>
27-
<button id="btnNotifications" class="btn-notif" style="display: none;">
28-
<i class="fa-solid fa-bell"></i>
29-
<span id="notifBadge" class="notif-badge" style="display: none;">0</span>
30-
</button>
31-
<button id="btnEmergencyExit" class="btn-danger"><i class="fa-solid fa-triangle-exclamation"></i> Emergency
32-
Exit</button>
33-
</div>
36+
<div class="score-display" id="scoreDisplayContainer"
37+
style="display: none; margin: 0 15px; font-weight: bold; color: #00E676;">
38+
<i class="fa-solid fa-bolt"></i> Score: <span id="liveScore">0</span> (<span
39+
id="scoreMultiplier">1.0</span>x)
40+
</div>
41+
42+
<div class="banner-actions">
43+
<div class="pause-dropdown">
44+
<button class="btn-secondary dropbtn" id="btnPauseSession"><i class="fa-solid fa-pause"></i>
45+
Pause</button>
46+
<div class="pause-content">
47+
<a href="#" class="pause-option" data-mins="5">5 mins</a>
48+
<a href="#" class="pause-option" data-mins="10">10 mins</a>
49+
<a href="#" class="pause-option" data-mins="15">15 mins</a>
50+
<a href="#" class="pause-option" data-mins="20">20 mins</a>
51+
</div>
52+
</div>
53+
54+
<div id="pauseStatusDisplay"
55+
style="display: none; color: #FF9800; font-weight: bold; margin-right: 15px; align-items: center; gap: 5px;">
56+
<i class="fa-solid fa-clock"></i> Paused: <span id="pauseTimerText">00:00</span>
57+
<button id="btnResumeSession" class="btn-secondary"
58+
style="padding: 4px 8px; font-size: 0.8rem; margin-left: 10px;">Resume</button>
59+
</div>
60+
61+
<button id="btnEndSession" class="btn-end-session"><i class="fa-solid fa-flag-checkered"></i> End
62+
Session</button>
63+
<button id="btnNotifications" class="btn-notif" style="display: none;">
64+
<i class="fa-solid fa-bell"></i>
65+
<span id="notifBadge" class="notif-badge" style="display: none;">0</span>
66+
</button>
67+
<button id="btnEmergencyExit" class="btn-danger"><i class="fa-solid fa-triangle-exclamation"></i>
68+
Emergency
69+
Exit</button>
70+
</div>
3471
</header>
3572

3673
<main>
@@ -130,6 +167,22 @@ <h2 class="notif-title"><i class="fa-solid fa-envelope-open-text"></i> Held Noti
130167
</div>
131168
</div>
132169

170+
<!-- Motivational Overlay -->
171+
<div id="motivationalOverlay" class="fullscreen-overlay" style="display: none;">
172+
<div class="overlay-panel border-success"
173+
style="border-color: #00E676; box-shadow: 0 0 30px rgba(0, 230, 118, 0.6);">
174+
<h2 class="success-title"
175+
style="color: #00E676; font-family: 'Bungee', cursive; font-size: 2.2rem; margin-bottom: 10px;"><i
176+
class="fa-solid fa-trophy"></i> QUEST COMPLETE!</h2>
177+
<p id="motivationalMessage" style="font-size: 1.2rem; margin: 20px 0; color: #fff;">Great job! You crushed
178+
it!</p>
179+
<div class="safety-actions">
180+
<button id="btnMotivationalClose" class="btn-primary"
181+
style="background: #00E676; color: #000; box-shadow: 0 4px 0 #00994d;">Awesome!</button>
182+
</div>
183+
</div>
184+
</div>
185+
133186
<script src="./script.js"></script>
134187
</body>
135188

0 commit comments

Comments
 (0)