added tryling, v0
This commit is contained in:
parent
2b320ab52a
commit
fa3fda144b
@ -105,6 +105,7 @@ fun App() {
|
|||||||
Div({ classes("col-12", if (isDocsRoute) "col-lg-9" else "col-lg-12") }) {
|
Div({ classes("col-12", if (isDocsRoute) "col-lg-9" else "col-lg-12") }) {
|
||||||
when {
|
when {
|
||||||
route.isBlank() -> HomePage()
|
route.isBlank() -> HomePage()
|
||||||
|
route == "tryling" -> TryLyngPage()
|
||||||
!isDocsRoute -> ReferencePage()
|
!isDocsRoute -> ReferencePage()
|
||||||
else -> DocsPage(
|
else -> DocsPage(
|
||||||
route = route,
|
route = route,
|
||||||
|
|||||||
@ -56,6 +56,14 @@ fun HomePage() {
|
|||||||
I({ classes("bi", "bi-journal-text", "me-1") })
|
I({ classes("bi", "bi-journal-text", "me-1") })
|
||||||
Text("Browse reference")
|
Text("Browse reference")
|
||||||
}
|
}
|
||||||
|
A(attrs = {
|
||||||
|
classes("btn", "btn-success", "btn-lg")
|
||||||
|
// Use the hash path requested by the user: "#tryling"
|
||||||
|
attr("href", "#tryling")
|
||||||
|
}) {
|
||||||
|
I({ classes("bi", "bi-code-slash", "me-1") })
|
||||||
|
Text("Try Lyng")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -194,7 +194,12 @@ fun ensureDocsLayoutStyles() {
|
|||||||
|
|
||||||
// DocLink and UnsafeRawHtml moved to Components.kt
|
// DocLink and UnsafeRawHtml moved to Components.kt
|
||||||
|
|
||||||
fun currentRoute(): String = window.location.hash.removePrefix("#/")
|
fun currentRoute(): String {
|
||||||
|
val h = window.location.hash
|
||||||
|
// Support both "#/path" and "#path" formats
|
||||||
|
val noHash = if (h.startsWith("#")) h.substring(1) else h
|
||||||
|
return noHash.removePrefix("/")
|
||||||
|
}
|
||||||
|
|
||||||
fun routeToPath(route: String): String {
|
fun routeToPath(route: String): String {
|
||||||
val noParams = stripQuery(stripFragment(route))
|
val noParams = stripQuery(stripFragment(route))
|
||||||
|
|||||||
181
site/src/jsMain/kotlin/TryLyngPage.kt
Normal file
181
site/src/jsMain/kotlin/TryLyngPage.kt
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
import androidx.compose.runtime.*
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.jetbrains.compose.web.attributes.placeholder
|
||||||
|
import org.jetbrains.compose.web.dom.*
|
||||||
|
import net.sergeych.lyng.Scope
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun TryLyngPage() {
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
var code by remember {
|
||||||
|
mutableStateOf(
|
||||||
|
"""
|
||||||
|
// Welcome to Lyng! Edit and run.
|
||||||
|
// Try changing the data and press Ctrl+Enter or click Run.
|
||||||
|
import lyng.stdlib
|
||||||
|
|
||||||
|
val data = [1, 2, 3, 4, 5]
|
||||||
|
val evens = data.filter { it % 2 == 0 }.map { it * it }
|
||||||
|
evens
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
var running by remember { mutableStateOf(false) }
|
||||||
|
var output by remember { mutableStateOf<String?>(null) }
|
||||||
|
var error by remember { mutableStateOf<String?>(null) }
|
||||||
|
|
||||||
|
fun runCode() {
|
||||||
|
if (running) return
|
||||||
|
running = true
|
||||||
|
output = null
|
||||||
|
error = null
|
||||||
|
scope.launch {
|
||||||
|
try {
|
||||||
|
// Create a fresh module scope each run so imports and vars are clean
|
||||||
|
val s = Scope.new()
|
||||||
|
// Capture printed output from Lyng `print`/`println` into the UI result window
|
||||||
|
val printed = StringBuilder()
|
||||||
|
s.addVoidFn("print") {
|
||||||
|
for ((i, a) in args.withIndex()) {
|
||||||
|
if (i > 0) printed.append(' ')
|
||||||
|
printed.append(a.toString(this).value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.addVoidFn("println") {
|
||||||
|
for ((i, a) in args.withIndex()) {
|
||||||
|
if (i > 0) printed.append(' ')
|
||||||
|
printed.append(a.toString(this).value)
|
||||||
|
}
|
||||||
|
printed.append('\n')
|
||||||
|
}
|
||||||
|
val result = s.eval(code)
|
||||||
|
// Render with inspect for nice, user-facing representation
|
||||||
|
val text = try {
|
||||||
|
result.inspect(s)
|
||||||
|
} catch (_: Throwable) {
|
||||||
|
// Fallback if some object lacks inspect override
|
||||||
|
result.toString()
|
||||||
|
}
|
||||||
|
val combined = buildString {
|
||||||
|
if (printed.isNotEmpty()) append(printed)
|
||||||
|
// Always show the final expression value, like a REPL
|
||||||
|
if (isNotEmpty()) append('\n')
|
||||||
|
append(">>> ")
|
||||||
|
append(text)
|
||||||
|
}
|
||||||
|
output = combined
|
||||||
|
} catch (t: Throwable) {
|
||||||
|
// Show error, but also keep anything that has been printed so far
|
||||||
|
error = t.message ?: t.toString()
|
||||||
|
} finally {
|
||||||
|
running = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resetCode() {
|
||||||
|
code = """
|
||||||
|
// Welcome to Lyng! Edit and run.
|
||||||
|
import lyng.stdlib
|
||||||
|
[1,2,3].map { it * 10 }
|
||||||
|
""".trimIndent()
|
||||||
|
output = null
|
||||||
|
error = null
|
||||||
|
}
|
||||||
|
|
||||||
|
PageTemplate(title = "Try Lyng", showBack = true) {
|
||||||
|
// Intro
|
||||||
|
P({ classes("lead", "text-muted", "mb-3") }) {
|
||||||
|
Text("Type or paste Lyng code and run it right in your browser.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Editor
|
||||||
|
Div({ classes("mb-3") }) {
|
||||||
|
Div({ classes("form-label", "fw-semibold") }) { Text("Code") }
|
||||||
|
TextArea(value = code, attrs = {
|
||||||
|
classes("form-control", "font-monospace")
|
||||||
|
attr("style", "min-height: 220px; tab-size: 2;")
|
||||||
|
placeholder("Write some Lyng code…")
|
||||||
|
onInput { ev -> code = ev.value }
|
||||||
|
onKeyDown { ev ->
|
||||||
|
val ctrlEnter = (ev.ctrlKey || ev.metaKey) && ev.key == "Enter"
|
||||||
|
if (ctrlEnter) {
|
||||||
|
ev.preventDefault()
|
||||||
|
runCode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actions
|
||||||
|
Div({ classes("d-flex", "gap-2", "mb-3") }) {
|
||||||
|
Button(attrs = {
|
||||||
|
classes("btn", "btn-primary")
|
||||||
|
if (running) attr("disabled", "disabled")
|
||||||
|
onClick {
|
||||||
|
it.preventDefault()
|
||||||
|
runCode()
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
I({ classes("bi", "bi-play-fill", "me-1") })
|
||||||
|
Text(if (running) "Running…" else "Run")
|
||||||
|
}
|
||||||
|
|
||||||
|
Button(attrs = {
|
||||||
|
classes("btn", "btn-outline-secondary")
|
||||||
|
if (running) attr("disabled", "disabled")
|
||||||
|
onClick {
|
||||||
|
it.preventDefault()
|
||||||
|
resetCode()
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
I({ classes("bi", "bi-arrow-counterclockwise", "me-1") })
|
||||||
|
Text("Reset")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Results
|
||||||
|
if (error != null) {
|
||||||
|
Div({ classes("alert", "alert-danger") }) {
|
||||||
|
I({ classes("bi", "bi-exclamation-triangle-fill", "me-2") })
|
||||||
|
Span({ classes("fw-semibold", "me-1") }) { Text("Error:") }
|
||||||
|
Span { Text(" ${'$'}{error}") }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (output != null) {
|
||||||
|
Div({ classes("card", "mb-3") }) {
|
||||||
|
Div({ classes("card-header", "d-flex", "align-items-center", "gap-2") }) {
|
||||||
|
I({ classes("bi", "bi-terminal") })
|
||||||
|
Span({ classes("fw-semibold") }) { Text("Result") }
|
||||||
|
}
|
||||||
|
Div({ classes("card-body", "bg-body-tertiary") }) {
|
||||||
|
Pre({ classes("mb-0") }) { Code { Text(output!!) } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tips
|
||||||
|
P({ classes("text-muted", "small") }) {
|
||||||
|
I({ classes("bi", "bi-info-circle", "me-1") })
|
||||||
|
Text("Tip: press Ctrl+Enter (or ⌘+Enter on Mac) to run.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user