Spaces:
Running
Running
Upload 9 files
Browse files- src/views/StudentView.js +40 -21
src/views/StudentView.js
CHANGED
|
@@ -192,14 +192,16 @@ export async function renderStudentView() {
|
|
| 192 |
return `
|
| 193 |
<div id="monster-container-fixed" class="fixed top-32 left-8 z-50 flex flex-col items-center group pointer-events-none sm:pointer-events-auto w-32 h-32">
|
| 194 |
<!-- Walking Container -->
|
|
|
|
| 195 |
<div class="pixel-art-container relative transform transition-transform duration-500 ease-out origin-center"
|
| 196 |
-
style="transform: scale(${currentScale}); animation:
|
| 197 |
|
| 198 |
-
<
|
|
|
|
| 199 |
${generateMonsterSVG(monster)}
|
| 200 |
</div>
|
| 201 |
|
| 202 |
-
<!-- Level Indicator -->
|
| 203 |
<div class="absolute -bottom-2 -right-2 bg-gray-900/90 text-xs text-yellow-400 px-2 py-0.5 rounded-full border border-yellow-500/50 font-mono font-bold transform scale-75 origin-top-left whitespace-nowrap">
|
| 204 |
Lv.${1 + totalCompleted}
|
| 205 |
</div>
|
|
@@ -234,12 +236,19 @@ export async function renderStudentView() {
|
|
| 234 |
0%, 100% { transform: translateY(0); filter: brightness(1); }
|
| 235 |
50% { transform: translateY(-3px); filter: brightness(1.1); }
|
| 236 |
}
|
| 237 |
-
@keyframes
|
| 238 |
-
0% { transform: translateX(0)
|
| 239 |
-
45% { transform: translateX(120px)
|
| 240 |
-
|
| 241 |
-
95% { transform: translateX(0)
|
| 242 |
-
100% { transform: translateX(0)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 243 |
}
|
| 244 |
</style>
|
| 245 |
`;
|
|
@@ -281,17 +290,20 @@ export async function renderStudentView() {
|
|
| 281 |
return `
|
| 282 |
<div class="min-h-screen p-4 pb-32 max-w-md mx-auto sm:max-w-4xl pt-24 sm:pt-4">
|
| 283 |
<header class="flex justify-end items-center mb-6 sticky top-0 bg-slate-900/95 backdrop-blur z-20 py-4 px-2 -mx-2 border-b border-gray-800">
|
| 284 |
-
<div class="flex
|
| 285 |
-
<div class="flex
|
| 286 |
-
<div class="
|
| 287 |
-
|
|
|
|
|
|
|
|
|
|
| 288 |
</div>
|
| 289 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
| 290 |
</div>
|
| 291 |
-
<!-- Logo removed/minimized since we have Monster -->
|
| 292 |
-
<!-- <div>
|
| 293 |
-
<h1 class="text-xl font-bold italic text-white tracking-widest">VIBECODING</h1>
|
| 294 |
-
</div> -->
|
| 295 |
</header>
|
| 296 |
|
| 297 |
<div class="space-y-4">
|
|
@@ -344,12 +356,11 @@ export async function renderStudentView() {
|
|
| 344 |
</div>
|
| 345 |
|
| 346 |
<!-- Peer Learning FAB -->
|
| 347 |
-
<button onclick="window.openPeerModal()" class="fixed bottom-
|
| 348 |
title="查看同學作業">
|
| 349 |
-
<svg xmlns="http://www.w3.org/2000/svg" class="h-
|
| 350 |
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0z" />
|
| 351 |
</svg>
|
| 352 |
-
<span class="font-bold text-lg hidden sm:inline">查看同學提示詞</span>
|
| 353 |
</button>
|
| 354 |
</div>
|
| 355 |
`;
|
|
@@ -489,6 +500,14 @@ function participantDataCheck(roomCode, userId) {
|
|
| 489 |
return true;
|
| 490 |
}
|
| 491 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 492 |
// Peer Learning Modal Logic
|
| 493 |
function renderPeerModal() {
|
| 494 |
// We need to re-fetch challenges for the dropdown?
|
|
|
|
| 192 |
return `
|
| 193 |
<div id="monster-container-fixed" class="fixed top-32 left-8 z-50 flex flex-col items-center group pointer-events-none sm:pointer-events-auto w-32 h-32">
|
| 194 |
<!-- Walking Container -->
|
| 195 |
+
<!-- Walking Container (Handles Movement Only) -->
|
| 196 |
<div class="pixel-art-container relative transform transition-transform duration-500 ease-out origin-center"
|
| 197 |
+
style="transform: scale(${currentScale}); animation: patrol-move 15s linear infinite;">
|
| 198 |
|
| 199 |
+
<!-- Monster Sprite (Handles Flip & Breathe) -->
|
| 200 |
+
<div class="pixel-monster w-28 h-28 drop-shadow-2xl filter" style="animation: breathe 3s infinite ease-in-out, patrol-flip 15s linear infinite;">
|
| 201 |
${generateMonsterSVG(monster)}
|
| 202 |
</div>
|
| 203 |
|
| 204 |
+
<!-- Level Indicator (No Flip) -->
|
| 205 |
<div class="absolute -bottom-2 -right-2 bg-gray-900/90 text-xs text-yellow-400 px-2 py-0.5 rounded-full border border-yellow-500/50 font-mono font-bold transform scale-75 origin-top-left whitespace-nowrap">
|
| 206 |
Lv.${1 + totalCompleted}
|
| 207 |
</div>
|
|
|
|
| 236 |
0%, 100% { transform: translateY(0); filter: brightness(1); }
|
| 237 |
50% { transform: translateY(-3px); filter: brightness(1.1); }
|
| 238 |
}
|
| 239 |
+
@keyframes patrol-move {
|
| 240 |
+
0% { transform: translateX(0); }
|
| 241 |
+
45% { transform: translateX(120px); }
|
| 242 |
+
55% { transform: translateX(120px); }
|
| 243 |
+
95% { transform: translateX(0); }
|
| 244 |
+
100% { transform: translateX(0); }
|
| 245 |
+
}
|
| 246 |
+
@keyframes patrol-flip {
|
| 247 |
+
0% { transform: scaleX(1); }
|
| 248 |
+
49% { transform: scaleX(1); }
|
| 249 |
+
50% { transform: scaleX(-1); }
|
| 250 |
+
99% { transform: scaleX(-1); }
|
| 251 |
+
100% { transform: scaleX(1); }
|
| 252 |
}
|
| 253 |
</style>
|
| 254 |
`;
|
|
|
|
| 290 |
return `
|
| 291 |
<div class="min-h-screen p-4 pb-32 max-w-md mx-auto sm:max-w-4xl pt-24 sm:pt-4">
|
| 292 |
<header class="flex justify-end items-center mb-6 sticky top-0 bg-slate-900/95 backdrop-blur z-20 py-4 px-2 -mx-2 border-b border-gray-800">
|
| 293 |
+
<div class="flex items-center space-x-4">
|
| 294 |
+
<div class="flex flex-col items-end">
|
| 295 |
+
<div class="flex items-center space-x-2">
|
| 296 |
+
<div class="w-2 h-2 rounded-full bg-green-500 animate-pulse"></div>
|
| 297 |
+
<span class="text-gray-400 text-sm truncate max-w-[150px]">${nickname}</span>
|
| 298 |
+
</div>
|
| 299 |
+
<div class="text-xs text-gray-500 mt-1">教室: <span class="font-mono text-cyan-400 font-bold">${roomCode}</span></div>
|
| 300 |
</div>
|
| 301 |
+
<button onclick="window.logout()" class="text-gray-500 hover:text-red-400 transition-colors p-2" title="登出">
|
| 302 |
+
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
| 303 |
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1" />
|
| 304 |
+
</svg>
|
| 305 |
+
</button>
|
| 306 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
| 307 |
</header>
|
| 308 |
|
| 309 |
<div class="space-y-4">
|
|
|
|
| 356 |
</div>
|
| 357 |
|
| 358 |
<!-- Peer Learning FAB -->
|
| 359 |
+
<button onclick="window.openPeerModal()" class="fixed bottom-6 right-6 bg-purple-600 hover:bg-purple-500 text-white rounded-full p-4 shadow-xl shadow-purple-600/40 transition-transform hover:scale-110 active:scale-90 z-40"
|
| 360 |
title="查看同學作業">
|
| 361 |
+
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
| 362 |
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0z" />
|
| 363 |
</svg>
|
|
|
|
| 364 |
</button>
|
| 365 |
</div>
|
| 366 |
`;
|
|
|
|
| 500 |
return true;
|
| 501 |
}
|
| 502 |
|
| 503 |
+
window.logout = () => {
|
| 504 |
+
if (!confirm("確定要登出嗎?")) return;
|
| 505 |
+
localStorage.removeItem('vibecoding_user_id');
|
| 506 |
+
localStorage.removeItem('vibecoding_room_code');
|
| 507 |
+
localStorage.removeItem('vibecoding_nickname');
|
| 508 |
+
window.location.reload();
|
| 509 |
+
};
|
| 510 |
+
|
| 511 |
// Peer Learning Modal Logic
|
| 512 |
function renderPeerModal() {
|
| 513 |
// We need to re-fetch challenges for the dropdown?
|