better homepage samples
This commit is contained in:
parent
d0230c5b89
commit
338dc00573
@ -1,7 +1,6 @@
|
||||
#!/bin/env lyng
|
||||
|
||||
import lyng.io.fs
|
||||
import lyng.stdlib
|
||||
|
||||
val files = Path("../..").list().toList()
|
||||
// most long is longest?
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
|
||||
* Copyright 2026 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.
|
||||
@ -118,7 +118,7 @@ fun App() {
|
||||
Div({ classes("col-12", if (isDocsRoute) "col-lg-9" else "col-lg-12") }) {
|
||||
when {
|
||||
route.isBlank() -> HomePage()
|
||||
route == "tryling" -> TryLyngPage()
|
||||
route.startsWith("tryling") -> TryLyngPage(route)
|
||||
route.startsWith("search") -> SearchPage(route)
|
||||
!isDocsRoute -> ReferencePage()
|
||||
else -> DocsPage(
|
||||
|
||||
@ -15,7 +15,9 @@
|
||||
*
|
||||
*/
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.*
|
||||
import kotlinx.browser.window
|
||||
import kotlinx.coroutines.delay
|
||||
import net.sergeych.lyngweb.ensureBootstrapCodeBlocks
|
||||
import net.sergeych.lyngweb.highlightLyngHtml
|
||||
import net.sergeych.lyngweb.htmlEscape
|
||||
@ -23,6 +25,141 @@ import org.jetbrains.compose.web.dom.*
|
||||
|
||||
@Composable
|
||||
fun HomePage() {
|
||||
val samples = remember {
|
||||
listOf(
|
||||
"""
|
||||
// Everything is an expression
|
||||
val x = 10
|
||||
val status = if (x > 0) "Positive" else "Zero or Negative"
|
||||
|
||||
// Even loops return values!
|
||||
val result = for (i in 1..5) {
|
||||
if (i == 3) break "Found 3!"
|
||||
} else "Not found"
|
||||
|
||||
println("Result: " + result)
|
||||
""".trimIndent(),
|
||||
"""
|
||||
// Functional power with ranges and collections
|
||||
val squares = (1..10)
|
||||
.filter { it % 2 == 0 }
|
||||
.map { it * it }
|
||||
|
||||
println("Even squares: " + squares)
|
||||
// Output: [4, 16, 36, 64, 100]
|
||||
""".trimIndent(),
|
||||
"""
|
||||
// Flexible map literals and shorthands
|
||||
val id = 101
|
||||
val name = "Lyng"
|
||||
val base = { id:, name: } // Shorthand for id: id, name: name
|
||||
|
||||
val full = { ...base, version: "1.0", status: "active" }
|
||||
println(full)
|
||||
""".trimIndent(),
|
||||
"""
|
||||
// Modern null safety
|
||||
var config = null
|
||||
config ?= { timeout: 30 } // Assign only if null
|
||||
|
||||
val timeout = config?.timeout ?? 60
|
||||
println("Timeout is: " + timeout)
|
||||
""".trimIndent(),
|
||||
"""
|
||||
// Destructuring with splat operator
|
||||
val [first, middle..., last] = [1, 2, 3, 4, 5, 6]
|
||||
|
||||
println("First: " + first)
|
||||
println("Middle: " + middle)
|
||||
println("Last: " + last)
|
||||
""".trimIndent(),
|
||||
"""
|
||||
// Diamond-safe Multiple Inheritance (C3 MRO)
|
||||
interface Logger {
|
||||
fun log(m) = println("[LOG] " + m)
|
||||
}
|
||||
interface Auth {
|
||||
fun login(u) = println("Login: " + u)
|
||||
}
|
||||
|
||||
class App() : Logger, Auth {
|
||||
fun run() {
|
||||
log("Starting...")
|
||||
login("admin")
|
||||
}
|
||||
}
|
||||
App().run()
|
||||
""".trimIndent(),
|
||||
"""
|
||||
// Extension functions and properties
|
||||
fun String.shout() = this.toUpperCase() + "!!!"
|
||||
|
||||
println("hello".shout())
|
||||
|
||||
val List.second get = this[1]
|
||||
println([10, 20, 30].second)
|
||||
""".trimIndent(),
|
||||
"""
|
||||
// Non-local returns from closures
|
||||
fun findFirst(list, predicate) {
|
||||
list.forEach {
|
||||
if (predicate(it)) return@findFirst it
|
||||
}
|
||||
null
|
||||
}
|
||||
|
||||
val found = findFirst([1, 5, 8, 12]) { it > 10 }
|
||||
println("Found: " + found)
|
||||
""".trimIndent(),
|
||||
"""
|
||||
// Easy operator overloading
|
||||
class Vector(val x, val y) {
|
||||
override fun plus(other) = Vector(x + other.x, y + other.y)
|
||||
override fun toString() = "Vector(%g, %g)"(x, y)
|
||||
}
|
||||
|
||||
val v1 = Vector(1, 2)
|
||||
val v2 = Vector(3, 4)
|
||||
println(v1 + v2)
|
||||
""".trimIndent(),
|
||||
"""
|
||||
// Property delegation to Map
|
||||
class User() {
|
||||
var name by Map()
|
||||
}
|
||||
|
||||
val u = User()
|
||||
u.name = "Sergey"
|
||||
println("User name: " + u.name)
|
||||
""".trimIndent(),
|
||||
"""
|
||||
// Implicit coroutines: parallelism without ceremony
|
||||
import lyng.time
|
||||
|
||||
val d1 = launch {
|
||||
delay(100.milliseconds)
|
||||
"Task A finished"
|
||||
}
|
||||
val d2 = launch {
|
||||
delay(50.milliseconds)
|
||||
"Task B finished"
|
||||
}
|
||||
|
||||
println(d1.await())
|
||||
println(d2.await())
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
var currentSlide by remember { mutableStateOf((samples.indices).random()) }
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
ensureSlideshowStyles()
|
||||
while (true) {
|
||||
delay(7000)
|
||||
currentSlide = (currentSlide + 1) % samples.size
|
||||
}
|
||||
}
|
||||
// Hero section
|
||||
Section({ classes("py-4", "py-lg-5") }) {
|
||||
Div({ classes("text-center") }) {
|
||||
@ -44,7 +181,7 @@ fun HomePage() {
|
||||
}
|
||||
}
|
||||
// CTA buttons
|
||||
Div({ classes("d-flex", "justify-content-center", "gap-2", "mb-4") }) {
|
||||
Div({ classes("d-flex", "justify-content-center", "gap-2", "mb-2") }) {
|
||||
A(attrs = {
|
||||
classes("btn", "btn-primary", "btn-lg")
|
||||
attr("href", "#/docs/tutorial.md")
|
||||
@ -72,48 +209,26 @@ fun HomePage() {
|
||||
}
|
||||
}
|
||||
|
||||
// Code sample
|
||||
val code = """
|
||||
// Create, transform, and verify — the Lyng way
|
||||
import lyng.stdlib
|
||||
|
||||
fun findFirstPositive(list) {
|
||||
list.forEach {
|
||||
if (it > 0) return@findFirstPositive it
|
||||
// Code sample slideshow
|
||||
Div({
|
||||
classes("markdown-body", "mt-0", "slide-container", "position-relative")
|
||||
onClick {
|
||||
window.location.hash = "#tryling?code=" + encodeURIComponent(samples[currentSlide])
|
||||
}
|
||||
null
|
||||
}
|
||||
assertEquals(42, findFirstPositive([-1, 42, -5]))
|
||||
|
||||
val data = 1..5 // or [1,2,3,4,5]
|
||||
val evens2 = data.filter { it % 2 == 0 }.map { it * it }
|
||||
assertEquals([4, 16], evens2)
|
||||
|
||||
// Map literal with identifier keys, shorthand, and spread
|
||||
val base = { a: 1, b: 2 }
|
||||
val patch = { b: 3, c: }
|
||||
val m = { "a": 0, ...base, ...patch, d: 4 }
|
||||
assertEquals(1, m["a"]) // base overwrites 0
|
||||
|
||||
// Object expressions: anonymous classes on the fly
|
||||
val worker = object : Runnable {
|
||||
override fun run() = println("Working...")
|
||||
}
|
||||
worker.run()
|
||||
|
||||
// User-defined exceptions: real classes with custom fields
|
||||
class MyError(val code, m) : Exception(m)
|
||||
try {
|
||||
throw MyError(500, "Something failed")
|
||||
} catch (e: MyError) {
|
||||
println("Error " + e.code + ": " + e.message)
|
||||
}
|
||||
>>> void
|
||||
""".trimIndent()
|
||||
val mapHtml = "<pre><code>" + htmlEscape(code) + "</code></pre>"
|
||||
Div({ classes("markdown-body", "mt-3") }) {
|
||||
}) {
|
||||
Small({
|
||||
classes("position-absolute", "top-0", "end-0", "m-2", "text-muted", "fw-light", "try-hint")
|
||||
}) {
|
||||
Text("click to try")
|
||||
}
|
||||
key(currentSlide) {
|
||||
val slideCode = samples[currentSlide]
|
||||
val mapHtml = "<pre><code>" + htmlEscape(slideCode) + "</code></pre>"
|
||||
Div({ classes("slide-animation") }) {
|
||||
UnsafeRawHtml(highlightLyngHtml(ensureBootstrapCodeBlocks(mapHtml)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Short features list
|
||||
Div({ classes("row", "g-4", "mt-1") }) {
|
||||
@ -147,3 +262,36 @@ try {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun ensureSlideshowStyles() {
|
||||
if (window.document.getElementById("slideshow-styles") != null) return
|
||||
val style = window.document.createElement("style") as org.w3c.dom.HTMLStyleElement
|
||||
style.id = "slideshow-styles"
|
||||
style.textContent = """
|
||||
@keyframes slideIn {
|
||||
from { opacity: 0; transform: translateX(30px); }
|
||||
to { opacity: 1; transform: translateX(0); }
|
||||
}
|
||||
.slide-animation {
|
||||
animation: slideIn 0.4s ease-out;
|
||||
}
|
||||
.slide-container {
|
||||
min-height: 320px;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s;
|
||||
background-color: var(--bs-body-bg);
|
||||
}
|
||||
.slide-container:hover {
|
||||
transform: scale(1.005);
|
||||
}
|
||||
.try-hint {
|
||||
opacity: 0.5;
|
||||
transition: opacity 0.2s;
|
||||
pointer-events: none;
|
||||
}
|
||||
.slide-container:hover .try-hint {
|
||||
opacity: 1;
|
||||
}
|
||||
""".trimIndent()
|
||||
window.document.head!!.appendChild(style)
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
|
||||
* Copyright 2026 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.
|
||||
@ -18,20 +18,30 @@
|
||||
import androidx.compose.runtime.*
|
||||
import kotlinx.coroutines.launch
|
||||
import net.sergeych.lyng.LyngVersion
|
||||
import net.sergeych.lyng.Scope
|
||||
import net.sergeych.lyng.Script
|
||||
import net.sergeych.lyng.ScriptError
|
||||
import net.sergeych.lyngweb.EditorWithOverlay
|
||||
import org.jetbrains.compose.web.dom.*
|
||||
|
||||
@Composable
|
||||
fun TryLyngPage() {
|
||||
fun TryLyngPage(route: String) {
|
||||
val scope = rememberCoroutineScope()
|
||||
var code by remember {
|
||||
val initialCode = remember(route) {
|
||||
val params = route.substringAfter('?', "")
|
||||
val codeParam = params.split('&').find { it.startsWith("code=") }?.substringAfter('=')
|
||||
if (codeParam != null) {
|
||||
try {
|
||||
decodeURIComponent(codeParam)
|
||||
} catch (_: Throwable) {
|
||||
null
|
||||
}
|
||||
} else null
|
||||
}
|
||||
var code by remember(initialCode) {
|
||||
mutableStateOf(
|
||||
"""
|
||||
initialCode ?: """
|
||||
// Welcome to Lyng! Edit and run.
|
||||
// Try changing the data and press Ctrl+Enter or click Run.
|
||||
import lyng.stdlib
|
||||
|
||||
val data = 1..5 // or [1, 2, 3, 4, 5]
|
||||
data.filter { it % 2 == 0 }.map { it * it }
|
||||
@ -54,16 +64,16 @@ fun TryLyngPage() {
|
||||
val printed = StringBuilder()
|
||||
try {
|
||||
// Create a fresh module scope each run so imports and vars are clean
|
||||
val s = Scope.new()
|
||||
val s = Script.newScope()
|
||||
// Capture printed output from Lyng `print`/`println` into the UI result window
|
||||
s.addVoidFn("print") {
|
||||
for ((i, a) in args.withIndex()) {
|
||||
for ((i, a) in this.args.withIndex()) {
|
||||
if (i > 0) printed.append(' ')
|
||||
printed.append(a.toString(this).value)
|
||||
}
|
||||
}
|
||||
s.addVoidFn("println") {
|
||||
for ((i, a) in args.withIndex()) {
|
||||
for ((i, a) in this.args.withIndex()) {
|
||||
if (i > 0) printed.append(' ')
|
||||
printed.append(a.toString(this).value)
|
||||
}
|
||||
@ -118,9 +128,8 @@ fun TryLyngPage() {
|
||||
}
|
||||
|
||||
fun resetCode() {
|
||||
code = """
|
||||
code = initialCode ?: """
|
||||
// Welcome to Lyng! Edit and run.
|
||||
import lyng.stdlib
|
||||
[1,2,3].map { it * 10 }
|
||||
""".trimIndent()
|
||||
output = null
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user