4 Commits

7 changed files with 284 additions and 12 deletions

47
.github/workflows/main.yml vendored Normal file
View File

@@ -0,0 +1,47 @@
name: Release Monaco
on:
push:
tags:
- 'v*' # Triggers on version tags like v1.0.0, v1.1.0-beta, etc.
jobs:
build-and-release:
runs-on: ubuntu-latest
strategy:
matrix:
go-version: [1.22.3] # Adjust to your Go version
os: [linux, darwin, windows]
arch: [amd64, arm64]
exclude:
- os: darwin
arch: arm64
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go-version }}
- name: Build binary
env:
GOOS: ${{ matrix.os }}
GOARCH: ${{ matrix.arch }}
run: |
cd backend
go mod tidy
GOOS=$GOOS GOARCH=$GOARCH go build -o monaco-${{ matrix.os }}-${{ matrix.arch }} .
- name: Create Release
uses: softprops/action-gh-release@v2
if: startsWith(github.ref, 'refs/tags/')
with:
files: |
backend/monaco-${{ matrix.os }}-${{ matrix.arch }}
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1 +1 @@
VITE_API_URL="https://monacoapi.thearnab.tech"
VITE_API_URL="http://localhost:8080"

View File

@@ -60,6 +60,11 @@ const EditorArea = ({
const [terminalOutput, setTerminalOutput] = useState([]);
const [activeRunningFile, setActiveRunningFile] = useState(null);
// Add a new state for user input
const [userInput, setUserInput] = useState("");
// Add a new state for waiting for input
const [waitingForInput, setWaitingForInput] = useState(false);
// Focus the input when new file modal opens
useEffect(() => {
if (isNewFileModalOpen && newFileInputRef.current) {
@@ -502,7 +507,7 @@ Happy coding!`;
width: `calc(100% - ${sidebarVisible ? sidebarWidth : 0}px)`
};
// Update the run code function to work with backend
// Modify the handleRunCode function to prompt for input first
const handleRunCode = async () => {
if (!activeFile) return;
@@ -512,8 +517,8 @@ Happy coding!`;
setPanelVisible(true);
}
// Set running state
setIsRunning(true);
// Set state to waiting for input
setWaitingForInput(true);
setActiveRunningFile(activeFile.id);
// Clear previous output and add new command
@@ -521,23 +526,40 @@ Happy coding!`;
const language = getLanguageFromExtension(fileExtension);
const newOutput = [
{ type: 'command', content: `$ run ${activeFile.id}` }
{ type: 'command', content: `$ run ${activeFile.id}` },
{ type: 'output', content: 'Waiting for input (press Enter if no input is needed)...' }
];
setTerminalOutput(newOutput);
};
// Add a new function to handle input submission
const handleInputSubmit = async () => {
if (!activeFile || !waitingForInput) return;
// Set running state
setIsRunning(true);
setWaitingForInput(false);
// Add message that we're running with the input
setTerminalOutput(prev => [
...prev,
{ type: 'output', content: userInput ? `Using input: "${userInput}"` : 'Running without input...' }
]);
// Use API URL from environment variable
const apiUrl = import.meta.env.VITE_API_URL || 'http://localhost:8080';
try {
// Step 1: Submit code to backend
// Now make the API call with the input that was entered
const submitResponse = await fetch(`${apiUrl}/submit`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
language: language,
code: activeFile.content
language: getLanguageFromExtension(activeFile.id.split('.').pop().toLowerCase()),
code: activeFile.content,
input: userInput
}),
});
@@ -640,6 +662,37 @@ Happy coding!`;
}
};
// Add this function above the return statement
const handleDownloadFile = () => {
if (!activeFile) return;
// Create a blob with the file content
const blob = new Blob([activeFile.content], { type: 'text/plain' });
// Create a URL for the blob
const url = URL.createObjectURL(blob);
// Create a temporary anchor element
const a = document.createElement('a');
a.href = url;
// Get just the filename without path
const fileName = activeFile.id.includes('/') ?
activeFile.id.split('/').pop() :
activeFile.id;
// Set the download attribute with the filename
a.download = fileName;
// Append to the document, click it, and then remove it
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
// Release the object URL
URL.revokeObjectURL(url);
};
return (
<div className="editor-container">
{sidebarVisible && (
@@ -785,13 +838,18 @@ Happy coding!`;
height={panelHeight}
terminalOutput={terminalOutput}
isRunning={isRunning}
waitingForInput={waitingForInput}
activeRunningFile={activeRunningFile}
initialTab="terminal"
onClose={togglePanel} // Use the new function
onClose={togglePanel}
userInput={userInput}
onUserInputChange={setUserInput}
onInputSubmit={handleInputSubmit}
/>
</>
)}
{/* Modify the editor-actions div to include the download button */}
<div className="editor-actions">
<button
className="editor-action-button"
@@ -801,6 +859,30 @@ Happy coding!`;
>
<Save size={16} />
</button>
{/* Add download button */}
<button
className="editor-action-button"
onClick={handleDownloadFile}
disabled={!activeTab}
title="Download file"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
<polyline points="7 10 12 15 17 10"></polyline>
<line x1="12" y1="15" x2="12" y2="3"></line>
</svg>
</button>
</div>
{isNewFileModalOpen && (
@@ -860,6 +942,61 @@ Happy coding!`;
<Edit size={14} className="mr-1" />
Rename
</div>
{/* Add download option - only show for files */}
{contextMenuTarget?.type === 'file' && (
<div className="context-menu-item" onClick={() => {
// Find the file in the files array
const file = files.find(f => f.id === contextMenuTarget.path);
if (file) {
// Create a blob with the file content
const blob = new Blob([file.content], { type: 'text/plain' });
// Create a URL for the blob
const url = URL.createObjectURL(blob);
// Create a temporary anchor element
const a = document.createElement('a');
a.href = url;
// Get just the filename without path
const fileName = file.id.includes('/') ?
file.id.split('/').pop() :
file.id;
// Set the download attribute with the filename
a.download = fileName;
// Append to the document, click it, and then remove it
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
// Release the object URL
URL.revokeObjectURL(url);
}
closeContextMenu();
}}>
<svg
xmlns="http://www.w3.org/2000/svg"
width="14"
height="14"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="mr-1"
>
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
<polyline points="7 10 12 15 17 10"></polyline>
<line x1="12" y1="15" x2="12" y2="3"></line>
</svg>
Download
</div>
)}
<div className="context-menu-item delete" onClick={() => {
deleteItem(contextMenuTarget.path, contextMenuTarget.type);
closeContextMenu();

View File

@@ -6,9 +6,13 @@ const Panel = ({
height,
terminalOutput = [],
isRunning = false,
waitingForInput = false,
activeRunningFile = null,
initialTab = "terminal",
onClose
onClose,
userInput = "",
onUserInputChange,
onInputSubmit
}) => {
const [activeTab, setActiveTab] = useState(initialTab);
@@ -28,9 +32,22 @@ const Panel = ({
{line.type === 'command' ? <span className="terminal-prompt">$</span> : ''} {line.content}
</div>
))}
{isRunning && (
{waitingForInput && (
<div className="terminal-line">
<span className="terminal-cursor"></span>
<span className="terminal-prompt">Input:</span>
<input
type="text"
className="terminal-input"
value={userInput}
onChange={(e) => onUserInputChange && onUserInputChange(e.target.value)}
placeholder="Enter input for your program here..."
onKeyDown={(e) => {
if (e.key === 'Enter' && onInputSubmit) {
onInputSubmit();
}
}}
autoFocus
/>
</div>
)}
</>

View File

@@ -924,6 +924,21 @@ body {
color: #569cd6;
}
.terminal-input {
background-color: transparent;
border: none;
color: inherit;
font-family: monospace;
font-size: inherit;
margin-left: 8px;
outline: none;
width: calc(100% - 60px);
}
.terminal-input:focus {
outline: none;
}
.terminal-line.info {
color: #75beff;
}

56
backend/data.py Normal file
View File

@@ -0,0 +1,56 @@
import os
import aiohttp
import asyncio
from datetime import datetime, timedelta
# Base URL template
BASE_URL = "https://bhuvan-app3.nrsc.gov.in/isroeodatadownloadutility/tiledownloadnew_cfr_new.php?f=nices_ssm2_{}_{}.zip&se=NICES&u=arnabafk"
# Directory to save files
SAVE_DIR = "data"
os.makedirs(SAVE_DIR, exist_ok=True)
async def download_file(session, file_url, file_path):
"""Download a file asynchronously."""
print(f"Downloading {file_url}...")
try:
async with session.get(file_url) as response:
if response.status == 200:
with open(file_path, 'wb') as file:
while chunk := await response.content.read(1024):
file.write(chunk)
print(f"Downloaded: {file_path}")
else:
print(f"Failed to download: {file_path}, Status Code: {response.status}")
except Exception as e:
print(f"Error downloading {file_url}: {e}")
async def fetch_data_for_year(session, year):
"""Fetch and download data for a given year."""
year_dir = os.path.join(SAVE_DIR, str(year))
os.makedirs(year_dir, exist_ok=True)
start_date = datetime(year, 1, 1)
end_date = datetime(year, 12, 31)
delta = timedelta(days=2)
tasks = []
date = start_date
while date <= end_date:
date_str = date.strftime("%Y%m%d")
file_url = BASE_URL.format(date_str, "NICES")
file_name = f"nices_ssm2_{date_str}.zip"
file_path = os.path.join(year_dir, file_name)
tasks.append(download_file(session, file_url, file_path))
date += delta
await asyncio.gather(*tasks)
async def main():
"""Main function to download data for multiple years."""
async with aiohttp.ClientSession() as session:
await asyncio.gather(*(fetch_data_for_year(session, year) for year in range(2002, 2025)))
if __name__ == "__main__":
asyncio.run(main())

BIN
backend/tmp/main.exe Normal file

Binary file not shown.