better homepage samples
This commit is contained in:
parent
d0230c5b89
commit
338dc00573
@ -1,7 +1,6 @@
|
|||||||
#!/bin/env lyng
|
#!/bin/env lyng
|
||||||
|
|
||||||
import lyng.io.fs
|
import lyng.io.fs
|
||||||
import lyng.stdlib
|
|
||||||
|
|
||||||
val files = Path("../..").list().toList()
|
val files = Path("../..").list().toList()
|
||||||
// most long is longest?
|
// 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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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") }) {
|
Div({ classes("col-12", if (isDocsRoute) "col-lg-9" else "col-lg-12") }) {
|
||||||
when {
|
when {
|
||||||
route.isBlank() -> HomePage()
|
route.isBlank() -> HomePage()
|
||||||
route == "tryling" -> TryLyngPage()
|
route.startsWith("tryling") -> TryLyngPage(route)
|
||||||
route.startsWith("search") -> SearchPage(route)
|
route.startsWith("search") -> SearchPage(route)
|
||||||
!isDocsRoute -> ReferencePage()
|
!isDocsRoute -> ReferencePage()
|
||||||
else -> DocsPage(
|
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.ensureBootstrapCodeBlocks
|
||||||
import net.sergeych.lyngweb.highlightLyngHtml
|
import net.sergeych.lyngweb.highlightLyngHtml
|
||||||
import net.sergeych.lyngweb.htmlEscape
|
import net.sergeych.lyngweb.htmlEscape
|
||||||
@ -23,6 +25,141 @@ import org.jetbrains.compose.web.dom.*
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun HomePage() {
|
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
|
// Hero section
|
||||||
Section({ classes("py-4", "py-lg-5") }) {
|
Section({ classes("py-4", "py-lg-5") }) {
|
||||||
Div({ classes("text-center") }) {
|
Div({ classes("text-center") }) {
|
||||||
@ -44,7 +181,7 @@ fun HomePage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// CTA buttons
|
// 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 = {
|
A(attrs = {
|
||||||
classes("btn", "btn-primary", "btn-lg")
|
classes("btn", "btn-primary", "btn-lg")
|
||||||
attr("href", "#/docs/tutorial.md")
|
attr("href", "#/docs/tutorial.md")
|
||||||
@ -72,48 +209,26 @@ fun HomePage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Code sample
|
// Code sample slideshow
|
||||||
val code = """
|
Div({
|
||||||
// Create, transform, and verify — the Lyng way
|
classes("markdown-body", "mt-0", "slide-container", "position-relative")
|
||||||
import lyng.stdlib
|
onClick {
|
||||||
|
window.location.hash = "#tryling?code=" + encodeURIComponent(samples[currentSlide])
|
||||||
fun findFirstPositive(list) {
|
|
||||||
list.forEach {
|
|
||||||
if (it > 0) return@findFirstPositive it
|
|
||||||
}
|
}
|
||||||
null
|
}) {
|
||||||
}
|
Small({
|
||||||
assertEquals(42, findFirstPositive([-1, 42, -5]))
|
classes("position-absolute", "top-0", "end-0", "m-2", "text-muted", "fw-light", "try-hint")
|
||||||
|
}) {
|
||||||
val data = 1..5 // or [1,2,3,4,5]
|
Text("click to try")
|
||||||
val evens2 = data.filter { it % 2 == 0 }.map { it * it }
|
}
|
||||||
assertEquals([4, 16], evens2)
|
key(currentSlide) {
|
||||||
|
val slideCode = samples[currentSlide]
|
||||||
// Map literal with identifier keys, shorthand, and spread
|
val mapHtml = "<pre><code>" + htmlEscape(slideCode) + "</code></pre>"
|
||||||
val base = { a: 1, b: 2 }
|
Div({ classes("slide-animation") }) {
|
||||||
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") }) {
|
|
||||||
UnsafeRawHtml(highlightLyngHtml(ensureBootstrapCodeBlocks(mapHtml)))
|
UnsafeRawHtml(highlightLyngHtml(ensureBootstrapCodeBlocks(mapHtml)))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Short features list
|
// Short features list
|
||||||
Div({ classes("row", "g-4", "mt-1") }) {
|
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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -18,20 +18,30 @@
|
|||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import net.sergeych.lyng.LyngVersion
|
import net.sergeych.lyng.LyngVersion
|
||||||
import net.sergeych.lyng.Scope
|
import net.sergeych.lyng.Script
|
||||||
import net.sergeych.lyng.ScriptError
|
import net.sergeych.lyng.ScriptError
|
||||||
import net.sergeych.lyngweb.EditorWithOverlay
|
import net.sergeych.lyngweb.EditorWithOverlay
|
||||||
import org.jetbrains.compose.web.dom.*
|
import org.jetbrains.compose.web.dom.*
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TryLyngPage() {
|
fun TryLyngPage(route: String) {
|
||||||
val scope = rememberCoroutineScope()
|
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(
|
mutableStateOf(
|
||||||
"""
|
initialCode ?: """
|
||||||
// Welcome to Lyng! Edit and run.
|
// Welcome to Lyng! Edit and run.
|
||||||
// Try changing the data and press Ctrl+Enter or click 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]
|
val data = 1..5 // or [1, 2, 3, 4, 5]
|
||||||
data.filter { it % 2 == 0 }.map { it * it }
|
data.filter { it % 2 == 0 }.map { it * it }
|
||||||
@ -54,16 +64,16 @@ fun TryLyngPage() {
|
|||||||
val printed = StringBuilder()
|
val printed = StringBuilder()
|
||||||
try {
|
try {
|
||||||
// Create a fresh module scope each run so imports and vars are clean
|
// 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
|
// Capture printed output from Lyng `print`/`println` into the UI result window
|
||||||
s.addVoidFn("print") {
|
s.addVoidFn("print") {
|
||||||
for ((i, a) in args.withIndex()) {
|
for ((i, a) in this.args.withIndex()) {
|
||||||
if (i > 0) printed.append(' ')
|
if (i > 0) printed.append(' ')
|
||||||
printed.append(a.toString(this).value)
|
printed.append(a.toString(this).value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s.addVoidFn("println") {
|
s.addVoidFn("println") {
|
||||||
for ((i, a) in args.withIndex()) {
|
for ((i, a) in this.args.withIndex()) {
|
||||||
if (i > 0) printed.append(' ')
|
if (i > 0) printed.append(' ')
|
||||||
printed.append(a.toString(this).value)
|
printed.append(a.toString(this).value)
|
||||||
}
|
}
|
||||||
@ -118,9 +128,8 @@ fun TryLyngPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun resetCode() {
|
fun resetCode() {
|
||||||
code = """
|
code = initialCode ?: """
|
||||||
// Welcome to Lyng! Edit and run.
|
// Welcome to Lyng! Edit and run.
|
||||||
import lyng.stdlib
|
|
||||||
[1,2,3].map { it * 10 }
|
[1,2,3].map { it * 10 }
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
output = null
|
output = null
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user