Spaces:
Running
Running
Commit ·
f949ee8
1
Parent(s): 921b162
commit initial 09-12-2025 019
Browse files- src/App.js +83 -41
src/App.js
CHANGED
|
@@ -217,17 +217,58 @@ function App() {
|
|
| 217 |
setTerminalLines((prev) => [...prev, text]);
|
| 218 |
};
|
| 219 |
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
|
|
|
|
|
|
|
| 225 |
}
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 229 |
return;
|
| 230 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 231 |
|
| 232 |
// clear terminal state and start a fresh run
|
| 233 |
resetTerminal();
|
|
@@ -260,41 +301,42 @@ function App() {
|
|
| 260 |
|
| 261 |
// Called when user types input into terminal and presses Enter
|
| 262 |
const sendTerminalInput = async () => {
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
|
| 268 |
-
|
| 269 |
-
|
| 270 |
-
|
| 271 |
-
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
|
| 275 |
-
|
| 276 |
-
|
| 277 |
-
|
| 278 |
-
|
| 279 |
-
|
| 280 |
-
|
| 281 |
-
|
| 282 |
-
|
| 283 |
-
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
}
|
| 291 |
-
} catch (err) {
|
| 292 |
-
appendTerminal(String(err));
|
| 293 |
setAwaitingInput(false);
|
| 294 |
-
} finally {
|
| 295 |
-
setIsRunning(false);
|
| 296 |
}
|
| 297 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 298 |
|
| 299 |
// Allow pressing Enter to send input
|
| 300 |
const onTerminalKeyDown = (e) => {
|
|
|
|
| 217 |
setTerminalLines((prev) => [...prev, text]);
|
| 218 |
};
|
| 219 |
|
| 220 |
+
// helper: detect input calls in source (python/java/js)
|
| 221 |
+
function codeNeedsInput(code, langId) {
|
| 222 |
+
if (!code) return false;
|
| 223 |
+
try {
|
| 224 |
+
const c = code.toString();
|
| 225 |
+
if (langId === "python") {
|
| 226 |
+
return /\binput\s*\(/i.test(c);
|
| 227 |
}
|
| 228 |
+
if (langId === "java") {
|
| 229 |
+
return /\bScanner\s*\(|\bnext(Int|Line|Double|())\b/i.test(c) || /\bSystem\.console\(\)/i.test(c);
|
| 230 |
+
}
|
| 231 |
+
if (langId === "javascript") {
|
| 232 |
+
// node.js stdin reads or prompt usage heuristics
|
| 233 |
+
return /process\.stdin|readline|prompt\(|window\.prompt/i.test(c);
|
| 234 |
+
}
|
| 235 |
+
// fallback: look for common words
|
| 236 |
+
return /\binput\b|\bScanner\b|\bnextInt\b|prompt\(/i.test(c);
|
| 237 |
+
} catch {
|
| 238 |
+
return false;
|
| 239 |
+
}
|
| 240 |
+
}
|
| 241 |
+
|
| 242 |
+
const handleRun = async () => {
|
| 243 |
+
const node = getNodeByPath(tree, activePath);
|
| 244 |
+
if (!node || node.type !== "file") {
|
| 245 |
+
setOutput("Select a file to run.");
|
| 246 |
+
return;
|
| 247 |
+
}
|
| 248 |
+
const selectedLang = LANGUAGE_OPTIONS.find((l) => node.name.endsWith(l.ext))?.id;
|
| 249 |
+
if (!selectedLang || !RUNNABLE_LANGS.includes(selectedLang)) {
|
| 250 |
+
setOutput(`⚠️ Run not supported for this file type.`);
|
| 251 |
+
return;
|
| 252 |
+
}
|
| 253 |
+
|
| 254 |
+
// If code likely needs input and accumStdin is empty, ask for initial input
|
| 255 |
+
const needs = codeNeedsInput(node.content, selectedLang);
|
| 256 |
+
if (needs && !accumStdin) {
|
| 257 |
+
// collect multi-line input: user can paste multiple lines separated by \n
|
| 258 |
+
const userText = window.prompt(
|
| 259 |
+
"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.",
|
| 260 |
+
""
|
| 261 |
+
);
|
| 262 |
+
if (userText == null) {
|
| 263 |
+
// user pressed cancel: proceed but warn
|
| 264 |
+
appendTerminal("[Run cancelled by user]");
|
| 265 |
return;
|
| 266 |
}
|
| 267 |
+
// userText may contain literal backslash-n if pasted; keep it as-is.
|
| 268 |
+
// Normalize: if the user typed using Enter, it's already multi-line.
|
| 269 |
+
const prepared = userText.replace(/\\n/g, "\n");
|
| 270 |
+
setAccumStdin(prepared);
|
| 271 |
+
}
|
| 272 |
|
| 273 |
// clear terminal state and start a fresh run
|
| 274 |
resetTerminal();
|
|
|
|
| 301 |
|
| 302 |
// Called when user types input into terminal and presses Enter
|
| 303 |
const sendTerminalInput = async () => {
|
| 304 |
+
if (!currentNode || currentNode.type !== "file") {
|
| 305 |
+
appendTerminal("No file selected.");
|
| 306 |
+
setAwaitingInput(false);
|
| 307 |
+
return;
|
| 308 |
+
}
|
| 309 |
+
|
| 310 |
+
// If not already in awaitingInput (heuristic missed), still allow sending input:
|
| 311 |
+
if (!awaitingInput && !isRunning && terminalInput.trim()) {
|
| 312 |
+
appendTerminal("[Info] Sending input to program (re-run).");
|
| 313 |
+
}
|
| 314 |
+
|
| 315 |
+
const userText = terminalInput;
|
| 316 |
+
appendTerminal(`> ${userText}`);
|
| 317 |
+
const newAccum = (accumStdin || "") + userText + "\n";
|
| 318 |
+
setAccumStdin(newAccum);
|
| 319 |
+
setTerminalInput("");
|
| 320 |
+
setIsRunning(true);
|
| 321 |
+
try {
|
| 322 |
+
const selectedLang = LANGUAGE_OPTIONS.find((l) => currentNode.name.endsWith(l.ext))?.id;
|
| 323 |
+
const res = await runCode(currentNode.content, selectedLang, newAccum);
|
| 324 |
+
const out = res.output ?? "";
|
| 325 |
+
setOutput(out);
|
| 326 |
+
appendTerminal(out);
|
| 327 |
+
setProblems(res.error ? parseProblems(res.output) : []);
|
| 328 |
+
if (outputLooksForInput(out)) {
|
| 329 |
+
setAwaitingInput(true);
|
| 330 |
+
} else {
|
|
|
|
|
|
|
|
|
|
| 331 |
setAwaitingInput(false);
|
|
|
|
|
|
|
| 332 |
}
|
| 333 |
+
} catch (err) {
|
| 334 |
+
appendTerminal(String(err));
|
| 335 |
+
setAwaitingInput(false);
|
| 336 |
+
} finally {
|
| 337 |
+
setIsRunning(false);
|
| 338 |
+
}
|
| 339 |
+
};
|
| 340 |
|
| 341 |
// Allow pressing Enter to send input
|
| 342 |
const onTerminalKeyDown = (e) => {
|