Skip to content

Commit c96d1b0

Browse files
committed
bugs
1 parent 67421cc commit c96d1b0

1 file changed

Lines changed: 77 additions & 64 deletions

File tree

templates/index.html

Lines changed: 77 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ <h3 class="text-xl font-black text-white mb-6 tracking-tighter italic">OPEN NEW
305305

306306
// --- 0. CTRL+S SAVE ---
307307
document.addEventListener('keydown', e => {
308-
if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key === 'l') { e.preventDefault(); clearTerminal(); }
308+
if ((e.ctrlKey || e.metaKey) && e.shiftKey && (e.key === 'l' || e.key === 'L')) { e.preventDefault(); clearTerminal(); }
309309
if ((e.ctrlKey || e.metaKey) && e.key === 's') { e.preventDefault(); triggerSave(); }
310310
if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key === 'M') { e.preventDefault(); const mc = e.target.closest('.monaco-container') || e.target.closest('.code-main-container'); if(mc) toggleMaximize(mc.id); }
311311
if ((e.ctrlKey || e.metaKey) && e.key === "'") { e.preventDefault(); runCode(); }
@@ -469,6 +469,7 @@ <h3 class="text-xl font-black text-white mb-6 tracking-tighter italic">OPEN NEW
469469

470470
// --- 3. EDITOR & VIEW MGMT ---
471471
function selectTab(id) {
472+
cancelTestGen();
472473
activeTabId = id;
473474
genMgr.clearAll();
474475

@@ -558,12 +559,15 @@ <h3 class="text-xl font-black text-white mb-6 tracking-tighter italic">OPEN NEW
558559
spinnerInterval = setInterval(() => { i = (i + 1) % frames.length; term.innerText = `${frames[i]} ${msg}`; syncDockedTerminal(); }, 80);
559560
return term;
560561
}
561-
function termDone(text, color) {
562+
function termDone(text, color, tabId) {
562563
clearInterval(spinnerInterval);
563-
const term = document.getElementById('terminal');
564-
term.innerText = text; term.style.color = color || '#10b981';
565-
saveTerminal(text, color || '#10b981');
566-
syncDockedTerminal();
564+
const tid = tabId || activeTabId;
565+
if (tabs[tid]) { tabs[tid].terminalOutput = text; tabs[tid].terminalColor = color || '#10b981'; }
566+
if (tid === activeTabId) {
567+
const term = document.getElementById('terminal');
568+
term.innerText = text; term.style.color = color || '#10b981';
569+
syncDockedTerminal();
570+
}
567571
}
568572

569573
async function geminiCall(prompt) {
@@ -585,24 +589,26 @@ <h3 class="text-xl font-black text-white mb-6 tracking-tighter italic">OPEN NEW
585589
}
586590

587591
async function askGemini() {
588-
const key = `analysis_${activeTabId}`;
592+
const tabId = activeTabId;
593+
const key = `analysis_${tabId}`;
589594
if (!genMgr.acquire(key)) return;
590595
const body = document.getElementById('aiAnalysisBody');
591596
body.innerHTML = "<p class='animate-pulse text-blue-400 font-bold'>ANALYZING PATTERNS...</p>";
592597
termSpinner('Analyzing patterns...');
593598
try {
594-
const text = await geminiCall(`Explain optimal approach:\n${tabs[activeTabId].statement}`);
595-
tabs[activeTabId].aiAnalysis = text;
596-
body.innerHTML = marked.parse(text);
599+
const text = await geminiCall(`Explain optimal approach:\n${tabs[tabId].statement}`);
600+
tabs[tabId].aiAnalysis = text;
601+
if (activeTabId === tabId) body.innerHTML = marked.parse(text);
597602
triggerSave();
598-
termDone('>>> Analysis complete.', '#10b981');
599-
} catch(e) { body.innerHTML = `<span class="text-red-400">${e.message}</span>`; termDone('>>> Analysis failed: ' + e.message, '#f87171'); }
603+
termDone('>>> Analysis complete.', '#10b981', tabId);
604+
} catch(e) { if (activeTabId === tabId) body.innerHTML = `<span class="text-red-400">${e.message}</span>`; termDone('>>> Analysis failed: ' + e.message, '#f87171', tabId); }
600605
finally { genMgr.release(); }
601606
}
602607

