Skip to content

Commit 60d451b

Browse files
authored
Add a Run button and simplify playground logic (#968)
* use dispatch for execution * add a 'Run' button * add keyboard shortcut (Meta + E) to execute code * simplify states * move to the Output panel when clicking run or auto-run * prevent default on shortcut key down
1 parent 556d744 commit 60d451b

10 files changed

+333
-277
lines changed

src/ConsolePanel.res

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,34 +3,35 @@ type logLevel = [
33
| #warn
44
| #error
55
]
6+
type log = {level: logLevel, content: array<string>}
67

78
@react.component
8-
let make = (~logs, ~setLogs) => {
9+
let make = (~logs, ~appendLog) => {
910
React.useEffect(() => {
1011
let cb = e => {
1112
let data = e["data"]
1213
switch data["type"] {
1314
| #...logLevel as logLevel =>
1415
let args: array<string> = data["args"]
15-
setLogs(previous => previous->Array.concat([(logLevel, args)]))
16+
appendLog(logLevel, args)
1617
| _ => ()
1718
}
1819
}
1920
Webapi.Window.addEventListener("message", cb)
2021
Some(() => Webapi.Window.removeEventListener("message", cb))
21-
}, [])
22+
}, [appendLog])
2223

2324
<div className="px-2 py-6 relative flex flex-col flex-1 overflow-y-hidden">
2425
<h2 className="font-bold text-gray-5/50 absolute right-2 top-2"> {React.string("Console")} </h2>
2526
{switch logs {
2627
| [] =>
2728
React.string(
28-
"Add some 'Console.log' to your code and enable 'Auto-run' to see your logs here.",
29+
"Add some 'Console.log' to your code and click 'Run' or enable 'Auto-run' to see your logs here.",
2930
)
3031
| logs =>
3132
let content =
3233
logs
33-
->Array.mapWithIndex(((logLevel, log), i) => {
34+
->Array.mapWithIndex(({level: logLevel, content: log}, i) => {
3435
let log = Array.join(log, " ")
3536
<pre
3637
key={RescriptCore.Int.toString(i)}

src/OutputPanel.res

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,21 @@
11
@react.component
2-
let make = (~runOutput, ~compilerState, ~logs, ~setLogs) => {
2+
let make = (~compilerState, ~appendLog) => {
3+
let validReact = switch compilerState {
4+
| CompilerManagerHook.Executing({state: {validReactCode: true}})
5+
| Compiling({state: {validReactCode: true}})
6+
| Ready({validReactCode: true}) => true
7+
| _ => false
8+
}
9+
10+
let logs = switch compilerState {
11+
| CompilerManagerHook.Executing({state: {logs}})
12+
| Compiling({state: {logs}})
13+
| Ready({logs}) => logs
14+
| _ => []
15+
}
316
<div className="h-full flex flex-col overflow-y-hidden">
4-
<RenderPanel runOutput compilerState clearLogs={() => setLogs(_ => [])} />
17+
<RenderPanel validReact />
518
<hr className="border-gray-60" />
6-
<ConsolePanel logs setLogs />
19+
<ConsolePanel logs appendLog />
720
</div>
821
}

src/Playground.res

Lines changed: 80 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -1038,20 +1038,6 @@ module Settings = {
10381038
}
10391039

10401040
module ControlPanel = {
1041-
let codeFromResult = (result: FinalResult.t): string => {
1042-
open Api
1043-
switch result {
1044-
| FinalResult.Comp(comp) =>
1045-
switch comp {
1046-
| CompilationResult.Success({js_code}) => js_code
1047-
| UnexpectedError(_)
1048-
| Unknown(_, _)
1049-
| Fail(_) => "/* No JS code generated */"
1050-
}
1051-
| Nothing
1052-
| Conv(_) => "/* No JS code generated */"
1053-
}
1054-
}
10551041
module Button = {
10561042
@react.component
10571043
let make = (~children, ~onClick=?) =>
@@ -1130,23 +1116,73 @@ module ControlPanel = {
11301116
~state: CompilerManagerHook.state,
11311117
~dispatch: CompilerManagerHook.action => unit,
11321118
~editorCode: React.ref<string>,
1133-
~runOutput,
1134-
~toggleRunOutput,
1119+
~setCurrentTab: (tab => tab) => unit,
11351120
) => {
11361121
let children = switch state {
11371122
| Init => React.string("Initializing...")
11381123
| SwitchingCompiler(_ready, _version) => React.string("Switching Compiler...")
1139-
| Compiling(_, _)
1124+
| Compiling(_)
1125+
| Executing(_)
11401126
| Ready(_) =>
11411127
let onFormatClick = evt => {
11421128
ReactEvent.Mouse.preventDefault(evt)
11431129
dispatch(Format(editorCode.current))
11441130
}
11451131

1132+
let autoRun = switch state {
1133+
| CompilerManagerHook.Executing({state: {autoRun: true}})
1134+
| Compiling({state: {autoRun: true}})
1135+
| Ready({autoRun: true}) => true
1136+
| _ => false
1137+
}
1138+
1139+
let runCode = () => {
1140+
setCurrentTab(_ => Output)
1141+
dispatch(RunCode)
1142+
}
1143+
1144+
let onKeyDown = event => {
1145+
switch (
1146+
event->ReactEvent.Keyboard.metaKey || event->ReactEvent.Keyboard.ctrlKey,
1147+
event->ReactEvent.Keyboard.key,
1148+
) {
1149+
| (true, "e") =>
1150+
event->ReactEvent.Keyboard.preventDefault
1151+
runCode()
1152+
| _ => ()
1153+
}
1154+
}
1155+
1156+
React.useEffect(() => {
1157+
Webapi.Window.addEventListener("keydown", onKeyDown)
1158+
Some(() => Webapi.Window.removeEventListener("keydown", onKeyDown))
1159+
}, [])
1160+
1161+
let runButtonText = {
1162+
let userAgent = Webapi.Window.Navigator.userAgent
1163+
let run = "Run"
1164+
if userAgent->String.includes("iPhone") || userAgent->String.includes("Android") {
1165+
run
1166+
} else if userAgent->String.includes("Mac") {
1167+
`${run} (⌘ + E)`
1168+
} else {
1169+
`${run} (Ctrl + E)`
1170+
}
1171+
}
1172+
11461173
<div className="flex flex-row gap-x-2">
1147-
<ToggleButton checked=runOutput onChange={_ => toggleRunOutput()}>
1174+
<ToggleButton
1175+
checked=autoRun
1176+
onChange={_ => {
1177+
switch state {
1178+
| Ready({autoRun: false}) => setCurrentTab(_ => Output)
1179+
| _ => ()
1180+
}
1181+
dispatch(ToggleAutoRun)
1182+
}}>
11481183
{React.string("Auto-run")}
11491184
</ToggleButton>
1185+
<Button onClick={_ => runCode()}> {React.string(runButtonText)} </Button>
11501186
<Button onClick=onFormatClick> {React.string("Format")} </Button>
11511187
<ShareButton actionIndicatorKey />
11521188
</div>
@@ -1176,78 +1212,27 @@ module OutputPanel = {
11761212
~compilerState: CompilerManagerHook.state,
11771213
~editorCode: React.ref<string>,
11781214
~currentTab: tab,
1179-
~runOutput,
11801215
) => {
1181-
/*
1182-
We need the prevState to understand different
1183-
state transitions, and to be able to keep displaying
1184-
old results until those transitions are done.
1185-
1186-
Goal was to reduce the UI flickering during different
1187-
state transitions
1188-
*/
1189-
let prevState = React.useRef(None)
1190-
1191-
let cmCode = switch prevState.current {
1192-
| Some(prev) =>
1193-
switch (prev, compilerState) {
1194-
| (_, Ready({result: Nothing})) => None
1195-
| (Ready(prevReady), Ready(ready)) =>
1196-
switch (prevReady.result, ready.result) {
1197-
| (_, Comp(Success(_))) => ControlPanel.codeFromResult(ready.result)->Some
1198-
| _ => None
1199-
}
1200-
| (_, Ready({result: Comp(Success(_)) as result})) =>
1201-
ControlPanel.codeFromResult(result)->Some
1202-
| (Ready({result: Comp(Success(_)) as result}), Compiling(_, _)) =>
1203-
ControlPanel.codeFromResult(result)->Some
1204-
| _ => None
1205-
}
1206-
| None =>
1207-
switch compilerState {
1208-
| Ready(ready) => ControlPanel.codeFromResult(ready.result)->Some
1209-
| _ => None
1210-
}
1211-
}
1212-
1213-
prevState.current = Some(compilerState)
1214-
1215-
let resultPane = switch compilerState {
1216-
| Compiling(ready, _)
1217-
| Ready(ready) =>
1218-
switch ready.result {
1219-
| Comp(Success(_))
1220-
| Conv(Success(_)) => React.null
1221-
| _ =>
1222-
<ResultPane
1223-
targetLang=ready.targetLang
1224-
compilerVersion=ready.selected.compilerVersion
1225-
result=ready.result
1226-
/>
1227-
}
1228-
1229-
| _ => React.null
1230-
}
1231-
1232-
let (code, showCm) = switch cmCode {
1233-
| None => ("", false)
1234-
| Some(code) => (code, true)
1235-
}
1236-
1237-
let codeElement =
1238-
<pre className={"whitespace-pre-wrap p-4 " ++ (showCm ? "block" : "hidden")}>
1239-
{HighlightJs.renderHLJS(~code, ~darkmode=true, ~lang="js", ())}
1240-
</pre>
1241-
12421216
let output =
12431217
<div className="text-gray-20">
1244-
resultPane
1245-
codeElement
1218+
{switch compilerState {
1219+
| Compiling({previousJsCode: Some(jsCode)})
1220+
| Executing({jsCode})
1221+
| Ready({result: Comp(Success({jsCode}))}) =>
1222+
<pre className={"whitespace-pre-wrap p-4 "}>
1223+
{HighlightJs.renderHLJS(~code=jsCode, ~darkmode=true, ~lang="js", ())}
1224+
</pre>
1225+
| Ready({result: Conv(Success(_))}) => React.null
1226+
| Ready({result, targetLang, selected}) =>
1227+
<ResultPane targetLang compilerVersion=selected.compilerVersion result />
1228+
| _ => React.null
1229+
}}
12461230
</div>
12471231

12481232
let errorPane = switch compilerState {
1249-
| Compiling(ready, _)
1233+
| Compiling({state: ready})
12501234
| Ready(ready)
1235+
| Executing({state: ready})
12511236
| SwitchingCompiler(ready, _) =>
12521237
<ResultPane
12531238
targetLang=ready.targetLang
@@ -1260,7 +1245,8 @@ module OutputPanel = {
12601245

12611246
let settingsPane = switch compilerState {
12621247
| Ready(ready)
1263-
| Compiling(ready, _)
1248+
| Compiling({state: ready})
1249+
| Executing({state: ready})
12641250
| SwitchingCompiler(ready, _) =>
12651251
let config = ready.selected.config
12661252
let setConfig = config => compilerDispatch(UpdateConfig(config))
@@ -1273,7 +1259,9 @@ module OutputPanel = {
12731259
let prevSelected = React.useRef(0)
12741260

12751261
let selected = switch compilerState {
1276-
| Compiling(_, _) => prevSelected.current
1262+
| Executing(_)
1263+
| Compiling(_) =>
1264+
prevSelected.current
12771265
| Ready(ready) =>
12781266
switch ready.result {
12791267
| Comp(Success(_))
@@ -1285,10 +1273,10 @@ module OutputPanel = {
12851273

12861274
prevSelected.current = selected
12871275

1288-
let (logs, setLogs) = React.useState(_ => [])
1276+
let appendLog = (level, content) => compilerDispatch(AppendLog({level, content}))
12891277

12901278
let tabs = [
1291-
(Output, <OutputPanel runOutput compilerState logs setLogs />),
1279+
(Output, <OutputPanel compilerState appendLog />),
12921280
(JavaScript, output),
12931281
(Problems, errorPane),
12941282
(Settings, settingsPane),
@@ -1483,7 +1471,7 @@ let make = (~versions: array<string>) => {
14831471
}
14841472

14851473
None
1486-
}, [compilerState])
1474+
}, (compilerState, compilerDispatch))
14871475

14881476
let (layout, setLayout) = React.useState(_ =>
14891477
Webapi.Window.innerWidth < breakingPoint ? Column : Row
@@ -1632,8 +1620,8 @@ let make = (~versions: array<string>) => {
16321620
}
16331621

16341622
let cmHoverHints = switch compilerState {
1635-
| Ready({result: FinalResult.Comp(Success({type_hints}))}) =>
1636-
Array.map(type_hints, hint => {
1623+
| Ready({result: FinalResult.Comp(Success({typeHints}))}) =>
1624+
Array.map(typeHints, hint => {
16371625
switch hint {
16381626
| TypeDeclaration({start, end, hint})
16391627
| Binding({start, end, hint})
@@ -1693,17 +1681,13 @@ let make = (~versions: array<string>) => {
16931681
</button>
16941682
})
16951683

1696-
let (runOutput, setRunOutput) = React.useState(() => false)
1697-
let toggleRunOutput = () => setRunOutput(prev => !prev)
1698-
16991684
<main className={"flex flex-col bg-gray-100 overflow-hidden"}>
17001685
<ControlPanel
17011686
actionIndicatorKey={Int.toString(actionCount)}
17021687
state=compilerState
17031688
dispatch=compilerDispatch
17041689
editorCode
1705-
runOutput
1706-
toggleRunOutput
1690+
setCurrentTab
17071691
/>
17081692
<div
17091693
className={`flex ${layout == Column ? "flex-col" : "flex-row"}`}
@@ -1758,7 +1742,7 @@ let make = (~versions: array<string>) => {
17581742
{React.array(headers)}
17591743
</div>
17601744
<div ref={ReactDOM.Ref.domRef(subPanelRef)} className="overflow-auto">
1761-
<OutputPanel currentTab compilerDispatch compilerState editorCode runOutput />
1745+
<OutputPanel currentTab compilerDispatch compilerState editorCode />
17621746
</div>
17631747
</div>
17641748
</div>

0 commit comments

Comments
 (0)