Implement Code Challenge component with styling and WebSocket integration
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
import VSCodeUI from "./components/VSCodeUI.jsx"
|
||||
import CodeChallenge from "./components/CodeChallenge.jsx"
|
||||
import "./index.css"
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<div className="App">
|
||||
<VSCodeUI />
|
||||
<CodeChallenge />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
282
Frontend/src/components/CodeChallenge.css
Normal file
282
Frontend/src/components/CodeChallenge.css
Normal file
@@ -0,0 +1,282 @@
|
||||
/* Code Challenge Component Styles */
|
||||
.code-challenge-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
background-color: #1e1e1e;
|
||||
color: #d4d4d4;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
}
|
||||
|
||||
.code-challenge-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 10px 20px;
|
||||
background-color: #1e1e1e;
|
||||
border-bottom: 1px solid #333;
|
||||
}
|
||||
|
||||
.code-challenge-header h1 {
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.sign-in-btn {
|
||||
background-color: #333;
|
||||
color: #fff;
|
||||
border: none;
|
||||
padding: 5px 10px;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.sign-in-btn:hover {
|
||||
background-color: #444;
|
||||
}
|
||||
|
||||
.code-challenge-problem-nav {
|
||||
padding: 10px 20px;
|
||||
border-bottom: 1px solid #333;
|
||||
}
|
||||
|
||||
.problem-number {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.code-challenge-main {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: calc(70vh - 120px);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.left-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 50%;
|
||||
border-right: 1px solid #333;
|
||||
}
|
||||
|
||||
.problem-tabs {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
border-bottom: 1px solid #333;
|
||||
}
|
||||
|
||||
.problem-tabs button {
|
||||
min-width: 120px;
|
||||
padding: 0 20px;
|
||||
background-color: #252526;
|
||||
color: #d4d4d4;
|
||||
border: none;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
border-right: 1px solid #333;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.problem-tabs button.tab-active {
|
||||
background-color: #1e1e1e;
|
||||
border-left: 2px solid #0078d4;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.problem-tabs button:hover:not(.tab-active) {
|
||||
background-color: #252526;
|
||||
}
|
||||
|
||||
.problem-content {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 20px;
|
||||
border-right: 1px solid #333;
|
||||
}
|
||||
|
||||
.problem-container h1 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 15px;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.problem-description {
|
||||
margin-bottom: 20px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.problem-description code {
|
||||
background-color: #2d2d2d;
|
||||
padding: 2px 4px;
|
||||
border-radius: 3px;
|
||||
font-family: 'Consolas', 'Courier New', monospace;
|
||||
}
|
||||
|
||||
/* Removed problem examples styles */
|
||||
|
||||
.editor-section {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-left: 1px solid #333;
|
||||
}
|
||||
|
||||
.editor-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
background-color: #252526;
|
||||
border-bottom: 1px solid #333;
|
||||
}
|
||||
|
||||
.editor-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.language-selector {
|
||||
background-color: #2d2d2d;
|
||||
color: #d4d4d4;
|
||||
border: 1px solid #3c3c3c;
|
||||
padding: 5px 10px;
|
||||
border-radius: 3px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.auto-btn {
|
||||
background-color: #252526;
|
||||
color: #d4d4d4;
|
||||
border: 1px solid #3c3c3c;
|
||||
padding: 5px 10px;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.auto-selected {
|
||||
background-color: #0e639c;
|
||||
border-color: #0e639c;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.editor-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.run-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: #252526;
|
||||
color: #d4d4d4;
|
||||
border: 1px solid #3c3c3c;
|
||||
padding: 5px 10px;
|
||||
border-radius: 3px;
|
||||
margin-right: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: #0e8a16;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 5px 10px;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.run-btn svg, .submit-btn svg {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.run-btn:hover {
|
||||
background-color: #2d2d2d;
|
||||
}
|
||||
|
||||
.submit-btn:hover {
|
||||
background-color: #0ca01c;
|
||||
}
|
||||
|
||||
.editor-container {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.terminal-section {
|
||||
height: 30vh;
|
||||
background-color: #1e1e1e;
|
||||
border-top: 1px solid #333;
|
||||
}
|
||||
|
||||
.terminal-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 5px 10px;
|
||||
background-color: #252526;
|
||||
border-bottom: 1px solid #333;
|
||||
}
|
||||
|
||||
.terminal-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.terminal-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: #d4d4d4;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.terminal-content {
|
||||
padding: 10px;
|
||||
font-family: 'Consolas', 'Courier New', monospace;
|
||||
height: calc(30vh - 40px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.terminal-line {
|
||||
margin-bottom: 5px;
|
||||
word-wrap: break-word;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.terminal-line.system {
|
||||
color: #569cd6;
|
||||
}
|
||||
|
||||
.terminal-line.output {
|
||||
color: #d4d4d4;
|
||||
}
|
||||
|
||||
.terminal-line.error {
|
||||
color: #f48771;
|
||||
}
|
||||
|
||||
.terminal-prompt {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.prompt-symbol {
|
||||
margin-right: 5px;
|
||||
color: #569cd6;
|
||||
}
|
||||
|
||||
.terminal-input {
|
||||
flex: 1;
|
||||
background: none;
|
||||
border: none;
|
||||
color: #d4d4d4;
|
||||
font-family: 'Consolas', 'Courier New', monospace;
|
||||
outline: none;
|
||||
}
|
||||
451
Frontend/src/components/CodeChallenge.jsx
Normal file
451
Frontend/src/components/CodeChallenge.jsx
Normal file
@@ -0,0 +1,451 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import Editor from "@monaco-editor/react";
|
||||
import { Play, Send } from 'lucide-react';
|
||||
|
||||
const CodeChallenge = () => {
|
||||
const [activeQuestion, setActiveQuestion] = useState("Q.1");
|
||||
const [language, setLanguage] = useState("JavaScript");
|
||||
const [code, setCode] = useState("");
|
||||
const [isRunning, setIsRunning] = useState(false);
|
||||
const [terminalOutput, setTerminalOutput] = useState([]);
|
||||
const [autoSelected, setAutoSelected] = useState(true);
|
||||
const [activeSocket, setActiveSocket] = useState(null);
|
||||
const [submissionId, setSubmissionId] = useState(null);
|
||||
const socketRef = useRef(null);
|
||||
|
||||
// Example problem data
|
||||
const problems = {
|
||||
"Q.1": {
|
||||
id: "two-sum",
|
||||
title: "Two Sum",
|
||||
description: "Given an array of integers nums and an integer target, return indices of the two numbers such that they add up to target.",
|
||||
constraints: "You may assume that each input would have exactly one solution, and you may not use the same element twice.",
|
||||
examples: [
|
||||
{
|
||||
input: "nums = [2,7,11,15], target = 9",
|
||||
output: "[0,1]",
|
||||
explanation: "Because nums[0] + nums[1] == 9, we return [0, 1]."
|
||||
},
|
||||
{
|
||||
input: "nums = [3,2,4], target = 6",
|
||||
output: "[1,2]"
|
||||
},
|
||||
{
|
||||
input: "nums = [3,3], target = 6",
|
||||
output: "[0,1]"
|
||||
}
|
||||
],
|
||||
starterCode: `/**
|
||||
* @param {number[]} nums
|
||||
* @param {number} target
|
||||
* @return {number[]}
|
||||
*/
|
||||
var twoSum = function(nums, target) {
|
||||
// Write your solution here
|
||||
|
||||
};`
|
||||
},
|
||||
"Q.2": {
|
||||
id: "palindrome-number",
|
||||
title: "Palindrome Number",
|
||||
description: "Given an integer x, return true if x is a palindrome, and false otherwise.",
|
||||
examples: [
|
||||
{
|
||||
input: "x = 121",
|
||||
output: "true"
|
||||
},
|
||||
{
|
||||
input: "x = -121",
|
||||
output: "false",
|
||||
explanation: "From left to right, it reads -121. From right to left, it reads 121-. Therefore it is not a palindrome."
|
||||
}
|
||||
],
|
||||
starterCode: `/**
|
||||
* @param {number} x
|
||||
* @return {boolean}
|
||||
*/
|
||||
var isPalindrome = function(x) {
|
||||
// Write your solution here
|
||||
|
||||
};`
|
||||
},
|
||||
"Q.3": {
|
||||
id: "valid-parentheses",
|
||||
title: "Valid Parentheses",
|
||||
description: "Given a string s containing just the characters '(', ')', '{', '}', '[' and ']', determine if the input string is valid.",
|
||||
constraints: "An input string is valid if: Open brackets must be closed by the same type of brackets. Open brackets must be closed in the correct order.",
|
||||
examples: [
|
||||
{
|
||||
input: 's = "()"',
|
||||
output: "true"
|
||||
},
|
||||
{
|
||||
input: 's = "()[]{}"',
|
||||
output: "true"
|
||||
},
|
||||
{
|
||||
input: 's = "(]"',
|
||||
output: "false"
|
||||
}
|
||||
],
|
||||
starterCode: `/**
|
||||
* @param {string} s
|
||||
* @return {boolean}
|
||||
*/
|
||||
var isValid = function(s) {
|
||||
// Write your solution here
|
||||
|
||||
};`
|
||||
}
|
||||
};
|
||||
|
||||
// Set initial code based on active problem
|
||||
useEffect(() => {
|
||||
if (problems[activeQuestion]) {
|
||||
setCode(problems[activeQuestion].starterCode);
|
||||
}
|
||||
}, [activeQuestion]);
|
||||
|
||||
// Cleanup WebSocket connection on unmount
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (socketRef.current) {
|
||||
socketRef.current.close();
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Connect to WebSocket
|
||||
const connectToWebSocket = (id) => {
|
||||
// Close existing connection if any
|
||||
if (socketRef.current && socketRef.current.readyState === WebSocket.OPEN) {
|
||||
socketRef.current.close();
|
||||
}
|
||||
|
||||
const wsUrl = `ws://localhost:8080/api/ws/terminal/${id}`;
|
||||
const socket = new WebSocket(wsUrl);
|
||||
|
||||
socket.onopen = () => {
|
||||
console.log('WebSocket connection established');
|
||||
setActiveSocket(socket);
|
||||
};
|
||||
|
||||
socket.onmessage = (event) => {
|
||||
try {
|
||||
const message = JSON.parse(event.data);
|
||||
|
||||
switch (message.type) {
|
||||
case 'output':
|
||||
setTerminalOutput(prev => [
|
||||
...prev,
|
||||
{
|
||||
type: message.content.isError ? 'error' : 'output',
|
||||
content: message.content.text
|
||||
}
|
||||
]);
|
||||
break;
|
||||
|
||||
case 'status':
|
||||
setTerminalOutput(prev => [
|
||||
...prev,
|
||||
{ type: 'system', content: `Status: ${message.content.status}` }
|
||||
]);
|
||||
|
||||
if (message.content.status === 'completed' || message.content.status === 'failed') {
|
||||
setIsRunning(false);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'error':
|
||||
setTerminalOutput(prev => [
|
||||
...prev,
|
||||
{ type: 'error', content: message.content.message }
|
||||
]);
|
||||
setIsRunning(false);
|
||||
break;
|
||||
|
||||
case 'system':
|
||||
setTerminalOutput(prev => [
|
||||
...prev,
|
||||
{ type: 'system', content: message.content }
|
||||
]);
|
||||
break;
|
||||
|
||||
default:
|
||||
console.log('Unknown message type:', message);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error parsing WebSocket message:', error);
|
||||
}
|
||||
};
|
||||
|
||||
socket.onerror = (error) => {
|
||||
console.error('WebSocket error:', error);
|
||||
setTerminalOutput(prev => [
|
||||
...prev,
|
||||
{ type: 'error', content: 'WebSocket connection error' }
|
||||
]);
|
||||
setIsRunning(false);
|
||||
};
|
||||
|
||||
socket.onclose = () => {
|
||||
console.log('WebSocket connection closed');
|
||||
setActiveSocket(null);
|
||||
};
|
||||
|
||||
socketRef.current = socket;
|
||||
return socket;
|
||||
};
|
||||
|
||||
// Handle code execution
|
||||
const runCode = async () => {
|
||||
setIsRunning(true);
|
||||
setTerminalOutput([
|
||||
{ type: 'system', content: `Running ${problems[activeQuestion].id}...` }
|
||||
]);
|
||||
|
||||
try {
|
||||
// Submit code to the backend
|
||||
const response = await fetch('http://localhost:8080/api/submit', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
code: code,
|
||||
language: language.toLowerCase(),
|
||||
input: '', // Add input if needed
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Error: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
setSubmissionId(data.id);
|
||||
|
||||
// Connect to WebSocket for real-time updates
|
||||
connectToWebSocket(data.id);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error submitting code:', error);
|
||||
setTerminalOutput(prev => [
|
||||
...prev,
|
||||
{ type: 'error', content: `Error: ${error.message}` }
|
||||
]);
|
||||
setIsRunning(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Handle code submission
|
||||
const submitCode = async () => {
|
||||
setIsRunning(true);
|
||||
setTerminalOutput([
|
||||
{ type: 'system', content: `Submitting solution for ${problems[activeQuestion].id}...` }
|
||||
]);
|
||||
|
||||
try {
|
||||
// Submit code to the backend
|
||||
const response = await fetch('http://localhost:8080/api/submit', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
code: code,
|
||||
language: language.toLowerCase(),
|
||||
input: '',
|
||||
problemId: problems[activeQuestion].id
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Error: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
setSubmissionId(data.id);
|
||||
|
||||
// Connect to WebSocket for real-time updates
|
||||
connectToWebSocket(data.id);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error submitting solution:', error);
|
||||
setTerminalOutput(prev => [
|
||||
...prev,
|
||||
{ type: 'error', content: `Error: ${error.message}` }
|
||||
]);
|
||||
setIsRunning(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Render the current problem
|
||||
const renderProblem = () => {
|
||||
const problem = problems[activeQuestion];
|
||||
if (!problem) return null;
|
||||
|
||||
return (
|
||||
<div className="problem-container">
|
||||
<h1>{problem.title}</h1>
|
||||
|
||||
<div className="problem-description">
|
||||
<p>{problem.description}</p>
|
||||
{problem.constraints && <p>{problem.constraints}</p>}
|
||||
</div>
|
||||
|
||||
{/* Test cases section removed */}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="code-challenge-container">
|
||||
<header className="code-challenge-header">
|
||||
<h1>OnScreen Test</h1>
|
||||
<button className="sign-in-btn">Sign In</button>
|
||||
</header>
|
||||
|
||||
<div className="code-challenge-problem-nav">
|
||||
<h3 className="problem-number">1. {problems["Q.1"].title}</h3>
|
||||
</div>
|
||||
|
||||
<div className="code-challenge-main">
|
||||
<div className="problem-tabs">
|
||||
<button
|
||||
className={activeQuestion === "Q.1" ? "tab-active" : ""}
|
||||
onClick={() => setActiveQuestion("Q.1")}
|
||||
>
|
||||
Q.1
|
||||
</button>
|
||||
<button
|
||||
className={activeQuestion === "Q.2" ? "tab-active" : ""}
|
||||
onClick={() => setActiveQuestion("Q.2")}
|
||||
>
|
||||
Q.2
|
||||
</button>
|
||||
<button
|
||||
className={activeQuestion === "Q.3" ? "tab-active" : ""}
|
||||
onClick={() => setActiveQuestion("Q.3")}
|
||||
>
|
||||
Q.3
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="problem-content">
|
||||
{renderProblem()}
|
||||
</div>
|
||||
|
||||
<div className="editor-section">
|
||||
<div className="editor-header">
|
||||
<div className="editor-controls">
|
||||
<select
|
||||
value={language}
|
||||
onChange={(e) => setLanguage(e.target.value)}
|
||||
className="language-selector"
|
||||
>
|
||||
<option value="JavaScript">JavaScript</option>
|
||||
<option value="Python">Python</option>
|
||||
<option value="Java">Java</option>
|
||||
<option value="C++">C++</option>
|
||||
</select>
|
||||
|
||||
<button
|
||||
className={`auto-btn ${autoSelected ? 'auto-selected' : ''}`}
|
||||
onClick={() => setAutoSelected(!autoSelected)}
|
||||
>
|
||||
Auto
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="editor-actions">
|
||||
<button
|
||||
className="run-btn"
|
||||
onClick={runCode}
|
||||
disabled={isRunning}
|
||||
>
|
||||
<Play size={16} /> Run
|
||||
</button>
|
||||
|
||||
<button
|
||||
className="submit-btn"
|
||||
onClick={submitCode}
|
||||
disabled={isRunning}
|
||||
>
|
||||
<Send size={16} /> Submit
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="editor-container">
|
||||
<Editor
|
||||
height="100%"
|
||||
defaultLanguage="javascript"
|
||||
language={language.toLowerCase()}
|
||||
value={code}
|
||||
onChange={(value) => setCode(value)}
|
||||
theme="vs-dark"
|
||||
options={{
|
||||
fontSize: 14,
|
||||
minimap: { enabled: false },
|
||||
scrollBeyondLastLine: false,
|
||||
automaticLayout: true,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="terminal-section">
|
||||
<div className="terminal-header">
|
||||
<span>Terminal</span>
|
||||
<div className="terminal-controls">
|
||||
<button className="terminal-btn">⊞</button>
|
||||
<button className="terminal-btn">□</button>
|
||||
<button className="terminal-btn">✕</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="terminal-content">
|
||||
{terminalOutput.map((line, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`terminal-line ${line.type}`}
|
||||
>
|
||||
{line.content}
|
||||
</div>
|
||||
))}
|
||||
<div className="terminal-prompt">
|
||||
<span className="prompt-symbol">$</span>
|
||||
<input
|
||||
type="text"
|
||||
className="terminal-input"
|
||||
placeholder="Type here..."
|
||||
disabled={!isRunning}
|
||||
onKeyPress={(e) => {
|
||||
if (e.key === 'Enter' && activeSocket) {
|
||||
const input = e.target.value;
|
||||
// Send input to WebSocket
|
||||
activeSocket.send(JSON.stringify({
|
||||
type: 'input',
|
||||
content: { text: input }
|
||||
}));
|
||||
|
||||
// Add input to terminal output
|
||||
setTerminalOutput(prev => [
|
||||
...prev,
|
||||
{ type: 'system', content: `$ ${input}` }
|
||||
]);
|
||||
|
||||
// Clear the input field
|
||||
e.target.value = '';
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CodeChallenge;
|
||||
@@ -1025,4 +1025,286 @@ body {
|
||||
|
||||
.panel-close-btn:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Code Challenge Component Styles */
|
||||
.code-challenge-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
background-color: var(--vscode-background);
|
||||
color: var(--vscode-foreground);
|
||||
}
|
||||
|
||||
.code-challenge-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 8px 16px;
|
||||
background-color: var(--vscode-background);
|
||||
border-bottom: 1px solid rgba(128, 128, 128, 0.35);
|
||||
}
|
||||
|
||||
.code-challenge-header h1 {
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.sign-in-btn {
|
||||
background-color: transparent;
|
||||
color: var(--vscode-foreground);
|
||||
border: 1px solid rgba(128, 128, 128, 0.5);
|
||||
padding: 4px 12px;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.sign-in-btn:hover {
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.code-challenge-problem-nav {
|
||||
padding: 8px 16px;
|
||||
border-bottom: 1px solid rgba(128, 128, 128, 0.35);
|
||||
}
|
||||
|
||||
.problem-number {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.code-challenge-main {
|
||||
display: flex;
|
||||
height: 60vh;
|
||||
border-bottom: 1px solid rgba(128, 128, 128, 0.35);
|
||||
}
|
||||
|
||||
.problem-tabs {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 130px;
|
||||
border-right: 1px solid rgba(128, 128, 128, 0.35);
|
||||
}
|
||||
|
||||
.problem-tabs button {
|
||||
padding: 16px;
|
||||
background-color: transparent;
|
||||
color: var(--vscode-foreground);
|
||||
border: none;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
border-bottom: 1px solid rgba(128, 128, 128, 0.2);
|
||||
}
|
||||
|
||||
.problem-tabs button.tab-active {
|
||||
background-color: var(--vscode-background);
|
||||
font-weight: 500;
|
||||
border-left: 2px solid #007acc;
|
||||
}
|
||||
|
||||
.problem-tabs button:hover:not(.tab-active) {
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.problem-content {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 16px;
|
||||
border-right: 1px solid rgba(128, 128, 128, 0.35);
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.problem-container h1 {
|
||||
margin-top: 0;
|
||||
font-size: 22px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.problem-description {
|
||||
margin-bottom: 20px;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.problem-examples h2 {
|
||||
font-size: 16px;
|
||||
margin-top: 24px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.example-box {
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
padding: 12px;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 12px;
|
||||
font-family: 'Consolas', 'Monaco', monospace;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.editor-section {
|
||||
width: 50%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.editor-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 8px 12px;
|
||||
background-color: #252526;
|
||||
border-bottom: 1px solid rgba(128, 128, 128, 0.35);
|
||||
}
|
||||
|
||||
.editor-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.language-selector {
|
||||
background-color: #3c3c3c;
|
||||
color: #d4d4d4;
|
||||
border: 1px solid #3c3c3c;
|
||||
border-radius: 4px;
|
||||
padding: 4px 8px;
|
||||
font-size: 13px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.auto-btn {
|
||||
background-color: #3c3c3c;
|
||||
color: #d4d4d4;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 4px 12px;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.auto-selected {
|
||||
background-color: #4d4d4d;
|
||||
}
|
||||
|
||||
.editor-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.run-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
background-color: #3c3c3c;
|
||||
color: #d4d4d4;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 4px 12px;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
background-color: #0e639c;
|
||||
color: #ffffff;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 4px 12px;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.run-btn:hover {
|
||||
background-color: #4d4d4d;
|
||||
}
|
||||
|
||||
.submit-btn:hover {
|
||||
background-color: #1177bb;
|
||||
}
|
||||
|
||||
.editor-container {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.terminal-section {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: var(--vscode-panel-background);
|
||||
}
|
||||
|
||||
.terminal-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 4px 12px;
|
||||
background-color: #252526;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.terminal-controls {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.terminal-btn {
|
||||
background-color: transparent;
|
||||
color: #d4d4d4;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.terminal-content {
|
||||
flex: 1;
|
||||
padding: 8px 12px;
|
||||
font-family: 'Consolas', 'Monaco', monospace;
|
||||
font-size: 14px;
|
||||
overflow-y: auto;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.terminal-line {
|
||||
margin-bottom: 4px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.terminal-line.system {
|
||||
color: #569cd6;
|
||||
}
|
||||
|
||||
.terminal-line.error {
|
||||
color: #f48771;
|
||||
}
|
||||
|
||||
.terminal-prompt {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.prompt-symbol {
|
||||
color: #569cd6;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.terminal-input {
|
||||
background-color: transparent;
|
||||
color: #d4d4d4;
|
||||
border: none;
|
||||
outline: none;
|
||||
flex: 1;
|
||||
font-family: 'Consolas', 'Monaco', monospace;
|
||||
font-size: 14px;
|
||||
}
|
||||
Reference in New Issue
Block a user