603608
async function validateStep(stepKey, btn) {
604-
const content = editors[`${activeTabId}_${stepKey}`]?.getValue() || '';
605-
const key = `validate_${activeTabId}_${stepKey}_${content.length}`;
609+
const tabId = activeTabId;
610+
const content = editors[`${tabId}_${stepKey}`]?.getValue() || '';
611+
const key = `validate_${tabId}_${stepKey}_${content.length}`;
606612
if (!genMgr.acquire(key)) return;
607613
const originalText = btn.innerText;
608614
btn.innerText = "VALIDATING...";
@@ -611,13 +617,12 @@ <h3 class="text-xl font-black text-white mb-6 tracking-tighter italic">OPEN NEW
611617
if (feedbackDiv) feedbackDiv.classList.add('hidden');
612618
termSpinner(`Validating step ${stepKey}...`);
613619
try {
614-
const text = await geminiCall(`Problem: ${tabs[activeTabId].statement}\nStep: ${stepKey}\nInput: ${content}\n\nGive short feedback (<60 words)`);
615-
feedbackDiv.innerHTML = marked.parse(text);
616-
feedbackDiv.classList.remove('hidden');
617-
termDone(`>>> Step ${stepKey} validated.`, '#10b981');
620+
const text = await geminiCall(`Problem: ${tabs[tabId].statement}\nStep: ${stepKey}\nInput: ${content}\n\nGive short feedback (<60 words)`);
621+
if (activeTabId === tabId) { feedbackDiv.innerHTML = marked.parse(text); feedbackDiv.classList.remove('hidden'); }
622+
termDone(`>>> Step ${stepKey} validated.`, '#10b981', tabId);
618623
} catch (e) {
619-
if (feedbackDiv) { feedbackDiv.innerHTML = `<span class="text-red-400">${e.message}</span>`; feedbackDiv.classList.remove('hidden'); }
620-
termDone('>>> Validation failed: ' + e.message, '#f87171');
624+
if (activeTabId === tabId && feedbackDiv) { feedbackDiv.innerHTML = `<span class="text-red-400">${e.message}</span>`; feedbackDiv.classList.remove('hidden'); }
625+
termDone('>>> Validation failed: ' + e.message, '#f87171', tabId);
621626
genMgr.clearSection(key);
622627
} finally {
623628
btn.innerText = originalText;
@@ -637,27 +642,28 @@ <h3 class="text-xl font-black text-white mb-6 tracking-tighter italic">OPEN NEW
637642
generateBoilerplate(lang);
638643
}
639644

640-
function saveTerminal(text, color) {
641-
if (!activeTabId) return;
642-
tabs[activeTabId].terminalOutput = text;
643-
tabs[activeTabId].terminalColor = color;
645+
function saveTerminal(text, color, tabId) {
646+
const tid = tabId || activeTabId;
647+
if (!tid || !tabs[tid]) return;
648+
tabs[tid].terminalOutput = text;
649+
tabs[tid].terminalColor = color;
644650
}
645651

646652
async function generateBoilerplate(lang) {
647-
const key = `boilerplate_${activeTabId}_${lang}`;
653+
const tabId = activeTabId;
654+
const key = `boilerplate_${tabId}_${lang}`;
648655
if (!genMgr.acquire(key)) return;
649656
termSpinner(`Generating ${lang} boilerplate...`);
650657
try {
651658
const javaHint = lang === 'java' ? ' For Java: use public class Main with public static void main.' : '';
652-
const prompt = `Generate ONLY ${lang} boilerplate (no solution) for:\n${tabs[activeTabId].statement}\n\nUse function name: solve. Include main/entry point.${javaHint} ONLY code, NO markdown fences.`;
659+
const prompt = `Generate ONLY ${lang} boilerplate (no solution) for:\n${tabs[tabId].statement}\n\nUse function name: solve. Include main/entry point.${javaHint} ONLY code, NO markdown fences.`;
653660
const raw = await geminiCall(prompt);
654661
const code = raw.replace(/```\w*/g, '').replace(/```/g, '').trim();
655-
const ed = editors[`${activeTabId}_code`];
656-
monaco.editor.setModelLanguage(ed.getModel(), lang);
657-
ed.setValue(code);
658-
termDone('>>> Boilerplate generated.', '#10b981');
662+
const ed = editors[`${tabId}_code`];
663+
if (ed) { monaco.editor.setModelLanguage(ed.getModel(), lang); ed.setValue(code); }
664+
termDone('>>> Boilerplate generated.', '#10b981', tabId);
659665
} catch (e) {
660-
termDone('>>> ERROR: ' + e.message, '#f87171');
666+
termDone('>>> ERROR: ' + e.message, '#f87171', tabId);
661667
genMgr.clearSection(key);
662668
} finally { genMgr.release(); }
663669
}
@@ -680,30 +686,31 @@ <h3 class="text-xl font-black text-white mb-6 tracking-tighter italic">OPEN NEW
680686
update();
681687
testGenTimer = setInterval(() => {
682688
testGenCountdown--;
683-
if (testGenCountdown <= 0) { clearInterval(testGenTimer); testGenTimer = null; generateTests(); return; }
689+
if (testGenCountdown <= 0) { clearInterval(testGenTimer); testGenTimer = null; generateTests(true); return; }
684690
update();
685691
}, 1000);
686692
}
687693

688-
async function generateTests() {
694+
async function generateTests(silent) {
689695
cancelTestGen();
690-
const code = editors[`${activeTabId}_code`]?.getValue() || '';
691-
const key = `tests_${activeTabId}_${code.length}`;
696+
const tabId = activeTabId;
697+
const code = editors[`${tabId}_code`]?.getValue() || '';
698+
const key = `tests_${tabId}_${code.length}`;
692699
if (!genMgr.acquire(key)) return;
693700
const btns = getGenTestBtns();
694701
btns.forEach(b => { b.innerHTML = 'Generating...'; b.classList.add('animate-pulse'); });
695-
termSpinner('Generating tests...');
702+
if (!silent) termSpinner('Generating tests...');
696703
try {
697-
const lang = document.getElementById('langSelect')?.value || 'python';
698-
const prompt = `Generate unit tests for this ${lang} code. Problem: ${tabs[activeTabId].statement}\nCode:\n${code}\n\nPrint a table of pass/fail results. Return ONLY code, NO markdown fences.`;
704+
const lang = tabs[tabId].lang || 'python';
705+
const prompt = `Generate unit tests for this ${lang} code. Problem: ${tabs[tabId].statement}\nCode:\n${code}\n\nPrint a table of pass/fail results. Return ONLY code, NO markdown fences.`;
699706
const raw = await geminiCall(prompt);
700707
const script = raw.replace(/```\w*/g, '').replace(/```/g, '').trim();
701-
editors[`${activeTabId}_tests`].setValue(script);
702-
tabs[activeTabId].steps.tests = script;
708+
const ed = editors[`${tabId}_tests`];
709+
if (ed) ed.setValue(script);
710+
tabs[tabId].steps.tests = script;
703711
triggerSave();
704-
termDone('>>> Tests generated. Running...', '#10b981');
705-
await runTests();
706-
} catch(e) { termDone('>>> ERROR: ' + e.message, '#f87171'); genMgr.clearSection(key); }
712+
if (!silent) { termDone('>>> Tests generated. Running...', '#10b981', tabId); if (activeTabId === tabId) await runTests(); }
713+
} catch(e) { if (!silent) termDone('>>> ERROR: ' + e.message, '#f87171', tabId); genMgr.clearSection(key); }
707714
finally { btns.forEach(b => { b.innerHTML = 'Gen Tests'; b.classList.remove('animate-pulse'); }); genMgr.release(); }
708715
}
709716

@@ -763,55 +770,57 @@ <h3 class="text-xl font-black text-white mb-6 tracking-tighter italic">OPEN NEW
763770
}
764771

