Skip to content

Commit bc81e33

Browse files
committed
Refactor code execution and change output box ui
1 parent f722f6a commit bc81e33

File tree

11 files changed

+163
-48
lines changed

11 files changed

+163
-48
lines changed

frontend/src/data/piston/PistonClient.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,25 @@
11
import axios from "axios";
2+
import { CodeExecResult } from "domain/entities/CodeExecResult";
23
import { Language } from "domain/entities/Language";
34

45
const API_URL = "https://emkc.org/api/v2/piston";
56

67
const pistonAxios = axios.create({ baseURL: API_URL });
78

89
class PistonClient {
9-
static async executeCode(language: Language, sourceCode: string) {
10+
static async executeCode(language: Language, sourceCode: string): Promise<CodeExecResult> {
1011
const res = await pistonAxios.post("/execute", {
1112
language: language.language,
1213
version: language.version,
1314
files: [{ content: sourceCode }]
1415
});
1516
const output = res.data.run;
16-
return { stdout: output.stdout, stderr: output.stderr };
17+
return {
18+
stdout: output.stdout,
19+
stderr: output.stderr,
20+
success: output.code === 0,
21+
timeout: output.signal !== null
22+
};
1723
}
1824

1925
static async getLanguageVersions() {

frontend/src/domain/context/CollaborationContext.tsx

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,18 @@ import { toast } from "react-toastify";
99
import { COLLABORATION_AWARENESS_KEYS, COLLABORATION_YMAP_KEYS } from "presentation/utils/constants";
1010
import PistonClient from "data/piston/PistonClient";
1111
import { Language } from "domain/entities/Language";
12+
import { CodeExecResult } from "domain/entities/CodeExecResult";
1213

1314
interface CollaborationContextType {
1415
initialiseEditor: (roomId: string, editor: any, monaco: Monaco) => void;
1516
selectedLanguage: Language;
1617
languages: Language[];
1718
handleChangeLanguage: (lang: Language) => void;
18-
handleExecuteCode: () => void;
19-
stdout: string;
20-
stderr: string;
19+
handleExecuteCode: () => Promise<void>;
20+
isExecuting: boolean;
21+
execResult: CodeExecResult | null;
2122
}
23+
2224
const CollaborationContext = createContext<CollaborationContextType | undefined>(undefined);
2325

2426
export const CollaborationProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
@@ -35,8 +37,8 @@ export const CollaborationProvider: React.FC<{ children: ReactNode }> = ({ child
3537
});
3638
const [languages, setLanguages] = useState<Language[]>([]);
3739

38-
const [stdout, setStdout] = useState<string>("");
39-
const [stderr, setStderr] = useState<string>("");
40+
const [execResult, setExecResult] = useState<CodeExecResult | null>(null);
41+
4042
const [isExecuting, setIsExecuting] = useState<boolean>(false);
4143

4244
const editorRef = useRef<monaco.editor.IStandaloneCodeEditor | null>(null);
@@ -158,10 +160,8 @@ export const CollaborationProvider: React.FC<{ children: ReactNode }> = ({ child
158160
// TODO
159161
return;
160162
}
161-
const output = await PistonClient.executeCode(selectedLanguage, sourceCode);
162-
const { stdout, stderr } = output;
163-
setStdout(stdout);
164-
setStderr(stderr);
163+
const output: CodeExecResult = await PistonClient.executeCode(selectedLanguage, sourceCode);
164+
setExecResult(output);
165165
} catch (e) {
166166
toast.error("There was an issue running the code");
167167
} finally {
@@ -177,8 +177,8 @@ export const CollaborationProvider: React.FC<{ children: ReactNode }> = ({ child
177177
languages,
178178
handleChangeLanguage,
179179
handleExecuteCode,
180-
stdout,
181-
stderr
180+
isExecuting,
181+
execResult
182182
}}
183183
>
184184
{children}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export interface CodeExecResult {
2+
stdout: string;
3+
stderr: string;
4+
success: boolean;
5+
timeout: boolean;
6+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
.submitButton {
2+
background-color: #ffa500;
3+
}
4+
5+
.runButton {
6+
background-color: black;
7+
color: white;
8+
}
9+
10+
button {
11+
width: fit-content;
12+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { Button } from "antd";
2+
import { useCollaboration } from "domain/context/CollaborationContext";
3+
import { PlayCircleOutlined, CloudUploadOutlined } from "@ant-design/icons";
4+
import styles from "./CodeActionButtons.module.css";
5+
6+
interface CodeActionButtonsProps {
7+
disabled?: boolean;
8+
}
9+
export const CodeActionButtons: React.FC<CodeActionButtonsProps> = ({ disabled = false }) => {
10+
const { handleExecuteCode } = useCollaboration();
11+
return (
12+
<>
13+
<Button
14+
onClick={async () => {
15+
await handleExecuteCode();
16+
}}
17+
className={styles.runButton}
18+
icon={<PlayCircleOutlined />}
19+
disabled={disabled}
20+
>
21+
Run
22+
</Button>
23+
<Button className={styles.submitButton} icon={<CloudUploadOutlined />} disabled={disabled}>
24+
Submit
25+
</Button>
26+
</>
27+
);
28+
};

frontend/src/presentation/components/CodeEditor/CodeEditor.module.css

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@
99

1010
.toolbar {
1111
padding: 4px;
12+
height: 40px;
1213
display: flex;
1314
width: 100%;
1415
justify-content: space-between;
16+
align-items: center;
1517
}
1618

1719
.editor {
@@ -25,15 +27,12 @@
2527
gap: 8px;
2628
}
2729

28-
button {
29-
width: fit-content;
30-
}
31-
32-
.submitButton {
33-
background-color: #ffa500;
30+
.pendingExecution {
31+
display: flex;
32+
align-items: center;
33+
gap: 8px;
3434
}
3535

36-
.runButton {
37-
background-color: black;
38-
color: white;
36+
.pendingExecution p {
37+
padding: 0;
3938
}

frontend/src/presentation/components/CodeEditor/CodeEditor.tsx

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,20 @@
1-
import React, { useMemo, useState } from "react";
1+
import React, { useState } from "react";
22
import styles from "./CodeEditor.module.css";
33
import Editor, { Monaco } from "@monaco-editor/react";
4-
import { Button, Select } from "antd";
4+
import { Button, Spin } from "antd";
55
import { useCollaboration } from "domain/context/CollaborationContext";
66
import { PlayCircleOutlined, CloudUploadOutlined } from "@ant-design/icons";
77
import * as monaco from "monaco-editor";
88
import { SunOutlined, MoonFilled } from "@ant-design/icons";
9-
import { Language } from "domain/entities/Language";
109
import { LanguageSelector } from "./LanguageSelector";
10+
import { CodeActionButtons } from "./CodeActionButtons";
1111

1212
interface CodeEditorProps {
1313
roomId: string;
1414
}
1515

16-
interface LanguageOption {
17-
label: string;
18-
value: string;
19-
langData: Language;
20-
}
21-
2216
const CodeEditor: React.FC<CodeEditorProps> = ({ roomId }) => {
23-
const { initialiseEditor, handleExecuteCode } = useCollaboration();
17+
const { initialiseEditor, isExecuting } = useCollaboration();
2418
const [theme, setTheme] = useState("vs-light");
2519
const handleEditorDidMount = (editor: monaco.editor.IStandaloneCodeEditor, monaco: Monaco) => {
2620
initialiseEditor(roomId, editor, monaco);
@@ -38,19 +32,20 @@ const CodeEditor: React.FC<CodeEditorProps> = ({ roomId }) => {
3832
<div className={styles.container}>
3933
<div className={styles.toolbar}>
4034
<LanguageSelector />
35+
{isExecuting && (
36+
<div className={styles.pendingExecution}>
37+
<Spin />
38+
<p>Running...</p>
39+
</div>
40+
)}
4141
<div className={styles.buttonGroup}>
4242
<Button
4343
onClick={handleToggleTheme}
4444
type="text"
4545
icon={theme === "vs-light" ? <SunOutlined /> : <MoonFilled />}
4646
/>
4747

48-
<Button onClick={handleExecuteCode} className={styles.runButton} icon={<PlayCircleOutlined />}>
49-
Run
50-
</Button>
51-
<Button className={styles.submitButton} icon={<CloudUploadOutlined />}>
52-
Submit
53-
</Button>
48+
<CodeActionButtons disabled={isExecuting} />
5449
</div>
5550
</div>
5651
<div className={styles.editor}>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
.content {
2+
white-space: pre-wrap;
3+
}
4+
5+
.status {
6+
font-weight: 600;
7+
}
8+
9+
.successStatus {
10+
color: green;
11+
}
12+
13+
.failStatus {
14+
color: red;
15+
}
16+
17+
.header {
18+
display: flex;
19+
align-items: center;
20+
gap: 16px;
21+
}
22+
23+
.container {
24+
border: 2px solid transparent;
25+
transition: border-color 0.5s ease;
26+
padding: 0 16px;
27+
height: 100%;
28+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { useCollaboration } from "domain/context/CollaborationContext";
2+
import styles from "./OutputBox.module.css";
3+
import { useEffect, useState } from "react";
4+
5+
export const OutputBox: React.FC<{}> = () => {
6+
const { execResult } = useCollaboration();
7+
const hasExecuted = execResult !== null;
8+
const isSuccess = execResult?.success;
9+
const isTimeout = execResult?.timeout;
10+
const stdout = execResult?.stdout;
11+
const stderr = execResult?.stderr;
12+
13+
// Output box flash color when it receives an output
14+
const [flashColor, setFlashColor] = useState<"red" | "green" | null>(null);
15+
useEffect(() => {
16+
if (execResult) {
17+
setFlashColor(execResult?.success ? "green" : "red");
18+
const id = setTimeout(() => setFlashColor(null), 500);
19+
return () => clearTimeout(id);
20+
}
21+
}, [execResult]);
22+
23+
return (
24+
<div className={`${styles.container} `} style={{ borderColor: flashColor ?? "transparent" }}>
25+
<div className={styles.header}>
26+
<h3>Output</h3>
27+
{hasExecuted && (
28+
<div className={styles.status}>
29+
{isSuccess ? (
30+
<p className={styles.successStatus}>Success</p>
31+
) : (
32+
<p className={styles.failStatus}>{isTimeout ? "Time Limit Exceeded" : "Failed"}</p>
33+
)}
34+
</div>
35+
)}
36+
</div>
37+
<div className={styles.content}>
38+
{stdout && <p>{stdout}</p>}
39+
{stderr && <p>{stderr}</p>}
40+
</div>
41+
</div>
42+
);
43+
};

frontend/src/presentation/pages/CollaborationRoomPage.module.css

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
height: 6px;
4444
}
4545

46-
.output {
46+
.outputContainer {
4747
flex-shrink: 0;
48-
padding: 0 8px;
48+
overflow-y: auto;
4949
}

0 commit comments

Comments
 (0)