-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #90 from haroldo-ok/editor Implemented a code editor. It can be invoked by calling: ```bash node . edit ``` It will open a backend on port 1235, a frontend in port 1234 and will launch a browser instance. This fixes #88
- Loading branch information
Showing
24 changed files
with
15,232 additions
and
3,657 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -107,3 +107,6 @@ dist | |
/examples/test/src/* | ||
/examples/test/res | ||
/examples/test/src | ||
|
||
/.cache | ||
/.parcel-cache |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"/api": { | ||
"target": "http://localhost:1235/", | ||
"pathRewrite": { | ||
"^/api": "" | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
'use strict'; | ||
|
||
const express = require('express'); | ||
const { fork } = require('child_process'); | ||
|
||
const { listProjectNames, listProjectScenes, openProjectOnExplorer, readProjectScene, writeProjectScene } = require('../../generator/project'); | ||
|
||
const startBackend = (commandLine, port) => { | ||
const errorHandler = (err, req, res, next) => { | ||
const fullUrl = req.protocol + '://' + req.get('host') + req.originalUrl;; | ||
console.error(`Error ${err} at URL ${fullUrl}`, err); | ||
res.status(500).send({ url: fullUrl, error: err }); | ||
} | ||
|
||
const api = express.Router(); | ||
api.get('/projects', async (req, res) => { | ||
try { | ||
res.send(await listProjectNames(commandLine)) | ||
} catch (e) { | ||
errorHandler(e, req, res); | ||
} | ||
}); | ||
|
||
api.get('/projects/:project/scenes', async (req, res) => { | ||
try { | ||
res.send(await listProjectScenes(commandLine, req.params.project)) | ||
} catch (e) { | ||
errorHandler(e, req, res); | ||
} | ||
}); | ||
|
||
api.get('/projects/:project/scenes/:scene', async (req, res) => { | ||
try { | ||
res.type('txt'); | ||
res.send(await readProjectScene(commandLine, req.params.project, req.params.scene)); | ||
} catch (e) { | ||
errorHandler(e, req, res); | ||
} | ||
}); | ||
|
||
api.put('/projects/:project/scenes/:scene', express.text(), async (req, res) => { | ||
try { | ||
await writeProjectScene(commandLine, req.params.project, req.params.scene, req.body) | ||
res.send({ message: 'File saved.' }); | ||
} catch (e) { | ||
errorHandler(e, req, res); | ||
} | ||
}); | ||
|
||
api.post('/projects/:project/run', async (req, res) => { | ||
try { | ||
const child = fork('.', ['transpile', req.params.project, '--', 'compile', 'emulate']); | ||
child.on('exit', () => { | ||
console.log('Execution OK'); | ||
res.send({ message: 'Execution OK' }); | ||
}); | ||
} catch (e) { | ||
errorHandler(e, req, res); | ||
} | ||
}); | ||
|
||
api.post('/projects/:project/explore', async (req, res) => { | ||
try { | ||
openProjectOnExplorer(commandLine, req.params.project); | ||
res.send({ message: 'Execution OK' }); | ||
} catch (e) { | ||
errorHandler(e, req, res); | ||
} | ||
}); | ||
|
||
const app = express(); | ||
|
||
app.use('/v0', api); | ||
app.listen(port); | ||
|
||
console.log(`Backend running on port ${port}`); | ||
}; | ||
|
||
module.exports = { startBackend }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
'use strict'; | ||
|
||
const PARCEL_PORT = 1234; | ||
const API_PORT = 1235; | ||
|
||
const showEditor = async (commandLine, executeCommands) => { | ||
const { Parcel } = require('@parcel/core'); | ||
const { openInBrowser } = require('@parcel/utils'); | ||
const { normalize } = require('path'); | ||
|
||
const { startBackend } = require('./back/backend'); | ||
|
||
startBackend(commandLine, API_PORT); | ||
|
||
let bundler = new Parcel({ | ||
entries: normalize(__dirname + '/front/index.html'), | ||
defaultConfig: '@parcel/config-default', | ||
shouldAutoInstall: true, | ||
serveOptions: { | ||
port: PARCEL_PORT | ||
}, | ||
hmrOptions: { | ||
port: PARCEL_PORT | ||
} | ||
}); | ||
|
||
await bundler.watch(); | ||
console.log(`Frontend running on port ${PARCEL_PORT}`); | ||
|
||
if (commandLine.openBrowser) { | ||
openInBrowser(`http://localhost:${PARCEL_PORT}/`); | ||
} | ||
}; | ||
|
||
module.exports = { showEditor }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
.editContainer { | ||
} | ||
|
||
.editContainer > nav { | ||
float: left; | ||
} | ||
|
||
.editContainer > div { | ||
overflow: auto; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import React, { useEffect, useState } from 'react'; | ||
import Editor, { DiffEditor, useMonaco, loader } from "@monaco-editor/react"; | ||
|
||
import '@picocss/pico/css/pico.min.css' | ||
import 'font-awesome/css/font-awesome.min.css' | ||
import './App.css' | ||
|
||
import { prepareSyntax } from './syntax' | ||
import { ProjectList } from './ProjectList' | ||
import { SceneList } from './SceneList' | ||
import { Scene } from './Scene' | ||
import { RunButton } from './RunButton' | ||
import { SaveAllButton } from './SaveAllButton' | ||
import { OpenFileExplorer } from './OpenFileExplorer' | ||
|
||
import { callSaveSceneApi } from './hooks/api' | ||
|
||
export function App() { | ||
prepareSyntax(); | ||
|
||
const [projectName, setProjectName] = useState(""); | ||
const [sceneName, setSceneName] = useState(""); | ||
const [scenes, setScenes] = useState({}); | ||
|
||
// TODO: The code that keeps track of whether the scene's data has been changed is a big mess, right now. | ||
|
||
const handleSceneDataChange = (sceneInfo) => { | ||
const { originalData, data } = sceneInfo; | ||
const lastData = sceneInfo.lastData === undefined ? (scenes[sceneName] || {}).lastData : sceneInfo.lastData; | ||
const comparedData = lastData === undefined ? originalData : lastData; | ||
const isModified = comparedData != data; | ||
|
||
const newScenes = { ...scenes }; | ||
newScenes[sceneName] = { ...sceneInfo, lastData, isModified }; | ||
setScenes(newScenes); | ||
} | ||
|
||
const isModifiedScene = sceneName => (scenes[sceneName] || {}).isModified; | ||
|
||
const saveScene = async sceneName => { | ||
const sceneInfo = scenes[sceneName]; | ||
const { data } = sceneInfo; | ||
await callSaveSceneApi(projectName, sceneName, data); | ||
handleSceneDataChange({ ...sceneInfo, lastData: data }); | ||
}; | ||
|
||
const saveAll = async () => { | ||
const promises = Object.entries(scenes) | ||
.filter(([sceneName, { isModified }]) => isModified) | ||
.map(([sceneName]) => saveScene(sceneName)); | ||
|
||
await Promise.all(promises); | ||
|
||
setScenes(Object.fromEntries(Object.entries(scenes).map(([sceneName, sceneInfo]) => { | ||
const { data } = sceneInfo; | ||
return [sceneName, { ...sceneInfo, lastData: data, isModified: false }] | ||
}))); | ||
}; | ||
|
||
if (!projectName) { | ||
return ( | ||
<main className="container"> | ||
<nav> | ||
<article> | ||
<header>Select a project to edit...</header> | ||
<ProjectList value={projectName} onChange={setProjectName} /> | ||
</article> | ||
</nav> | ||
</main> | ||
); | ||
} | ||
|
||
return ( | ||
<div> | ||
<nav> | ||
<ul> | ||
<li><SaveAllButton projectName={projectName} onClick={saveAll} /></li> | ||
<li><OpenFileExplorer projectName={projectName} /></li> | ||
</ul> | ||
<ul> | ||
<li><strong>choice4genesis editor</strong></li> | ||
</ul> | ||
<ul> | ||
<li><RunButton projectName={projectName} onSaveAll={saveAll} /></li> | ||
</ul> | ||
</nav> | ||
<div className="editContainer"> | ||
<nav> | ||
<article> | ||
<header>Scenes</header> | ||
<SceneList projectName={projectName} value={sceneName} onChange={setSceneName} onSaveScene={saveScene} isModified={isModifiedScene} /> | ||
</article> | ||
</nav> | ||
<div> | ||
<Scene projectName={projectName} sceneName={sceneName} onChange={handleSceneDataChange} /> | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
'use strict'; | ||
|
||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' | ||
import { faFolderOpen } from '@fortawesome/free-solid-svg-icons' | ||
|
||
import { callOpenExplorerApi } from './hooks/api'; | ||
|
||
export function OpenFileExplorer(props) { | ||
const handleButtonClick = async e => { | ||
try { | ||
await callOpenExplorerApi(props.projectName); | ||
} catch (e) { | ||
console.error('Error while opening explorer', e); | ||
} | ||
}; | ||
|
||
return ( | ||
<a href="#" role="button" onClick={handleButtonClick} className="secondary"> | ||
<FontAwesomeIcon icon={faFolderOpen} /> View Files | ||
</a> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { useProjectListApi } from './hooks/api'; | ||
|
||
export function ProjectList(props) { | ||
const { data, error } = useProjectListApi(); | ||
|
||
if (error) { | ||
console.error('Error while listing projects', error); | ||
return <h1>Error while listing projects</h1>; | ||
} | ||
if (!data) return <h1>Loading project list...</h1>; | ||
|
||
const selectedValue = props.value && data.find(projectName => projectName === props.value) ? props.value : ''; | ||
|
||
const handleProjectNameChange = projectName => { | ||
props.onChange && props.onChange(projectName); | ||
}; | ||
|
||
return ( | ||
<aside> | ||
<nav> | ||
<ul> | ||
{data.map(projectName => | ||
<li key={projectName}> | ||
<a href="#" className={projectName === selectedValue ? '' : 'secondary'} onClick={() => handleProjectNameChange(projectName)}> | ||
{projectName} | ||
</a> | ||
</li> | ||
)} | ||
</ul> | ||
</nav> | ||
</aside> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
'use strict'; | ||
|
||
import React, { useState } from 'react'; | ||
|
||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' | ||
import { faCircleNotch, faPlay } from '@fortawesome/free-solid-svg-icons' | ||
|
||
import { callRunApi } from './hooks/api'; | ||
|
||
export function RunButton(props) { | ||
if (!props.projectName) return <a href="#" role="button" disabled={true}><FontAwesomeIcon icon={faPlay} /> Run</a>; | ||
|
||
const [processing, setProcessing] = useState(false); | ||
|
||
const handleButtonClick = async e => { | ||
setProcessing(true); | ||
try { | ||
props.onSaveAll && await props.onSaveAll(); | ||
await callRunApi(props.projectName); | ||
} catch (e) { | ||
console.error('Error while executing', e); | ||
} finally { | ||
setProcessing(false); | ||
} | ||
}; | ||
|
||
return ( | ||
<a href="#" role="button" disabled={processing} onClick={handleButtonClick}> | ||
<FontAwesomeIcon icon={processing ? faCircleNotch : faPlay} className={processing ? 'fa-spin' : ''} /> Run | ||
</a> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
'use strict'; | ||
|
||
import React, { useState } from 'react'; | ||
|
||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' | ||
import { faCircleNotch, faFloppyDisk } from '@fortawesome/free-solid-svg-icons' | ||
|
||
export function SaveAllButton(props) { | ||
const [processing, setProcessing] = useState(false); | ||
|
||
const handleButtonClick = async e => { | ||
setProcessing(true); | ||
try { | ||
await props.onClick(e); | ||
} catch (e) { | ||
console.error('Error while executing', e); | ||
} finally { | ||
setProcessing(false); | ||
} | ||
}; | ||
|
||
return ( | ||
<a href="#" role="button" disabled={processing} onClick={handleButtonClick} className="secondary"> | ||
<FontAwesomeIcon icon={processing ? faCircleNotch : faFloppyDisk} className={processing ? 'fa-spin' : ''} /> Save all | ||
</a> | ||
); | ||
} |
Oops, something went wrong.