765772
async function runCode() {
773+
const tabId = activeTabId;
766774
const selectedLang = document.getElementById('langSelect').value;
767-
const fullScript = editors[`${activeTabId}_code`].getValue();
775+
const fullScript = editors[`${tabId}_code`].getValue();
768776

769777
if (selectedLang !== 'javascript') {
770778
termSpinner('Sanity check...');
771779
const check = await sanityCheck(fullScript, selectedLang);
772-
if (!check.safe) { termDone(`[BLOCKED] ${check.reason}`, '#f87171'); return; }
780+
if (!check.safe) { termDone(`[BLOCKED] ${check.reason}`, '#f87171', tabId); return; }
773781
}
774782

775783
termSpinner('Running...');
776784
let runSuccess = false;
777785
if (selectedLang === 'javascript') {
778786
const r = await runJsInSandbox(fullScript);
779787
runSuccess = r.success;
780-
termDone(r.output || '(no output)', r.success ? '#10b981' : '#f87171');
788+
termDone(r.output || '(no output)', r.success ? '#10b981' : '#f87171', tabId);
781789
} else if (serverAvailable) {
782790
try {
783791
const langMap = { python:'Python', java:'Java' };
784792
const res = await fetch('/run_code', { method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({code:fullScript, language:langMap[selectedLang]}) });
785793
const d = await res.json();
786794
runSuccess = d.success;
787-
termDone(d.output, d.success ? '#10b981' : '#f87171');
788-
} catch(e) { termDone('ERROR: ' + e.message, '#f87171'); }
789-
} else { termDone(`[ERROR] ${selectedLang} requires server.`, '#f87171'); }
790-
if (runSuccess) scheduleTestGen();
795+
termDone(d.output, d.success ? '#10b981' : '#f87171', tabId);
796+
} catch(e) { termDone('ERROR: ' + e.message, '#f87171', tabId); }
797+
} else { termDone(`[ERROR] ${selectedLang} requires server.`, '#f87171', tabId); }
798+
if (runSuccess && activeTabId === tabId && !(editors[`${tabId}_tests`]?.getValue()?.trim())) scheduleTestGen();
791799
}
792800

