Spaces:
Running
Running
Upload 9 files
Browse files- src/views/StudentView.js +13 -15
src/views/StudentView.js
CHANGED
|
@@ -197,12 +197,11 @@ export async function renderStudentView() {
|
|
| 197 |
monster = getNextMonster(actualStage, totalLikes, classSize);
|
| 198 |
}
|
| 199 |
|
| 200 |
-
return `
|
| 201 |
// Left sidebar position: Fixed Left-8 or similar.
|
| 202 |
// User wants it in the empty left area.
|
| 203 |
return `
|
| 204 |
-
|
| 205 |
-
<
|
| 206 |
<div class="pixel-art-container relative transform transition-transform duration-500 ease-out origin-center"
|
| 207 |
style="transform: scale(${currentScale}); animation: walk-float 6s ease-in-out infinite;">
|
| 208 |
|
|
@@ -217,8 +216,7 @@ export async function renderStudentView() {
|
|
| 217 |
</div>
|
| 218 |
|
| 219 |
<!--Evolution Prompt-- >
|
| 220 |
-
${
|
| 221 |
-
canEvolve ? `
|
| 222 |
<div id="evolution-prompt" class="absolute top-full mt-2 pointer-events-auto animate-bounce z-50">
|
| 223 |
<div class="flex flex-col items-center">
|
| 224 |
<div class="bg-gray-900/90 text-pink-200 text-xs py-2 px-3 rounded-xl border border-pink-500/30 shadow-lg text-center font-bold mb-1 backdrop-blur-sm whitespace-nowrap">
|
|
@@ -231,7 +229,7 @@ export async function renderStudentView() {
|
|
| 231 |
</div>
|
| 232 |
</div>
|
| 233 |
` : ''
|
| 234 |
-
|
| 235 |
|
| 236 |
< !--Stats Tooltip-- >
|
| 237 |
<div class="opacity-0 group-hover:opacity-100 transition-opacity duration-300 bg-gray-900/90 backdrop-blur text-xs text-slate-300 p-3 rounded-xl border border-slate-700 mt-6 text-left pointer-events-auto shadow-2xl min-w-[120px]">
|
|
@@ -250,7 +248,7 @@ export async function renderStudentView() {
|
|
| 250 |
50% {transform: translateY(-3px); filter: brightness(1.1); }
|
| 251 |
}
|
| 252 |
@keyframes walk-float {
|
| 253 |
-
0 %, 100 % { transform: translateX(0) scale(${
|
| 254 |
25% {transform: translateX(-10px) rotate(-2deg) scale(${currentScale}); }
|
| 255 |
75% {transform: translateX(10px) rotate(2deg) scale(${currentScale}); }
|
| 256 |
}
|
|
@@ -310,13 +308,13 @@ export async function renderStudentView() {
|
|
| 310 |
<div class="space-y-4">
|
| 311 |
${['beginner', 'intermediate', 'advanced'].map(level => {
|
| 312 |
const tasks = levelGroups[level] || [];
|
| 313 |
-
|
| 314 |
// State Preservation Logic:
|
| 315 |
// Check DOM for existing details element and its open attribute
|
| 316 |
// ID strategy: details-{level}
|
| 317 |
// If not found, default to open for 'beginner', closed for others.
|
| 318 |
// But if we are re-rendering, we want to keep current state.
|
| 319 |
-
|
| 320 |
const detailsId = `details-group-${level}`;
|
| 321 |
const existingDetails = document.getElementById(detailsId);
|
| 322 |
let isOpenStr = '';
|
|
@@ -324,8 +322,8 @@ export async function renderStudentView() {
|
|
| 324 |
if (existingDetails) {
|
| 325 |
if (existingDetails.hasAttribute('open')) isOpenStr = 'open';
|
| 326 |
} else {
|
| 327 |
-
|
| 328 |
-
|
| 329 |
}
|
| 330 |
|
| 331 |
// Count completed
|
|
@@ -394,8 +392,8 @@ export function setupStudentEvents() {
|
|
| 394 |
};
|
| 395 |
|
| 396 |
window.submitLevel = async (challengeId) => {
|
| 397 |
-
const input = document.getElementById(`input - ${
|
| 398 |
-
const errorMsg = document.getElementById(`error - ${
|
| 399 |
const prompt = input.value;
|
| 400 |
const roomCode = localStorage.getItem('vibecoding_room_code');
|
| 401 |
const userId = localStorage.getItem('vibecoding_user_id');
|
|
@@ -441,7 +439,7 @@ export function setupStudentEvents() {
|
|
| 441 |
const newCardHTML = renderTaskCard(challenge, newProgress);
|
| 442 |
|
| 443 |
// 3. Replace in DOM
|
| 444 |
-
const oldCard = document.getElementById(`card - ${
|
| 445 |
if (oldCard) {
|
| 446 |
oldCard.outerHTML = newCardHTML;
|
| 447 |
} else {
|
|
@@ -508,7 +506,7 @@ function renderPeerModal() {
|
|
| 508 |
let optionsHtml = '<option value="" disabled selected>選擇題目...</option>';
|
| 509 |
if (cachedChallenges.length > 0) {
|
| 510 |
optionsHtml += cachedChallenges.map(c =>
|
| 511 |
-
`< option value = "${c.id}" > [${
|
| 512 |
).join('');
|
| 513 |
}
|
| 514 |
|
|
|
|
| 197 |
monster = getNextMonster(actualStage, totalLikes, classSize);
|
| 198 |
}
|
| 199 |
|
|
|
|
| 200 |
// Left sidebar position: Fixed Left-8 or similar.
|
| 201 |
// User wants it in the empty left area.
|
| 202 |
return `
|
| 203 |
+
<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">
|
| 204 |
+
<!-- Walking Container -->
|
| 205 |
<div class="pixel-art-container relative transform transition-transform duration-500 ease-out origin-center"
|
| 206 |
style="transform: scale(${currentScale}); animation: walk-float 6s ease-in-out infinite;">
|
| 207 |
|
|
|
|
| 216 |
</div>
|
| 217 |
|
| 218 |
<!--Evolution Prompt-- >
|
| 219 |
+
${canEvolve ? `
|
|
|
|
| 220 |
<div id="evolution-prompt" class="absolute top-full mt-2 pointer-events-auto animate-bounce z-50">
|
| 221 |
<div class="flex flex-col items-center">
|
| 222 |
<div class="bg-gray-900/90 text-pink-200 text-xs py-2 px-3 rounded-xl border border-pink-500/30 shadow-lg text-center font-bold mb-1 backdrop-blur-sm whitespace-nowrap">
|
|
|
|
| 229 |
</div>
|
| 230 |
</div>
|
| 231 |
` : ''
|
| 232 |
+
}
|
| 233 |
|
| 234 |
< !--Stats Tooltip-- >
|
| 235 |
<div class="opacity-0 group-hover:opacity-100 transition-opacity duration-300 bg-gray-900/90 backdrop-blur text-xs text-slate-300 p-3 rounded-xl border border-slate-700 mt-6 text-left pointer-events-auto shadow-2xl min-w-[120px]">
|
|
|
|
| 248 |
50% {transform: translateY(-3px); filter: brightness(1.1); }
|
| 249 |
}
|
| 250 |
@keyframes walk-float {
|
| 251 |
+
0 %, 100 % { transform: translateX(0) scale(${currentScale}); }
|
| 252 |
25% {transform: translateX(-10px) rotate(-2deg) scale(${currentScale}); }
|
| 253 |
75% {transform: translateX(10px) rotate(2deg) scale(${currentScale}); }
|
| 254 |
}
|
|
|
|
| 308 |
<div class="space-y-4">
|
| 309 |
${['beginner', 'intermediate', 'advanced'].map(level => {
|
| 310 |
const tasks = levelGroups[level] || [];
|
| 311 |
+
|
| 312 |
// State Preservation Logic:
|
| 313 |
// Check DOM for existing details element and its open attribute
|
| 314 |
// ID strategy: details-{level}
|
| 315 |
// If not found, default to open for 'beginner', closed for others.
|
| 316 |
// But if we are re-rendering, we want to keep current state.
|
| 317 |
+
|
| 318 |
const detailsId = `details-group-${level}`;
|
| 319 |
const existingDetails = document.getElementById(detailsId);
|
| 320 |
let isOpenStr = '';
|
|
|
|
| 322 |
if (existingDetails) {
|
| 323 |
if (existingDetails.hasAttribute('open')) isOpenStr = 'open';
|
| 324 |
} else {
|
| 325 |
+
// Initial Load defaults
|
| 326 |
+
if (level === 'beginner') isOpenStr = 'open';
|
| 327 |
}
|
| 328 |
|
| 329 |
// Count completed
|
|
|
|
| 392 |
};
|
| 393 |
|
| 394 |
window.submitLevel = async (challengeId) => {
|
| 395 |
+
const input = document.getElementById(`input - ${challengeId} `);
|
| 396 |
+
const errorMsg = document.getElementById(`error - ${challengeId} `);
|
| 397 |
const prompt = input.value;
|
| 398 |
const roomCode = localStorage.getItem('vibecoding_room_code');
|
| 399 |
const userId = localStorage.getItem('vibecoding_user_id');
|
|
|
|
| 439 |
const newCardHTML = renderTaskCard(challenge, newProgress);
|
| 440 |
|
| 441 |
// 3. Replace in DOM
|
| 442 |
+
const oldCard = document.getElementById(`card - ${challengeId} `);
|
| 443 |
if (oldCard) {
|
| 444 |
oldCard.outerHTML = newCardHTML;
|
| 445 |
} else {
|
|
|
|
| 506 |
let optionsHtml = '<option value="" disabled selected>選擇題目...</option>';
|
| 507 |
if (cachedChallenges.length > 0) {
|
| 508 |
optionsHtml += cachedChallenges.map(c =>
|
| 509 |
+
`< option value = "${c.id}" > [${c.level}] ${c.title}</option > `
|
| 510 |
).join('');
|
| 511 |
}
|
| 512 |
|