FrederickSundeep commited on
Commit
f4f6dff
·
1 Parent(s): 47e3df3

commit initial 09-12-2025 32

Browse files
Files changed (1) hide show
  1. src/App.js +124 -90
src/App.js CHANGED
@@ -140,6 +140,9 @@ function App() {
140
  const [isRunning, setIsRunning] = useState(false);
141
  const [isFixing, setIsFixing] = useState(false);
142
  const [isExplaining, setIsExplaining] = useState(false);
 
 
 
143
 
144
  useEffect(() => {
145
  saveTree(tree);
@@ -271,106 +274,130 @@ function App() {
271
 
272
  // Core run with updated input (used by XTerm onData and Send button)
273
  const runCodeWithUpdatedInput = async (inputLine) => {
274
- // append newline like a real console
275
- const newAccum = (accumStdin || "") + inputLine + "\n";
276
- setAccumStdin(newAccum);
 
 
 
277
 
278
- const node = getNodeByPath(tree, activePath);
279
- if (!node || node.type !== "file") {
280
- setOutput((prev) => prev + "\n[Error] No file selected to run.");
281
- setAwaitingInput(false);
282
- return;
283
- }
284
 
285
- const selectedLang = LANGUAGE_OPTIONS.find((l) => node.name.endsWith(l.ext))?.id;
286
- if (!selectedLang || !RUNNABLE_LANGS.includes(selectedLang)) {
287
- setOutput((prev) => prev + `\n[Error] Run not supported for ${node.name}`);
288
- setAwaitingInput(false);
289
- return;
290
- }
291
 
292
- setIsRunning(true);
293
- try {
294
- const res = await runCode(node.content, selectedLang, newAccum);
295
- const out = res.output ?? "";
296
- setOutput(out);
297
- setProblems(res.error ? parseProblems(res.output) : []);
298
- if (outputLooksForInput(out)) {
299
- setAwaitingInput(true);
300
- // keep focus
301
- focusXtermHelper();
302
- } else {
303
- setAwaitingInput(false);
304
- }
305
- } catch (err) {
306
- setOutput(String(err));
 
 
 
307
  setAwaitingInput(false);
308
- } finally {
309
- setIsRunning(false);
310
  }
311
- };
 
 
 
 
 
 
 
312
 
313
  // Initial Run: do NOT run if code needs interactive input and there's no accumulated stdin.
314
- const handleRun = async () => {
315
- const node = getNodeByPath(tree, activePath);
316
- if (!node || node.type !== "file") {
317
- setOutput("Select a file to run.");
318
- return;
319
- }
320
- const selectedLang = LANGUAGE_OPTIONS.find((l) => node.name.endsWith(l.ext))?.id;
321
- if (!selectedLang || !RUNNABLE_LANGS.includes(selectedLang)) {
322
- setOutput(`⚠️ Run not supported for this file type.`);
323
- return;
324
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
325
 
326
- const needs = codeNeedsInput(node.content, selectedLang);
327
- const haveAccum = !!(accumStdin && accumStdin.length > 0);
328
- const haveLegacyStdin = !!(stdin && stdin.length > 0);
329
 
330
- if (needs && !haveAccum && !haveLegacyStdin) {
331
- // Show instruction and enable typing in XTerm; do not perform a run with empty stdin (avoids EOFError)
332
- setOutput("[Interactive program detected — click Start interactive session or type in terminal]");
 
 
 
 
 
 
 
333
  setAwaitingInput(true);
334
  focusXtermHelper();
335
- return;
 
 
336
  }
 
 
 
 
 
 
 
 
 
 
337
 
338
- // Otherwise perform run with existing accumulated or legacy input
339
- const stdinToSend = accumStdin || stdin || "";
340
- resetTerminal(true);
341
- setOutput(`[Running with stdin length=${stdinToSend ? stdinToSend.length : 0}]\n`);
342
- setIsRunning(true);
343
- setProblems([]);
344
- try {
345
- const res = await runCode(node.content, selectedLang, stdinToSend);
346
- const out = res.output ?? "";
347
- setOutput(out);
348
- setProblems(res.error ? parseProblems(res.output) : []);
349
- if (outputLooksForInput(out)) {
350
- setAwaitingInput(true);
351
- focusXtermHelper();
352
- } else {
353
- setAwaitingInput(false);
354
- }
355
- } catch (err) {
356
- setOutput(String(err));
357
- // allow interactive continuation after EOF
358
- setAwaitingInput(true);
359
  focusXtermHelper();
360
- } finally {
361
- setIsRunning(false);
362
  }
363
- };
 
 
 
 
 
 
 
 
 
364
 
365
- // Send from the small input box: delegate to runCodeWithUpdatedInput to keep behavior uniform
366
- const sendTerminalInput = async () => {
367
- if (!terminalInput || terminalInput.length === 0) return;
368
- // echo in output for small box then run
369
- setOutput((prev) => prev + `\n> ${terminalInput}`);
370
- const line = terminalInput;
371
- setTerminalInput("");
372
- await runCodeWithUpdatedInput(line);
373
- };
374
 
375
  // handle press Enter for small input
376
  const onTerminalKeyDown = (e) => {
@@ -554,12 +581,19 @@ function App() {
554
  <div style={{ fontSize: 12, color: "#ccc", marginBottom: 6 }}>Terminal</div>
555
 
556
  <XTerm
557
- output={output}
558
- onData={(line) => {
559
- // Called when XTerm wrapper detects a full line (ENTER)
560
- runCodeWithUpdatedInput(line);
561
- }}
562
- />
 
 
 
 
 
 
 
563
 
564
  {/* When interactive program detected and waiting for user input */}
565
  {awaitingInput && (
 
140
  const [isRunning, setIsRunning] = useState(false);
141
  const [isFixing, setIsFixing] = useState(false);
142
  const [isExplaining, setIsExplaining] = useState(false);
143
+ // near other useState declarations
144
+ const [interactivePromptShown, setInteractivePromptShown] = useState(false);
145
+
146
 
147
  useEffect(() => {
148
  saveTree(tree);
 
274
 
275
  // Core run with updated input (used by XTerm onData and Send button)
276
  const runCodeWithUpdatedInput = async (inputLine) => {
277
+ if (typeof inputLine !== "string") inputLine = String(inputLine || "");
278
+ const trimmed = inputLine.replace(/\r$/, ""); // normalize
279
+ if (trimmed.length === 0 && !accumStdin) {
280
+ // nothing to send
281
+ return;
282
+ }
283
 
284
+ // append newline like a real console
285
+ const newAccum = (accumStdin || "") + trimmed + "\n";
286
+ setAccumStdin(newAccum);
287
+ setInteractivePromptShown(false); // first input arrived -> clear prompt-shown flag
 
 
288
 
289
+ const node = getNodeByPath(tree, activePath);
290
+ if (!node || node.type !== "file") {
291
+ setOutput((prev) => prev + "\n[Error] No file selected to run.");
292
+ setAwaitingInput(false);
293
+ return;
294
+ }
295
 
296
+ const selectedLang = LANGUAGE_OPTIONS.find((l) => node.name.endsWith(l.ext))?.id;
297
+ if (!selectedLang || !RUNNABLE_LANGS.includes(selectedLang)) {
298
+ setOutput((prev) => prev + `\n[Error] Run not supported for ${node.name}`);
299
+ setAwaitingInput(false);
300
+ return;
301
+ }
302
+
303
+ setIsRunning(true);
304
+ try {
305
+ const res = await runCode(node.content, selectedLang, newAccum);
306
+ const out = res.output ?? "";
307
+ setOutput(out);
308
+ setProblems(res.error ? parseProblems(res.output) : []);
309
+ if (outputLooksForInput(out)) {
310
+ // program still waits for more input
311
+ setAwaitingInput(true);
312
+ focusXtermHelper();
313
+ } else {
314
  setAwaitingInput(false);
 
 
315
  }
316
+ } catch (err) {
317
+ setOutput(String(err));
318
+ setAwaitingInput(true);
319
+ } finally {
320
+ setIsRunning(false);
321
+ }
322
+ };
323
+
324
 
325
  // Initial Run: do NOT run if code needs interactive input and there's no accumulated stdin.
326
+ const handleRun = async () => {
327
+ const node = getNodeByPath(tree, activePath);
328
+ if (!node || node.type !== "file") {
329
+ setOutput("Select a file to run.");
330
+ return;
331
+ }
332
+ const selectedLang = LANGUAGE_OPTIONS.find((l) => node.name.endsWith(l.ext))?.id;
333
+ if (!selectedLang || !RUNNABLE_LANGS.includes(selectedLang)) {
334
+ setOutput(`⚠️ Run not supported for this file type.`);
335
+ return;
336
+ }
337
+
338
+ const needs = codeNeedsInput(node.content, selectedLang);
339
+ const haveAccum = !!(accumStdin && accumStdin.length > 0);
340
+ const haveLegacyStdin = !!(stdin && stdin.length > 0);
341
+
342
+ if (needs && !haveAccum && !haveLegacyStdin) {
343
+ // Show helpful message in terminal and enable awaitingInput so user can type into XTerm.
344
+ setOutput("[Interactive program detected — type input directly into the terminal]");
345
+ setAwaitingInput(true);
346
+ setInteractivePromptShown(true);
347
+ focusXtermHelper();
348
+ return; // do not run now (avoids EOFError)
349
+ }
350
 
351
+ // Otherwise run immediately with whichever stdin we have (accumStdin or legacy stdin)
352
+ const stdinToSend = accumStdin || stdin || "";
 
353
 
354
+ resetTerminal(true); // keep accumStdin
355
+ setOutput(`[Running with stdin length=${stdinToSend ? stdinToSend.length : 0}]\n`);
356
+ setIsRunning(true);
357
+ setProblems([]);
358
+ try {
359
+ const res = await runCode(node.content, selectedLang, stdinToSend);
360
+ const out = res.output ?? "";
361
+ setOutput(out);
362
+ setProblems(res.error ? parseProblems(res.output) : []);
363
+ if (outputLooksForInput(out)) {
364
  setAwaitingInput(true);
365
  focusXtermHelper();
366
+ } else {
367
+ setAwaitingInput(false);
368
+ setInteractivePromptShown(false);
369
  }
370
+ } catch (err) {
371
+ setOutput(String(err));
372
+ // Allow user to type into terminal after EOF errors
373
+ setAwaitingInput(true);
374
+ setInteractivePromptShown(true);
375
+ focusXtermHelper();
376
+ } finally {
377
+ setIsRunning(false);
378
+ }
379
+ };
380
 
381
+
382
+ // Send from the small input box: delegate to runCodeWithUpdatedInput to keep behavior uniform
383
+ const sendTerminalInput = async () => {
384
+ const line = (terminalInput || "").replace(/\r$/, "");
385
+ if (!line) {
386
+ // If there is no typed small-input but interactivePromptShown is true, just focus terminal
387
+ if (interactivePromptShown) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
388
  focusXtermHelper();
 
 
389
  }
390
+ return;
391
+ }
392
+
393
+ // show echo in output
394
+ setOutput((prev) => (prev ? prev + `\n> ${line}` : `> ${line}`));
395
+ setTerminalInput("");
396
+
397
+ // Use the unified runner that appends input and runs
398
+ await runCodeWithUpdatedInput(line);
399
+ };
400
 
 
 
 
 
 
 
 
 
 
401
 
402
  // handle press Enter for small input
403
  const onTerminalKeyDown = (e) => {
 
581
  <div style={{ fontSize: 12, color: "#ccc", marginBottom: 6 }}>Terminal</div>
582
 
583
  <XTerm
584
+ output={output}
585
+ onData={(line) => {
586
+ const text = (line || "").replace(/\r$/, "");
587
+ if (!text) return;
588
+ // only auto-send when we previously showed a prompt OR are awaiting input
589
+ if (interactivePromptShown || awaitingInput) {
590
+ runCodeWithUpdatedInput(text);
591
+ } else {
592
+ // not an interactive session — you can still echo into small input if desired
593
+ setTerminalInput((prev) => (prev ? prev + text : text));
594
+ }
595
+ }}
596
+ />
597
 
598
  {/* When interactive program detected and waiting for user input */}
599
  {awaitingInput && (