793801
async function runTests() {
802+
const tabId = activeTabId;
794803
const selectedLang = document.getElementById('langSelect').value;
795-
const fullScript = editors[`${activeTabId}_code`].getValue() + '\n\n' + editors[`${activeTabId}_tests`].getValue();
804+
const fullScript = editors[`${tabId}_code`].getValue() + '\n\n' + editors[`${tabId}_tests`].getValue();
796805

797806
if (selectedLang !== 'javascript') {
798807
termSpinner('Sanity check...');
799808
const check = await sanityCheck(fullScript, selectedLang);
800-
if (!check.safe) { termDone(`[BLOCKED] ${check.reason}`, '#f87171'); return; }
809+
if (!check.safe) { termDone(`[BLOCKED] ${check.reason}`, '#f87171', tabId); return; }
801810
}
802811

803812
termSpinner('Running code + tests...');
804813
if (selectedLang === 'javascript') {
805814
const r = await runJsInSandbox(fullScript);
806-
termDone(r.output || '(no output)', r.success ? '#10b981' : '#f87171');
815+
termDone(r.output || '(no output)', r.success ? '#10b981' : '#f87171', tabId);
807816
} else if (serverAvailable) {
808817
try {
809818
const langMap = { python:'Python', java:'Java' };
810819
const res = await fetch('/run_code', { method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({code:fullScript, language:langMap[selectedLang]}) });
811820
const d = await res.json();
812-
termDone(d.output, d.success ? '#10b981' : '#f87171');
813-
} catch(e) { termDone('ERROR: ' + e.message, '#f87171'); }
814-
} else { termDone(`[ERROR] ${selectedLang} requires server.`, '#f87171'); }
821+
termDone(d.output, d.success ? '#10b981' : '#f87171', tabId);
822+
} catch(e) { termDone('ERROR: ' + e.message, '#f87171', tabId); }
823+
} else { termDone(`[ERROR] ${selectedLang} requires server.`, '#f87171', tabId); }
815824
}
816825

817826
// --- 5. PERSISTENCE (localStorage always, server mirror when available) ---
@@ -1019,6 +1028,7 @@ <h3 class="text-xl font-black text-white mb-6 tracking-tighter italic">OPEN NEW
10191028
}
10201029

