Skip to content

Add a Run button and simplify playground logic #968

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Feb 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions src/ConsolePanel.res
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,35 @@ type logLevel = [
| #warn
| #error
]
type log = {level: logLevel, content: array<string>}

@react.component
let make = (~logs, ~setLogs) => {
let make = (~logs, ~appendLog) => {
React.useEffect(() => {
let cb = e => {
let data = e["data"]
switch data["type"] {
| #...logLevel as logLevel =>
let args: array<string> = data["args"]
setLogs(previous => previous->Array.concat([(logLevel, args)]))
appendLog(logLevel, args)
| _ => ()
}
}
Webapi.Window.addEventListener("message", cb)
Some(() => Webapi.Window.removeEventListener("message", cb))
}, [])
}, [appendLog])

<div className="px-2 py-6 relative flex flex-col flex-1 overflow-y-hidden">
<h2 className="font-bold text-gray-5/50 absolute right-2 top-2"> {React.string("Console")} </h2>
{switch logs {
| [] =>
React.string(
"Add some 'Console.log' to your code and enable 'Auto-run' to see your logs here.",
"Add some 'Console.log' to your code and click 'Run' or enable 'Auto-run' to see your logs here.",
)
| logs =>
let content =
logs
->Array.mapWithIndex(((logLevel, log), i) => {
->Array.mapWithIndex(({level: logLevel, content: log}, i) => {
let log = Array.join(log, " ")
<pre
key={RescriptCore.Int.toString(i)}
Expand Down
19 changes: 16 additions & 3 deletions src/OutputPanel.res
Original file line number Diff line number Diff line change
@@ -1,8 +1,21 @@
@react.component
let make = (~runOutput, ~compilerState, ~logs, ~setLogs) => {
let make = (~compilerState, ~appendLog) => {
let validReact = switch compilerState {
| CompilerManagerHook.Executing({state: {validReactCode: true}})
| Compiling({state: {validReactCode: true}})
| Ready({validReactCode: true}) => true
| _ => false
}

let logs = switch compilerState {
| CompilerManagerHook.Executing({state: {logs}})
| Compiling({state: {logs}})
| Ready({logs}) => logs
| _ => []
}
<div className="h-full flex flex-col overflow-y-hidden">
<RenderPanel runOutput compilerState clearLogs={() => setLogs(_ => [])} />
<RenderPanel validReact />
<hr className="border-gray-60" />
<ConsolePanel logs setLogs />
<ConsolePanel logs appendLog />
</div>
}
176 changes: 80 additions & 96 deletions src/Playground.res
Original file line number Diff line number Diff line change
Expand Up @@ -1038,20 +1038,6 @@ module Settings = {
}

module ControlPanel = {
let codeFromResult = (result: FinalResult.t): string => {
open Api
switch result {
| FinalResult.Comp(comp) =>
switch comp {
| CompilationResult.Success({js_code}) => js_code
| UnexpectedError(_)
| Unknown(_, _)
| Fail(_) => "/* No JS code generated */"
}
| Nothing
| Conv(_) => "/* No JS code generated */"
}
}
module Button = {
@react.component
let make = (~children, ~onClick=?) =>
Expand Down Expand Up @@ -1130,23 +1116,73 @@ module ControlPanel = {
~state: CompilerManagerHook.state,
~dispatch: CompilerManagerHook.action => unit,
~editorCode: React.ref<string>,
~runOutput,
~toggleRunOutput,
~setCurrentTab: (tab => tab) => unit,
) => {
let children = switch state {
| Init => React.string("Initializing...")
| SwitchingCompiler(_ready, _version) => React.string("Switching Compiler...")
| Compiling(_, _)
| Compiling(_)
| Executing(_)
| Ready(_) =>
let onFormatClick = evt => {
ReactEvent.Mouse.preventDefault(evt)
dispatch(Format(editorCode.current))
}

let autoRun = switch state {
| CompilerManagerHook.Executing({state: {autoRun: true}})
| Compiling({state: {autoRun: true}})
| Ready({autoRun: true}) => true
| _ => false
}

let runCode = () => {
setCurrentTab(_ => Output)
dispatch(RunCode)
}

let onKeyDown = event => {
switch (
event->ReactEvent.Keyboard.metaKey || event->ReactEvent.Keyboard.ctrlKey,
event->ReactEvent.Keyboard.key,
) {
| (true, "e") =>
event->ReactEvent.Keyboard.preventDefault
runCode()
| _ => ()
}
}

React.useEffect(() => {
Webapi.Window.addEventListener("keydown", onKeyDown)
Some(() => Webapi.Window.removeEventListener("keydown", onKeyDown))
}, [])

let runButtonText = {
let userAgent = Webapi.Window.Navigator.userAgent
let run = "Run"
if userAgent->String.includes("iPhone") || userAgent->String.includes("Android") {
run
} else if userAgent->String.includes("Mac") {
`${run} (⌘ + E)`
} else {
`${run} (Ctrl + E)`
}
}

<div className="flex flex-row gap-x-2">
<ToggleButton checked=runOutput onChange={_ => toggleRunOutput()}>
<ToggleButton
checked=autoRun
onChange={_ => {
switch state {
| Ready({autoRun: false}) => setCurrentTab(_ => Output)
| _ => ()
}
dispatch(ToggleAutoRun)
}}>
{React.string("Auto-run")}
</ToggleButton>
<Button onClick={_ => runCode()}> {React.string(runButtonText)} </Button>
<Button onClick=onFormatClick> {React.string("Format")} </Button>
<ShareButton actionIndicatorKey />
</div>
Expand Down Expand Up @@ -1176,78 +1212,27 @@ module OutputPanel = {
~compilerState: CompilerManagerHook.state,
~editorCode: React.ref<string>,
~currentTab: tab,
~runOutput,
) => {
/*
We need the prevState to understand different
state transitions, and to be able to keep displaying
old results until those transitions are done.

Goal was to reduce the UI flickering during different
state transitions
*/
let prevState = React.useRef(None)

let cmCode = switch prevState.current {
| Some(prev) =>
switch (prev, compilerState) {
| (_, Ready({result: Nothing})) => None
| (Ready(prevReady), Ready(ready)) =>
switch (prevReady.result, ready.result) {
| (_, Comp(Success(_))) => ControlPanel.codeFromResult(ready.result)->Some
| _ => None
}
| (_, Ready({result: Comp(Success(_)) as result})) =>
ControlPanel.codeFromResult(result)->Some
| (Ready({result: Comp(Success(_)) as result}), Compiling(_, _)) =>
ControlPanel.codeFromResult(result)->Some
| _ => None
}
| None =>
switch compilerState {
| Ready(ready) => ControlPanel.codeFromResult(ready.result)->Some
| _ => None
}
}

prevState.current = Some(compilerState)

let resultPane = switch compilerState {
| Compiling(ready, _)
| Ready(ready) =>
switch ready.result {
| Comp(Success(_))
| Conv(Success(_)) => React.null
| _ =>
<ResultPane
targetLang=ready.targetLang
compilerVersion=ready.selected.compilerVersion
result=ready.result
/>
}

| _ => React.null
}

let (code, showCm) = switch cmCode {
| None => ("", false)
| Some(code) => (code, true)
}

let codeElement =
<pre className={"whitespace-pre-wrap p-4 " ++ (showCm ? "block" : "hidden")}>
{HighlightJs.renderHLJS(~code, ~darkmode=true, ~lang="js", ())}
</pre>

let output =
<div className="text-gray-20">
resultPane
codeElement
{switch compilerState {
| Compiling({previousJsCode: Some(jsCode)})
| Executing({jsCode})
| Ready({result: Comp(Success({jsCode}))}) =>
<pre className={"whitespace-pre-wrap p-4 "}>
{HighlightJs.renderHLJS(~code=jsCode, ~darkmode=true, ~lang="js", ())}
</pre>
| Ready({result: Conv(Success(_))}) => React.null
| Ready({result, targetLang, selected}) =>
<ResultPane targetLang compilerVersion=selected.compilerVersion result />
| _ => React.null
}}
</div>

let errorPane = switch compilerState {
| Compiling(ready, _)
| Compiling({state: ready})
| Ready(ready)
| Executing({state: ready})
| SwitchingCompiler(ready, _) =>
<ResultPane
targetLang=ready.targetLang
Expand All @@ -1260,7 +1245,8 @@ module OutputPanel = {

let settingsPane = switch compilerState {
| Ready(ready)
| Compiling(ready, _)
| Compiling({state: ready})
| Executing({state: ready})
| SwitchingCompiler(ready, _) =>
let config = ready.selected.config
let setConfig = config => compilerDispatch(UpdateConfig(config))
Expand All @@ -1273,7 +1259,9 @@ module OutputPanel = {
let prevSelected = React.useRef(0)

let selected = switch compilerState {
| Compiling(_, _) => prevSelected.current
| Executing(_)
| Compiling(_) =>
prevSelected.current
| Ready(ready) =>
switch ready.result {
| Comp(Success(_))
Expand All @@ -1285,10 +1273,10 @@ module OutputPanel = {

prevSelected.current = selected

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

let tabs = [
(Output, <OutputPanel runOutput compilerState logs setLogs />),
(Output, <OutputPanel compilerState appendLog />),
(JavaScript, output),
(Problems, errorPane),
(Settings, settingsPane),
Expand Down Expand Up @@ -1483,7 +1471,7 @@ let make = (~versions: array<string>) => {
}

None
}, [compilerState])
}, (compilerState, compilerDispatch))

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

let cmHoverHints = switch compilerState {
| Ready({result: FinalResult.Comp(Success({type_hints}))}) =>
Array.map(type_hints, hint => {
| Ready({result: FinalResult.Comp(Success({typeHints}))}) =>
Array.map(typeHints, hint => {
switch hint {
| TypeDeclaration({start, end, hint})
| Binding({start, end, hint})
Expand Down Expand Up @@ -1693,17 +1681,13 @@ let make = (~versions: array<string>) => {
</button>
})

let (runOutput, setRunOutput) = React.useState(() => false)
let toggleRunOutput = () => setRunOutput(prev => !prev)

<main className={"flex flex-col bg-gray-100 overflow-hidden"}>
<ControlPanel
actionIndicatorKey={Int.toString(actionCount)}
state=compilerState
dispatch=compilerDispatch
editorCode
runOutput
toggleRunOutput
setCurrentTab
/>
<div
className={`flex ${layout == Column ? "flex-col" : "flex-row"}`}
Expand Down Expand Up @@ -1758,7 +1742,7 @@ let make = (~versions: array<string>) => {
{React.array(headers)}
</div>
<div ref={ReactDOM.Ref.domRef(subPanelRef)} className="overflow-auto">
<OutputPanel currentTab compilerDispatch compilerState editorCode runOutput />
<OutputPanel currentTab compilerDispatch compilerState editorCode />
</div>
</div>
</div>
Expand Down
Loading