|
| 1 | +"use client"; |
| 2 | + |
| 3 | +import chalk from "chalk"; |
| 4 | +import { usePyodide } from "./python/pyodide"; |
| 5 | +import { clearTerminal, getRows, useTerminal } from "./terminal"; |
| 6 | + |
| 7 | +interface ExecProps { |
| 8 | + filename: string; |
| 9 | + language: string; |
| 10 | + content: string; |
| 11 | +} |
| 12 | +export function ExecFile(props: ExecProps) { |
| 13 | + const { terminalRef, terminalInstanceRef, termReady } = useTerminal({ |
| 14 | + getRows: (cols: number) => getRows(props.content, cols), |
| 15 | + onReady: () => { |
| 16 | + // カーソル非表示 |
| 17 | + terminalInstanceRef.current!.write("\x1b[?25l"); |
| 18 | + for (const line of props.content.split("\n")) { |
| 19 | + terminalInstanceRef.current!.writeln(line); |
| 20 | + } |
| 21 | + }, |
| 22 | + }); |
| 23 | + |
| 24 | + const pyodide = usePyodide(); |
| 25 | + |
| 26 | + let commandline: string; |
| 27 | + let exec: () => Promise<void> | void; |
| 28 | + let runtimeInitializing: boolean; |
| 29 | + switch (props.language) { |
| 30 | + case "python": |
| 31 | + commandline = `python ${props.filename}`; |
| 32 | + runtimeInitializing = pyodide.initializing; |
| 33 | + exec = async () => { |
| 34 | + if (!pyodide.ready) { |
| 35 | + clearTerminal(terminalInstanceRef.current!); |
| 36 | + terminalInstanceRef.current!.write( |
| 37 | + chalk.dim.bold.italic("(初期化しています...しばらくお待ちください)") |
| 38 | + ); |
| 39 | + await pyodide.init(); |
| 40 | + } |
| 41 | + clearTerminal(terminalInstanceRef.current!); |
| 42 | + const outputs = await pyodide.runFile(props.filename); |
| 43 | + for (const output of outputs) { |
| 44 | + // 出力内容に応じて色を変える |
| 45 | + const message = String(output.message).replace(/\n/g, "\r\n"); |
| 46 | + switch (output.type) { |
| 47 | + case "error": |
| 48 | + terminalInstanceRef.current!.writeln(chalk.red(message)); |
| 49 | + break; |
| 50 | + default: |
| 51 | + terminalInstanceRef.current!.writeln(message); |
| 52 | + break; |
| 53 | + } |
| 54 | + } |
| 55 | + }; |
| 56 | + break; |
| 57 | + default: |
| 58 | + commandline = `エラー: 非対応の言語 ${props.language}`; |
| 59 | + runtimeInitializing = false; |
| 60 | + exec = () => undefined; |
| 61 | + break; |
| 62 | + } |
| 63 | + return ( |
| 64 | + <div className="relative"> |
| 65 | + <div> |
| 66 | + <button |
| 67 | + className="btn btn-soft btn-primary rounded-tl-lg rounded-none" |
| 68 | + onClick={exec} |
| 69 | + disabled={!termReady || runtimeInitializing} |
| 70 | + > |
| 71 | + ▶ 実行 |
| 72 | + </button> |
| 73 | + <code className="text-sm ml-4">{commandline}</code> |
| 74 | + </div> |
| 75 | + <div className="bg-base-300 p-4 pt-2 rounded-b-lg"> |
| 76 | + <div ref={terminalRef} /> |
| 77 | + </div> |
| 78 | + {runtimeInitializing && ( |
| 79 | + <div className="absolute z-10 inset-0 cursor-wait" /> |
| 80 | + )} |
| 81 | + </div> |
| 82 | + ); |
| 83 | +} |
0 commit comments