Spaces:
Running
Running
Commit ·
34104e6
1
Parent(s): 950fad2
commit initial 09-12-2025 07
Browse files- src/App.js +164 -127
src/App.js
CHANGED
|
@@ -16,6 +16,8 @@ import {
|
|
| 16 |
import { downloadProjectZip } from "./zipExport";
|
| 17 |
import { parseProblems } from "./problemParser";
|
| 18 |
import "./App.css";
|
|
|
|
|
|
|
| 19 |
|
| 20 |
// =================== SUPPORTED LANGUAGES ===================
|
| 21 |
const LANGUAGE_OPTIONS = [
|
|
@@ -50,20 +52,40 @@ function outputLooksForInput(output) {
|
|
| 50 |
return patterns.some((p) => p.test(o));
|
| 51 |
}
|
| 52 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
// =================== APP ===================
|
| 54 |
function App() {
|
| 55 |
const [tree, setTree] = useState(loadTree());
|
| 56 |
const [activePath, setActivePath] = useState("main.py"); // selected file or folder path
|
|
|
|
| 57 |
// Terminal-ish state
|
| 58 |
-
const [terminalLines, setTerminalLines] = useState([]); // array of strings shown in terminal
|
| 59 |
-
const [terminalInput, setTerminalInput] = useState(""); // current typed input in terminal prompt
|
| 60 |
const [accumStdin, setAccumStdin] = useState(""); // accumulated stdin passed to backend
|
| 61 |
const [awaitingInput, setAwaitingInput] = useState(false); // true if program waiting for input
|
| 62 |
-
const [output, setOutput] = useState(""); // last raw output (
|
| 63 |
const [prompt, setPrompt] = useState("");
|
| 64 |
const [explanation, setExplanation] = useState("");
|
| 65 |
-
|
| 66 |
-
|
|
|
|
|
|
|
| 67 |
const [problems, setProblems] = useState([]);
|
| 68 |
const [theme, setTheme] = useState("vs-dark");
|
| 69 |
const [searchOpen, setSearchOpen] = useState(false);
|
|
@@ -200,94 +222,108 @@ function App() {
|
|
| 200 |
e.target.value = "";
|
| 201 |
};
|
| 202 |
|
| 203 |
-
// ---------- Terminal
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
if (!keepAccum) {
|
| 214 |
-
setAccumStdin("");
|
| 215 |
-
}
|
| 216 |
-
setAwaitingInput(false);
|
| 217 |
-
};
|
| 218 |
-
|
| 219 |
-
const appendTerminal = (text) => {
|
| 220 |
-
setTerminalLines((prev) => [...prev, text]);
|
| 221 |
};
|
| 222 |
|
| 223 |
-
//
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
| 227 |
-
const
|
| 228 |
-
|
| 229 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 230 |
}
|
| 231 |
-
|
| 232 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 233 |
}
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 237 |
}
|
| 238 |
-
|
| 239 |
-
return /\binput\b|\bScanner\b|\bnextInt\b|prompt\(/i.test(c);
|
| 240 |
-
} catch {
|
| 241 |
-
return false;
|
| 242 |
-
}
|
| 243 |
-
}
|
| 244 |
|
|
|
|
| 245 |
const handleRun = async () => {
|
| 246 |
-
|
| 247 |
-
|
| 248 |
-
|
| 249 |
-
return;
|
| 250 |
-
}
|
| 251 |
-
const selectedLang = LANGUAGE_OPTIONS.find((l) => node.name.endsWith(l.ext))?.id;
|
| 252 |
-
if (!selectedLang || !RUNNABLE_LANGS.includes(selectedLang)) {
|
| 253 |
-
setOutput(`⚠️ Run not supported for this file type.`);
|
| 254 |
-
return;
|
| 255 |
-
}
|
| 256 |
-
|
| 257 |
-
// If code likely needs input and accumStdin is empty, ask for initial input
|
| 258 |
-
const needs = codeNeedsInput(node.content, selectedLang);
|
| 259 |
-
if (needs && !accumStdin) {
|
| 260 |
-
// collect multi-line input: user can paste multiple lines separated by \n
|
| 261 |
-
const userText = window.prompt(
|
| 262 |
-
"This program appears to request console input (e.g. input() / Scanner).\n\nEnter input lines separated by a newline (\\n). Leave empty to run without input.",
|
| 263 |
-
""
|
| 264 |
-
);
|
| 265 |
-
if (userText == null) {
|
| 266 |
-
// user pressed cancel: proceed but warn
|
| 267 |
-
appendTerminal("[Run cancelled by user]");
|
| 268 |
return;
|
| 269 |
}
|
| 270 |
-
|
| 271 |
-
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 275 |
|
| 276 |
-
// clear terminal state and start a fresh run
|
| 277 |
-
resetTerminal();
|
| 278 |
setIsRunning(true);
|
| 279 |
setProblems([]);
|
| 280 |
-
setOutput("");
|
| 281 |
try {
|
| 282 |
-
//
|
| 283 |
-
const res = await runCode(node.content, selectedLang,
|
| 284 |
const out = res.output ?? "";
|
| 285 |
setOutput(out);
|
| 286 |
-
appendTerminal(out);
|
| 287 |
setProblems(res.error ? parseProblems(res.output) : []);
|
| 288 |
-
|
| 289 |
if (outputLooksForInput(out)) {
|
| 290 |
-
// program likely wants input: show prompt
|
| 291 |
setAwaitingInput(true);
|
| 292 |
} else {
|
| 293 |
setAwaitingInput(false);
|
|
@@ -295,53 +331,46 @@ function codeNeedsInput(code, langId) {
|
|
| 295 |
} catch (err) {
|
| 296 |
const e = String(err);
|
| 297 |
setOutput(e);
|
| 298 |
-
appendTerminal(e);
|
| 299 |
setAwaitingInput(false);
|
| 300 |
} finally {
|
| 301 |
setIsRunning(false);
|
| 302 |
}
|
| 303 |
};
|
| 304 |
|
| 305 |
-
// Called when user types input into terminal and presses
|
| 306 |
const sendTerminalInput = async () => {
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
|
| 312 |
-
|
| 313 |
-
// If not already in awaitingInput (heuristic missed), still allow sending input:
|
| 314 |
-
if (!awaitingInput && !isRunning && terminalInput.trim()) {
|
| 315 |
-
appendTerminal("[Info] Sending input to program (re-run).");
|
| 316 |
-
}
|
| 317 |
|
| 318 |
-
|
| 319 |
-
|
| 320 |
-
|
| 321 |
-
|
| 322 |
-
|
| 323 |
-
|
| 324 |
-
|
| 325 |
-
|
| 326 |
-
|
| 327 |
-
|
| 328 |
-
|
| 329 |
-
|
| 330 |
-
|
| 331 |
-
|
| 332 |
-
|
| 333 |
-
|
|
|
|
|
|
|
|
|
|
| 334 |
setAwaitingInput(false);
|
|
|
|
|
|
|
| 335 |
}
|
| 336 |
-
}
|
| 337 |
-
appendTerminal(String(err));
|
| 338 |
-
setAwaitingInput(false);
|
| 339 |
-
} finally {
|
| 340 |
-
setIsRunning(false);
|
| 341 |
-
}
|
| 342 |
-
};
|
| 343 |
|
| 344 |
-
// Allow pressing Enter to send input
|
| 345 |
const onTerminalKeyDown = (e) => {
|
| 346 |
if (e.key === "Enter") {
|
| 347 |
e.preventDefault();
|
|
@@ -528,20 +557,34 @@ function codeNeedsInput(code, langId) {
|
|
| 528 |
|
| 529 |
{/* Bottom panels */}
|
| 530 |
<div className="ide-panels">
|
| 531 |
-
{/* Terminal
|
| 532 |
-
<div
|
| 533 |
<div style={{ fontSize: 12, color: "#ccc", marginBottom: 6 }}>Terminal</div>
|
| 534 |
-
<div className="terminal-content" style={{ background: "#000", color: "#0f0", padding: 8, borderRadius: 6, maxHeight: 220, overflowY: "auto", whiteSpace: "pre-wrap", fontFamily: "Consolas, monospace" }}>
|
| 535 |
-
{terminalLines.length === 0 ? <div style={{ color: "#999" }}>[Program output will appear here]</div> : terminalLines.map((ln, i) => <div key={i}>{ln}</div>)}
|
| 536 |
-
{awaitingInput && <div style={{ color: "#fff" }}> </div>}
|
| 537 |
-
</div>
|
| 538 |
|
| 539 |
-
{/*
|
| 540 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 541 |
<div style={{ display: "flex", gap: 6, marginTop: 6 }}>
|
| 542 |
<input
|
| 543 |
className="ide-input-box"
|
| 544 |
-
placeholder="Type input and press
|
| 545 |
value={terminalInput}
|
| 546 |
onChange={(e) => setTerminalInput(e.target.value)}
|
| 547 |
onKeyDown={onTerminalKeyDown}
|
|
@@ -549,12 +592,6 @@ function codeNeedsInput(code, langId) {
|
|
| 549 |
/>
|
| 550 |
<button onClick={sendTerminalInput} disabled={!awaitingInput || isRunning} className="ide-button">Send</button>
|
| 551 |
</div>
|
| 552 |
-
) : (
|
| 553 |
-
// If not awaiting input, show legacy single-run input + hint to run for interactive programs
|
| 554 |
-
<div style={{ display: "flex", gap: 6, marginTop: 6 }}>
|
| 555 |
-
<input className="ide-input-box" placeholder="(Optional) Program input for single-run" value={stdin} onChange={(e) => setStdin(e.target.value)} />
|
| 556 |
-
<div style={{ alignSelf: "center", color: "#999", fontSize: 12 }}>Press Run → to execute</div>
|
| 557 |
-
</div>
|
| 558 |
)}
|
| 559 |
</div>
|
| 560 |
|
|
|
|
| 16 |
import { downloadProjectZip } from "./zipExport";
|
| 17 |
import { parseProblems } from "./problemParser";
|
| 18 |
import "./App.css";
|
| 19 |
+
import "xterm/css/xterm.css";
|
| 20 |
+
import XTerm from "./Terminal"; // <-- your terminal component
|
| 21 |
|
| 22 |
// =================== SUPPORTED LANGUAGES ===================
|
| 23 |
const LANGUAGE_OPTIONS = [
|
|
|
|
| 52 |
return patterns.some((p) => p.test(o));
|
| 53 |
}
|
| 54 |
|
| 55 |
+
function codeNeedsInput(code, langId) {
|
| 56 |
+
if (!code) return false;
|
| 57 |
+
try {
|
| 58 |
+
const c = code.toString();
|
| 59 |
+
if (langId === "python") {
|
| 60 |
+
return /\binput\s*\(/i.test(c);
|
| 61 |
+
}
|
| 62 |
+
if (langId === "java") {
|
| 63 |
+
return /\bScanner\s*\(|\bnext(Int|Line|Double)\b/i.test(c) || /\bSystem\.console\(\)/i.test(c);
|
| 64 |
+
}
|
| 65 |
+
if (langId === "javascript") {
|
| 66 |
+
return /process\.stdin|readline|prompt\(|window\.prompt/i.test(c);
|
| 67 |
+
}
|
| 68 |
+
return /\binput\b|\bScanner\b|\bnextInt\b|prompt\(/i.test(c);
|
| 69 |
+
} catch {
|
| 70 |
+
return false;
|
| 71 |
+
}
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
// =================== APP ===================
|
| 75 |
function App() {
|
| 76 |
const [tree, setTree] = useState(loadTree());
|
| 77 |
const [activePath, setActivePath] = useState("main.py"); // selected file or folder path
|
| 78 |
+
|
| 79 |
// Terminal-ish state
|
|
|
|
|
|
|
| 80 |
const [accumStdin, setAccumStdin] = useState(""); // accumulated stdin passed to backend
|
| 81 |
const [awaitingInput, setAwaitingInput] = useState(false); // true if program waiting for input
|
| 82 |
+
const [output, setOutput] = useState(""); // last raw output (passed to XTerm)
|
| 83 |
const [prompt, setPrompt] = useState("");
|
| 84 |
const [explanation, setExplanation] = useState("");
|
| 85 |
+
|
| 86 |
+
// legacy/other UI state
|
| 87 |
+
const [terminalInput, setTerminalInput] = useState("");
|
| 88 |
+
const [stdin, setStdin] = useState(""); // optional single-run input
|
| 89 |
const [problems, setProblems] = useState([]);
|
| 90 |
const [theme, setTheme] = useState("vs-dark");
|
| 91 |
const [searchOpen, setSearchOpen] = useState(false);
|
|
|
|
| 222 |
e.target.value = "";
|
| 223 |
};
|
| 224 |
|
| 225 |
+
// ---------- Terminal helpers ----------
|
| 226 |
+
const resetTerminal = (keepAccum = false) => {
|
| 227 |
+
// We clear output state (XTerm will reflect this)
|
| 228 |
+
setOutput("");
|
| 229 |
+
// only reset accumulated stdin if caller explicitly wants to clear it
|
| 230 |
+
if (!keepAccum) {
|
| 231 |
+
setAccumStdin("");
|
| 232 |
+
}
|
| 233 |
+
setAwaitingInput(false);
|
| 234 |
+
setTerminalInput("");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 235 |
};
|
| 236 |
|
| 237 |
+
// runCodeWithUpdatedInput:
|
| 238 |
+
// called when a line is typed in XTerm (onData)
|
| 239 |
+
const runCodeWithUpdatedInput = async (inputLine) => {
|
| 240 |
+
// echoing is handled by XTerm itself; here we update accumStdin and run
|
| 241 |
+
const newAccum = (accumStdin || "") + inputLine + "\n";
|
| 242 |
+
setAccumStdin(newAccum);
|
| 243 |
+
|
| 244 |
+
const node = getNodeByPath(tree, activePath);
|
| 245 |
+
if (!node || node.type !== "file") {
|
| 246 |
+
// write a small error into output so XTerm displays it
|
| 247 |
+
setOutput((prev) => prev + "\n[Error] No file selected to run.");
|
| 248 |
+
setAwaitingInput(false);
|
| 249 |
+
return;
|
| 250 |
}
|
| 251 |
+
|
| 252 |
+
const selectedLang = LANGUAGE_OPTIONS.find((l) => node.name.endsWith(l.ext))?.id;
|
| 253 |
+
if (!selectedLang || !RUNNABLE_LANGS.includes(selectedLang)) {
|
| 254 |
+
setOutput((prev) => prev + `\n[Error] Run not supported for ${node.name}`);
|
| 255 |
+
setAwaitingInput(false);
|
| 256 |
+
return;
|
| 257 |
}
|
| 258 |
+
|
| 259 |
+
setIsRunning(true);
|
| 260 |
+
try {
|
| 261 |
+
const res = await runCode(node.content, selectedLang, newAccum);
|
| 262 |
+
const out = res.output ?? "";
|
| 263 |
+
setOutput(out);
|
| 264 |
+
setProblems(res.error ? parseProblems(res.output) : []);
|
| 265 |
+
if (outputLooksForInput(out)) {
|
| 266 |
+
setAwaitingInput(true);
|
| 267 |
+
} else {
|
| 268 |
+
setAwaitingInput(false);
|
| 269 |
+
}
|
| 270 |
+
} catch (err) {
|
| 271 |
+
setOutput(String(err));
|
| 272 |
+
setAwaitingInput(false);
|
| 273 |
+
} finally {
|
| 274 |
+
setIsRunning(false);
|
| 275 |
}
|
| 276 |
+
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 277 |
|
| 278 |
+
// ---------- Run (initial) ----------
|
| 279 |
const handleRun = async () => {
|
| 280 |
+
const node = getNodeByPath(tree, activePath);
|
| 281 |
+
if (!node || node.type !== "file") {
|
| 282 |
+
setOutput("Select a file to run.");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 283 |
return;
|
| 284 |
}
|
| 285 |
+
const selectedLang = LANGUAGE_OPTIONS.find((l) => node.name.endsWith(l.ext))?.id;
|
| 286 |
+
if (!selectedLang || !RUNNABLE_LANGS.includes(selectedLang)) {
|
| 287 |
+
setOutput(`⚠️ Run not supported for this file type.`);
|
| 288 |
+
return;
|
| 289 |
+
}
|
| 290 |
+
|
| 291 |
+
// If code likely needs input and accumStdin is empty, ask for initial input
|
| 292 |
+
const needs = codeNeedsInput(node.content, selectedLang);
|
| 293 |
+
let stdinToSend = accumStdin || stdin || "";
|
| 294 |
+
|
| 295 |
+
if (needs && !stdinToSend) {
|
| 296 |
+
const userText = window.prompt(
|
| 297 |
+
"This program appears to request console input (e.g. input() / Scanner).\n\nEnter input lines — use Enter for new lines or paste multi-line input. Leave empty to run without input.",
|
| 298 |
+
""
|
| 299 |
+
);
|
| 300 |
+
|
| 301 |
+
if (userText === null) {
|
| 302 |
+
// user cancelled prompt -> do nothing (abort run)
|
| 303 |
+
setOutput((prev) => prev + "\n[Run cancelled by user]");
|
| 304 |
+
return;
|
| 305 |
+
}
|
| 306 |
+
|
| 307 |
+
// normalize any literal "\n" sequences into real newlines (helps users pasting)
|
| 308 |
+
const prepared = userText.replace(/\\n/g, "\n");
|
| 309 |
+
stdinToSend = prepared;
|
| 310 |
+
// set accumStdin state (so subsequent interactive sends append to it)
|
| 311 |
+
setAccumStdin(prepared);
|
| 312 |
+
}
|
| 313 |
+
|
| 314 |
+
// start fresh terminal output but keep accumulated stdin
|
| 315 |
+
resetTerminal(true);
|
| 316 |
+
setOutput(`[Running with stdin length=${stdinToSend ? stdinToSend.length : 0}]\n`);
|
| 317 |
|
|
|
|
|
|
|
| 318 |
setIsRunning(true);
|
| 319 |
setProblems([]);
|
|
|
|
| 320 |
try {
|
| 321 |
+
// IMPORTANT: use local stdinToSend (not state) so the run uses the value right away
|
| 322 |
+
const res = await runCode(node.content, selectedLang, stdinToSend);
|
| 323 |
const out = res.output ?? "";
|
| 324 |
setOutput(out);
|
|
|
|
| 325 |
setProblems(res.error ? parseProblems(res.output) : []);
|
|
|
|
| 326 |
if (outputLooksForInput(out)) {
|
|
|
|
| 327 |
setAwaitingInput(true);
|
| 328 |
} else {
|
| 329 |
setAwaitingInput(false);
|
|
|
|
| 331 |
} catch (err) {
|
| 332 |
const e = String(err);
|
| 333 |
setOutput(e);
|
|
|
|
| 334 |
setAwaitingInput(false);
|
| 335 |
} finally {
|
| 336 |
setIsRunning(false);
|
| 337 |
}
|
| 338 |
};
|
| 339 |
|
| 340 |
+
// Called when user types input into the legacy terminal input box and presses Send
|
| 341 |
const sendTerminalInput = async () => {
|
| 342 |
+
if (!currentNode || currentNode.type !== "file") {
|
| 343 |
+
setOutput((prev) => prev + "\nNo file selected.");
|
| 344 |
+
setAwaitingInput(false);
|
| 345 |
+
return;
|
| 346 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 347 |
|
| 348 |
+
const userText = terminalInput;
|
| 349 |
+
setOutput((prev) => prev + `\n> ${userText}`);
|
| 350 |
+
const newAccum = (accumStdin || "") + userText + "\n";
|
| 351 |
+
setAccumStdin(newAccum);
|
| 352 |
+
setTerminalInput("");
|
| 353 |
+
setIsRunning(true);
|
| 354 |
+
try {
|
| 355 |
+
const selectedLang = LANGUAGE_OPTIONS.find((l) => currentNode.name.endsWith(l.ext))?.id;
|
| 356 |
+
const res = await runCode(currentNode.content, selectedLang, newAccum);
|
| 357 |
+
const out = res.output ?? "";
|
| 358 |
+
setOutput(out);
|
| 359 |
+
setProblems(res.error ? parseProblems(res.output) : []);
|
| 360 |
+
if (outputLooksForInput(out)) {
|
| 361 |
+
setAwaitingInput(true);
|
| 362 |
+
} else {
|
| 363 |
+
setAwaitingInput(false);
|
| 364 |
+
}
|
| 365 |
+
} catch (err) {
|
| 366 |
+
setOutput((prev) => prev + "\n" + String(err));
|
| 367 |
setAwaitingInput(false);
|
| 368 |
+
} finally {
|
| 369 |
+
setIsRunning(false);
|
| 370 |
}
|
| 371 |
+
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 372 |
|
| 373 |
+
// Allow pressing Enter to send input for legacy input box
|
| 374 |
const onTerminalKeyDown = (e) => {
|
| 375 |
if (e.key === "Enter") {
|
| 376 |
e.preventDefault();
|
|
|
|
| 557 |
|
| 558 |
{/* Bottom panels */}
|
| 559 |
<div className="ide-panels">
|
| 560 |
+
{/* Terminal (XTerm) */}
|
| 561 |
+
<div style={{ marginBottom: 8 }}>
|
| 562 |
<div style={{ fontSize: 12, color: "#ccc", marginBottom: 6 }}>Terminal</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
| 563 |
|
| 564 |
+
{/* XTerm shows the terminal and accepts input inline.
|
| 565 |
+
onData calls runCodeWithUpdatedInput which appends stdin and re-runs */}
|
| 566 |
+
<XTerm
|
| 567 |
+
output={output}
|
| 568 |
+
onData={(line) => {
|
| 569 |
+
// runCodeWithUpdatedInput handles accumulation and re-run
|
| 570 |
+
runCodeWithUpdatedInput(line);
|
| 571 |
+
}}
|
| 572 |
+
/>
|
| 573 |
+
|
| 574 |
+
{/* legacy input fallback - still available if user prefers box */}
|
| 575 |
+
{!awaitingInput && (
|
| 576 |
+
<div style={{ display: "flex", gap: 6, marginTop: 6 }}>
|
| 577 |
+
<input className="ide-input-box" placeholder="(Optional) Program input for single-run" value={stdin} onChange={(e) => setStdin(e.target.value)} />
|
| 578 |
+
<div style={{ alignSelf: "center", color: "#999", fontSize: 12 }}>Press Run → to execute</div>
|
| 579 |
+
</div>
|
| 580 |
+
)}
|
| 581 |
+
|
| 582 |
+
{/* If using legacy input box to send while awaitingInput */}
|
| 583 |
+
{awaitingInput && (
|
| 584 |
<div style={{ display: "flex", gap: 6, marginTop: 6 }}>
|
| 585 |
<input
|
| 586 |
className="ide-input-box"
|
| 587 |
+
placeholder="Type input and press Send..."
|
| 588 |
value={terminalInput}
|
| 589 |
onChange={(e) => setTerminalInput(e.target.value)}
|
| 590 |
onKeyDown={onTerminalKeyDown}
|
|
|
|
| 592 |
/>
|
| 593 |
<button onClick={sendTerminalInput} disabled={!awaitingInput || isRunning} className="ide-button">Send</button>
|
| 594 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 595 |
)}
|
| 596 |
</div>
|
| 597 |
|