Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -142,22 +142,38 @@ HTML_TEMPLATE = """
|
|
| 142 |
}
|
| 143 |
* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
|
| 144 |
|
|
|
|
| 145 |
body, html {
|
| 146 |
-
margin: 0; padding: 0; height: 100dvh; width: 100%;
|
| 147 |
background: var(--bg); color: var(--text); font-family: 'Outfit', sans-serif;
|
| 148 |
-
overflow: hidden;
|
| 149 |
font-size: 17px;
|
| 150 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 151 |
}
|
| 152 |
|
| 153 |
-
|
| 154 |
-
|
|
|
|
|
|
|
| 155 |
}
|
| 156 |
|
| 157 |
#app-container {
|
| 158 |
display: flex; flex-direction: column;
|
| 159 |
height: 100dvh;
|
|
|
|
| 160 |
position: relative;
|
|
|
|
| 161 |
}
|
| 162 |
|
| 163 |
header {
|
|
@@ -260,12 +276,16 @@ HTML_TEMPLATE = """
|
|
| 260 |
}
|
| 261 |
.ai-content strong { color: #fff; font-weight: 700; }
|
| 262 |
.ai-content h1, .ai-content h2 { margin-top: 20px; color: #fff; font-weight: 700; }
|
|
|
|
|
|
|
|
|
|
|
|
|
| 263 |
|
| 264 |
-
pre { background: #1e1e1e !important; border-radius: 12px; padding: 15px; overflow-x: auto; margin: 15px 0; border: 1px solid #333; }
|
| 265 |
code { font-family: 'JetBrains Mono', monospace; font-size: 14px; }
|
| 266 |
|
| 267 |
-
.mjx-chtml { background: #18181b; padding: 10px; border-radius: 8px; border: 1px solid #333; overflow-x: auto; margin: 10px 0; text-align: center; }
|
| 268 |
-
.mermaid { background: #111; padding: 15px; border-radius: 10px; text-align: center; margin: 15px 0; }
|
| 269 |
|
| 270 |
.msg-actions { margin-top: 10px; opacity: 0; transition: opacity 0.2s; display: flex; gap: 20px; align-items: center; }
|
| 271 |
.user-msg .msg-actions { justify-content: flex-end; }
|
|
@@ -293,6 +313,19 @@ HTML_TEMPLATE = """
|
|
| 293 |
|
| 294 |
#login-overlay { position: fixed; inset: 0; background: #000; z-index: 2000; display: flex; align-items: center; justify-content: center; }
|
| 295 |
.login-box { width: 90%; max-width: 350px; text-align: center; padding: 40px; border: 1px solid var(--border); border-radius: 20px; background: #0a0a0a; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 296 |
</style>
|
| 297 |
</head>
|
| 298 |
<body>
|
|
@@ -305,6 +338,10 @@ HTML_TEMPLATE = """
|
|
| 305 |
</div>
|
| 306 |
</div>
|
| 307 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 308 |
<div id="sidebar">
|
| 309 |
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:20px;">
|
| 310 |
<div class="user-info"><span id="display-name">User</span></div>
|
|
@@ -353,8 +390,13 @@ HTML_TEMPLATE = """
|
|
| 353 |
let currentChatId = null;
|
| 354 |
let currentAttachment = { type: null, data: null, name: null };
|
| 355 |
let longPressTimer;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 356 |
|
| 357 |
-
// --- AUTH LOGIC
|
| 358 |
function checkLogin() {
|
| 359 |
const stored = localStorage.getItem("student_ai_user");
|
| 360 |
if (stored) { currentUser = stored; showApp(); }
|
|
@@ -380,8 +422,8 @@ HTML_TEMPLATE = """
|
|
| 380 |
if(!currentChatId) {
|
| 381 |
const box = document.getElementById("chat-box");
|
| 382 |
if(box.innerHTML === "") {
|
| 383 |
-
//
|
| 384 |
-
box.innerHTML =
|
| 385 |
}
|
| 386 |
}
|
| 387 |
}
|
|
@@ -498,6 +540,23 @@ HTML_TEMPLATE = """
|
|
| 498 |
}
|
| 499 |
function editMessage(oldText) { document.getElementById('input').value = oldText; document.getElementById('input').focus(); }
|
| 500 |
function regenerate(text) { document.getElementById('input').value = text; send(); }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 501 |
|
| 502 |
// --- HISTORY LOGIC ---
|
| 503 |
function handleHistoryTouchStart(e, cid, title) {
|
|
@@ -555,11 +614,13 @@ HTML_TEMPLATE = """
|
|
| 555 |
|
| 556 |
function toggleSidebar() { document.getElementById('sidebar').classList.toggle('open'); }
|
| 557 |
async function newChat() {
|
| 558 |
-
currentChatId = null;
|
|
|
|
|
|
|
|
|
|
| 559 |
const r = await fetch('/new_chat', {method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({username:currentUser})});
|
| 560 |
const d = await r.json(); currentChatId = d.chat_id; loadHistory();
|
| 561 |
document.getElementById('sidebar').classList.remove('open');
|
| 562 |
-
showApp();
|
| 563 |
}
|
| 564 |
async function loadChat(cid) {
|
| 565 |
currentChatId = cid; const res = await fetch('/get_chat', {method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({username:currentUser, chat_id:cid})});
|
|
|
|
| 142 |
}
|
| 143 |
* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
|
| 144 |
|
| 145 |
+
/* --- FIX 4: PREVENT HORIZONTAL SCROLL --- */
|
| 146 |
body, html {
|
| 147 |
+
margin: 0; padding: 0; height: 100dvh; width: 100%; max-width: 100%;
|
| 148 |
background: var(--bg); color: var(--text); font-family: 'Outfit', sans-serif;
|
| 149 |
+
overflow: hidden; /* Locks the page */
|
| 150 |
font-size: 17px;
|
| 151 |
+
|
| 152 |
+
/* --- FIX 2: DISABLE SELECT GLOBALLY --- */
|
| 153 |
+
-webkit-user-select: none;
|
| 154 |
+
-moz-user-select: none;
|
| 155 |
+
-ms-user-select: none;
|
| 156 |
+
user-select: none;
|
| 157 |
+
}
|
| 158 |
+
|
| 159 |
+
/* Allow typing in inputs only */
|
| 160 |
+
textarea, input {
|
| 161 |
+
-webkit-user-select: text !important;
|
| 162 |
+
user-select: text !important;
|
| 163 |
}
|
| 164 |
|
| 165 |
+
/* Make sure content is NOT selectable via long press */
|
| 166 |
+
.user-content, .ai-content, code, pre, p, h1, h2, span, div {
|
| 167 |
+
-webkit-user-select: none !important;
|
| 168 |
+
user-select: none !important;
|
| 169 |
}
|
| 170 |
|
| 171 |
#app-container {
|
| 172 |
display: flex; flex-direction: column;
|
| 173 |
height: 100dvh;
|
| 174 |
+
width: 100%;
|
| 175 |
position: relative;
|
| 176 |
+
overflow-x: hidden; /* Extra safety */
|
| 177 |
}
|
| 178 |
|
| 179 |
header {
|
|
|
|
| 276 |
}
|
| 277 |
.ai-content strong { color: #fff; font-weight: 700; }
|
| 278 |
.ai-content h1, .ai-content h2 { margin-top: 20px; color: #fff; font-weight: 700; }
|
| 279 |
+
|
| 280 |
+
/* FIX 3: STYLE FOR IMAGES TO BE CLICKABLE */
|
| 281 |
+
.ai-content img, .user-content img { cursor: pointer; transition: 0.2s; }
|
| 282 |
+
.ai-content img:active, .user-content img:active { transform: scale(0.98); }
|
| 283 |
|
| 284 |
+
pre { background: #1e1e1e !important; border-radius: 12px; padding: 15px; overflow-x: auto; margin: 15px 0; border: 1px solid #333; max-width: 100%; }
|
| 285 |
code { font-family: 'JetBrains Mono', monospace; font-size: 14px; }
|
| 286 |
|
| 287 |
+
.mjx-chtml { background: #18181b; padding: 10px; border-radius: 8px; border: 1px solid #333; overflow-x: auto; margin: 10px 0; text-align: center; max-width: 100%; }
|
| 288 |
+
.mermaid { background: #111; padding: 15px; border-radius: 10px; text-align: center; margin: 15px 0; overflow-x: auto; }
|
| 289 |
|
| 290 |
.msg-actions { margin-top: 10px; opacity: 0; transition: opacity 0.2s; display: flex; gap: 20px; align-items: center; }
|
| 291 |
.user-msg .msg-actions { justify-content: flex-end; }
|
|
|
|
| 313 |
|
| 314 |
#login-overlay { position: fixed; inset: 0; background: #000; z-index: 2000; display: flex; align-items: center; justify-content: center; }
|
| 315 |
.login-box { width: 90%; max-width: 350px; text-align: center; padding: 40px; border: 1px solid var(--border); border-radius: 20px; background: #0a0a0a; }
|
| 316 |
+
|
| 317 |
+
/* --- FIX 3: LIGHTBOX (IMAGE PREVIEW) CSS --- */
|
| 318 |
+
#image-modal {
|
| 319 |
+
position: fixed; top: 0; left: 0; width: 100%; height: 100%;
|
| 320 |
+
background: rgba(0,0,0,0.9); z-index: 3000;
|
| 321 |
+
display: none; align-items: center; justify-content: center;
|
| 322 |
+
opacity: 0; transition: opacity 0.3s;
|
| 323 |
+
}
|
| 324 |
+
#image-modal img {
|
| 325 |
+
max-width: 95%; max-height: 90%; border-radius: 8px;
|
| 326 |
+
box-shadow: 0 0 20px rgba(0,0,0,0.8);
|
| 327 |
+
}
|
| 328 |
+
#image-modal.active { opacity: 1; }
|
| 329 |
</style>
|
| 330 |
</head>
|
| 331 |
<body>
|
|
|
|
| 338 |
</div>
|
| 339 |
</div>
|
| 340 |
|
| 341 |
+
<div id="image-modal" onclick="closeImagePreview()">
|
| 342 |
+
<img id="modal-img" src="" alt="Preview">
|
| 343 |
+
</div>
|
| 344 |
+
|
| 345 |
<div id="sidebar">
|
| 346 |
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:20px;">
|
| 347 |
<div class="user-info"><span id="display-name">User</span></div>
|
|
|
|
| 390 |
let currentChatId = null;
|
| 391 |
let currentAttachment = { type: null, data: null, name: null };
|
| 392 |
let longPressTimer;
|
| 393 |
+
|
| 394 |
+
// --- HELPER FOR INTRO TEXT ---
|
| 395 |
+
function getIntroHtml(name) {
|
| 396 |
+
return `<div class="msg ai-msg"><div class="ai-content"><h1>Hi ${name},</h1><p>Ready to master your studies today?</p></div></div>`;
|
| 397 |
+
}
|
| 398 |
|
| 399 |
+
// --- AUTH LOGIC ---
|
| 400 |
function checkLogin() {
|
| 401 |
const stored = localStorage.getItem("student_ai_user");
|
| 402 |
if (stored) { currentUser = stored; showApp(); }
|
|
|
|
| 422 |
if(!currentChatId) {
|
| 423 |
const box = document.getElementById("chat-box");
|
| 424 |
if(box.innerHTML === "") {
|
| 425 |
+
// FIX 1: USE FUNCTION FOR CONSISTENT INTRO
|
| 426 |
+
box.innerHTML = getIntroHtml(currentUser);
|
| 427 |
}
|
| 428 |
}
|
| 429 |
}
|
|
|
|
| 540 |
}
|
| 541 |
function editMessage(oldText) { document.getElementById('input').value = oldText; document.getElementById('input').focus(); }
|
| 542 |
function regenerate(text) { document.getElementById('input').value = text; send(); }
|
| 543 |
+
|
| 544 |
+
// --- FIX 3: LIGHTBOX LOGIC ---
|
| 545 |
+
// Open Image Logic
|
| 546 |
+
document.getElementById('chat-box').addEventListener('click', function(e) {
|
| 547 |
+
if(e.target.tagName === 'IMG') {
|
| 548 |
+
const modal = document.getElementById('image-modal');
|
| 549 |
+
const modalImg = document.getElementById('modal-img');
|
| 550 |
+
modalImg.src = e.target.src;
|
| 551 |
+
modal.style.display = 'flex';
|
| 552 |
+
setTimeout(() => modal.classList.add('active'), 10);
|
| 553 |
+
}
|
| 554 |
+
});
|
| 555 |
+
function closeImagePreview() {
|
| 556 |
+
const modal = document.getElementById('image-modal');
|
| 557 |
+
modal.classList.remove('active');
|
| 558 |
+
setTimeout(() => modal.style.display = 'none', 300);
|
| 559 |
+
}
|
| 560 |
|
| 561 |
// --- HISTORY LOGIC ---
|
| 562 |
function handleHistoryTouchStart(e, cid, title) {
|
|
|
|
| 614 |
|
| 615 |
function toggleSidebar() { document.getElementById('sidebar').classList.toggle('open'); }
|
| 616 |
async function newChat() {
|
| 617 |
+
currentChatId = null;
|
| 618 |
+
// FIX 1: SET INTRO TEXT FOR EVERY NEW CHAT
|
| 619 |
+
document.getElementById('chat-box').innerHTML = getIntroHtml(currentUser);
|
| 620 |
+
|
| 621 |
const r = await fetch('/new_chat', {method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({username:currentUser})});
|
| 622 |
const d = await r.json(); currentChatId = d.chat_id; loadHistory();
|
| 623 |
document.getElementById('sidebar').classList.remove('open');
|
|
|
|
| 624 |
}
|
| 625 |
async function loadChat(cid) {
|
| 626 |
currentChatId = cid; const res = await fetch('/get_chat', {method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({username:currentUser, chat_id:cid})});
|