lyng/lyngweb/README.md

119 lines
4.4 KiB
Markdown

### Lyng Web utilities (`:lyngweb`)
Reusable JS/Compose for Web utilities and UI pieces for Lyng-powered sites. The module is self-sufficient: adding it as a dependency is enough — no external CSS classes are required for its editor overlay to render correctly.
#### What’s inside
- `EditorWithOverlay` — a pure code editor Composable with a syntax-highlight overlay. It keeps a native `<textarea>` for input/caret while rendering highlighted HTML on top, staying in perfect glyph alignment. No built-in buttons or actions.
- HTML utilities for Markdown pipelines:
- `ensureBootstrapCodeBlocks(html: String): String` — adds `class="code"` to `<pre>` blocks (for Bootstrap-like styling).
- `highlightLyngHtml(html: String): String` — transforms Lyng code blocks inside HTML into highlighted spans using `hl-*` classes.
- `htmlEscape(s: String): String` — HTML-escape utility used by the highlighter.
- Backward-compatible `net.sergeych.site.SiteHighlight.renderHtml(text)` that renders a highlighted string with the same `hl-*` classes used by the site.
All essential styles for the editor are injected inline; there is no dependency on external CSS class names (e.g., `editor-overlay`). You can still override visuals with your own CSS if desired.
---
#### Quick start
1) Add the dependency in your JS `build.gradle.kts`:
```kotlin
dependencies {
implementation(project(":lyngweb"))
}
```
2) Use the editor in a Compose HTML page:
```kotlin
@Composable
fun TryLyngSnippet() {
var code by remember { mutableStateOf("""
// Type Lyng code here
import lyng.stdlib
[1,2,3].map { it * 10 }
""".trimIndent()) }
fun runCode() { /* evaluate code in your Scope */ }
Div({ classes("mb-3") }) {
Div({ classes("form-label") }) { Text("Code") }
EditorWithOverlay(
code = code,
setCode = { code = it },
// Optionally handle keyboard shortcuts (e.g., Ctrl/Cmd+Enter to run):
onKeyDown = { ev ->
val ctrlOrMeta = ev.ctrlKey || ev.metaKey
if (ctrlOrMeta && ev.key.lowercase() == "enter") {
ev.preventDefault()
runCode()
}
}
)
}
// Your own action buttons
// ...your own action buttons...
}
```
The editor provides:
- Tab insertion with configurable `tabSize` (default 4)
- Smart newline indentation (copies the leading spaces of the current line)
- Scroll sync and 1:1 glyph alignment between overlay and textarea
- Inline styles for overlay/textarea; no external CSS required
---
#### Highlight Lyng code inside Markdown HTML
If you render Markdown to HTML first (e.g., with `marked`), you can post-process it with `lyngweb` to highlight Lyng code blocks:
```kotlin
fun renderMarkdownLyng(mdHtml: String): String {
// 1) ensure <pre> blocks have class="code"
val withPre = ensureBootstrapCodeBlocks(mdHtml)
// 2) highlight <code class="language-lyng"> blocks
return highlightLyngHtml(withPre)
}
```
Lyng tokens are wrapped into spans with classes like `hl-kw`, `hl-id`, `hl-num`, `hl-cmt`, etc. You can style them as you wish, for example:
```css
.hl-kw { color: #6f42c1; font-weight: 600; }
.hl-id { color: #1f2328; }
.hl-num { color: #0a3069; }
.hl-str { color: #015b2f; }
.hl-cmt { color: #6a737d; font-style: italic; }
```
---
#### API Summary
- `@Composable fun EditorWithOverlay(code: String, setCode: (String) -> Unit, tabSize: Int = 4, onKeyDown: ((SyntheticKeyboardEvent) -> Unit)? = null)`
- Pure editor, no actions. Wire your own buttons and shortcuts.
- Self-contained styling, adjustable with your own CSS if desired.
- `fun ensureBootstrapCodeBlocks(html: String): String`
- Adds `class="code"` to `<pre>` tags if not present.
- `fun highlightLyngHtml(html: String): String`
- Highlights Lyng `<code class="language-lyng">...</code>` blocks inside the provided HTML.
- `fun htmlEscape(s: String): String`
- Escapes special HTML characters.
- `object net.sergeych.site.SiteHighlight`
- `fun renderHtml(text: String): String` — renders highlighted spans with `hl-*` classes; kept for compatibility with existing site code/tests.
---
#### Notes
- The editor does not ship default color styles for `hl-*` classes. Provide your own CSS to match your theme.
- If you want a minimal look without Bootstrap, the editor still works out of the box due to inline styles.