Lashtw commited on
Commit
4922584
·
verified ·
1 Parent(s): 6aa5fff

Upload 9 files

Browse files
Files changed (1) hide show
  1. 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
- < 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" >
205
- < !--Walking Container-- >
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(${ currentScale }); }
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
- // Initial Load defaults
328
- if (level === 'beginner') isOpenStr = 'open';
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 - ${ challengeId } `);
398
- const errorMsg = document.getElementById(`error - ${ challengeId } `);
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 - ${ challengeId } `);
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}" > [${ c.level }] ${ c.title }</option > `
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