10211030
async function submitCtxQuestion() {
1031+
const tabId = activeTabId;
10221032
const inp = document.getElementById('ctxInput');
10231033
const raw = inp.value.trim();
10241034
if (!raw) return;
@@ -1035,14 +1045,15 @@ <h3 class="text-xl font-black text-white mb-6 tracking-tighter italic">OPEN NEW
10351045
renderChatThreads();
10361046

10371047
const refCtx = buildRefContext(refs);
1038-
const answer = await callChat(cleanText, ctxSelection, ctxSource, refCtx);
1048+
const answer = await callChat(cleanText, ctxSelection, ctxSource, refCtx, tabId);
10391049
threads[hash].messages.push({ question: cleanText, answer });
1040-
renderChatThreads();
1050+
if (activeTabId === tabId) renderChatThreads();
10411051
triggerSave();
10421052
}
10431053

10441054
async function sendThreadReply() {
10451055
if (!replyingToThread) return;
1056+
const tabId = activeTabId;
10461057
const inp = document.getElementById('threadReplyInput');
10471058
const raw = inp.value.trim();
10481059
if (!raw) return;
@@ -1054,19 +1065,19 @@ <h3 class="text-xl font-black text-white mb-6 tracking-tighter italic">OPEN NEW
10541065
if (!t) return;
10551066

10561067
const refCtx = buildRefContext(refs);
1057-
// Thread-local context only: original selection + this thread's history
10581068
let threadCtx = `Original selection: ${t.selection}\n`;
10591069
t.messages.forEach(m => { threadCtx += `Q: ${m.question}\nA: ${m.answer}\n`; });
10601070

1061-
const answer = await callChat(cleanText, threadCtx, t.source, refCtx);
1071+
const answer = await callChat(cleanText, threadCtx, t.source, refCtx, tabId);
10621072
t.messages.push({ question: cleanText, answer });
1063-
renderChatThreads();
1073+
if (activeTabId === tabId) renderChatThreads();
10641074
triggerSave();
10651075
}
10661076

1067-
async function callChat(question, selection, source, refContext) {
1077+
async function callChat(question, selection, source, refContext, tabId) {
10681078
try {
1069-
const prompt = `You are a concise coding tutor. Answer in <100 words.\n\nProblem: ${(tabs[activeTabId]?.statement || '').slice(0,500)}\nSelected text from [${source}]: ${(selection||'').slice(0,500)}\n${refContext ? 'Referenced threads:\n' + refContext : ''}\nQuestion: ${question}`;
1079+
const tid = tabId || activeTabId;
1080+
const prompt = `You are a concise coding tutor. Answer in <100 words.\n\nProblem: ${(tabs[tid]?.statement || '').slice(0,500)}\nSelected text from [${source}]: ${(selection||'').slice(0,500)}\n${refContext ? 'Referenced threads:\n' + refContext : ''}\nQuestion: ${question}`;
10701081
return await geminiCall(prompt);
10711082
} catch (e) { return 'Error: ' + e.message; }
10721083
}
@@ -1082,6 +1093,7 @@ <h3 class="text-xl font-black text-white mb-6 tracking-tighter italic">OPEN NEW
10821093
}
10831094

10841095
async function sendAdHocChat() {
1096+
const tabId = activeTabId;
10851097
const inp = document.getElementById('adHocChatInput');
10861098
const raw = inp.value.trim();
10871099
if (!raw) return;
@@ -1097,8 +1109,9 @@ <h3 class="text-xl font-black text-white mb-6 tracking-tighter italic">OPEN NEW
10971109
renderChatThreads();
10981110

10991111
const refCtx = buildRefContext(refs);
1100-
const answer = await callChat(cleanText, '', 'general', refCtx);
1112+
const answer = await callChat(cleanText, '', 'general', refCtx, tabId);
11011113
threads[hash].messages.push({ question: cleanText, answer });
1114+
if (activeTabId === tabId) renderChatThreads();
11021115
renderChatThreads();
11031116
triggerSave();
11041117
}

0 commit comments

Comments
 (0)