feat: add fullscreen TUI and project updates

- New TUI screens: Main Menu, Compile, Run, REPL, AI Generator, AI Settings, Help
- AI configuration persisted in ~/.config/cljnim/config.json
- Added illwill dependency for terminal UI
- Updated experiments, examples, docs, and core modules
This commit is contained in:
2026-05-09 01:53:23 +03:00
parent 2bc64eca22
commit ba0b300917
42 changed files with 3042 additions and 153 deletions
+36
View File
@@ -0,0 +1,36 @@
# Clojure/Nim → JavaScript
Compile Clojure code to JavaScript via Nim's JS backend.
## Why?
- **No JVM** — Clojure in the browser without 50MB runtime
- **No JavaScript** — write Clojure, get JS
- **Smaller than ClojureScript** — no Google Closure compiler needed
## Quick Start
```bash
./build.sh
```
Then open `www/index.html` in a browser.
## How it works
1. `cljnim compile-lib` — generates Nim with exported functions
2. `js_wrappers.nim` — thin wrappers that convert CljVal ↔ JS types
3. `nim js` — compiles Nim to JavaScript
4. Browser loads `math.js` and calls `jsSquare(7)`, `jsFactorial(5)`, etc.
## Limitations
- Full `cljnim_runtime.nim` uses C FFI (threads, processes) — not JS-compatible
- For now, only numeric and string functions work
- Persistent data structures need JS-specific runtime
## Future
- Full JS runtime for HAMT vectors/maps
- DOM interop: `(dom/getElementById "app")`
- npm package output
+30
View File
@@ -0,0 +1,30 @@
#!/bin/bash
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CLJNIM="${SCRIPT_DIR}/../../cljnim"
LIB_PATH="${SCRIPT_DIR}/../../lib"
mkdir -p "${SCRIPT_DIR}/build"
echo "=== Step 1: Clojure → Nim ==="
"$CLJNIM" compile-lib \
"${SCRIPT_DIR}/examples/math.clj" \
"${SCRIPT_DIR}/build/math.nim"
echo "=== Step 2: Patch runtime for JS ==="
sed -i 's/import cljnim_runtime/import cljnim_runtime_js/' "${SCRIPT_DIR}/build/math.nim"
echo "=== Step 3: Nim → JavaScript ==="
cd "$SCRIPT_DIR" && nim js -d:release \
--path:"$LIB_PATH" \
--path:"build" \
-o:"build/math.js" \
"js_wrappers.nim"
echo "=== Step 3: Generated files ==="
ls -la "${SCRIPT_DIR}/build/"
echo ""
echo "JS: ${SCRIPT_DIR}/build/math.js"
echo ""
echo "Open ${SCRIPT_DIR}/www/index.html in a browser to test"
File diff suppressed because it is too large Load Diff
+12
View File
@@ -0,0 +1,12 @@
# Generated by Clojure/Nim
import cljnim_runtime_js
proc square*(x: CljVal): CljVal =
cljMul(@[x, x])
proc factorial*(n: CljVal): CljVal =
if cljIsTruthy(cljNumEq(@[n, cljInt(0)])):
result = cljInt(1)
else:
result = cljMul(@[n, factorial(cljSub(@[n, cljInt(1)]))])
proc greet*(name: CljVal): CljVal =
cljStrConcat(@[cljString("Hello, "), name, cljString("!")])
+12
View File
@@ -0,0 +1,12 @@
(ns math)
(defn square [x]
(* x x))
(defn factorial [n]
(if (= n 0)
1
(* n (factorial (- n 1)))))
(defn greet [name]
(str "Hello, " name "!"))
+19
View File
@@ -0,0 +1,19 @@
# JS-friendly wrappers around Clojure-generated code
# These expose plain JS types (number, string) instead of CljVal
import cljnim_runtime_js
# Import the generated Clojure functions
include build/math
proc jsSquare*(x: int): int {.exportc.} =
let r = square(cljInt(x.int64))
return r.intVal.int
proc jsFactorial*(n: int): int {.exportc.} =
let r = factorial(cljInt(n.int64))
return r.intVal.int
proc jsGreet*(name: cstring): cstring {.exportc.} =
let r = greet(cljString($name))
return r.strVal.cstring
+50
View File
@@ -0,0 +1,50 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Clojure/Nim → JS Demo</title>
<style>
body { font-family: monospace; max-width: 800px; margin: 40px auto; padding: 20px; background: #1a1a2e; color: #eee; }
h1 { color: #e94560; }
.result { background: #16213e; padding: 15px; margin: 10px 0; border-radius: 5px; }
.label { color: #0f3460; font-weight: bold; }
button { background: #e94560; color: white; border: none; padding: 10px 20px; cursor: pointer; border-radius: 3px; }
button:hover { background: #ff6b6b; }
input { padding: 8px; font-size: 16px; border-radius: 3px; border: 1px solid #0f3460; background: #16213e; color: white; }
</style>
</head>
<body>
<h1>🔥 Clojure/Nim → JavaScript</h1>
<p>Clojure code compiled to Nim, then to JavaScript. Running in your browser.</p>
<div class="result">
<div class="label">square(7)</div>
<div id="square-result">Click button to calculate</div>
<br>
<button onclick="document.getElementById('square-result').textContent = jsSquare(7)">Calculate</button>
</div>
<div class="result">
<div class="label">factorial(5)</div>
<div id="fact-result">Click button to calculate</div>
<br>
<button onclick="document.getElementById('fact-result').textContent = jsFactorial(5)">Calculate</button>
</div>
<div class="result">
<div class="label">greet("World")</div>
<div id="greet-result">Click button to greet</div>
<br>
<button onclick="document.getElementById('greet-result').textContent = jsGreet('World')">Greet</button>
</div>
<div class="result">
<div class="label">Interactive: factorial of</div>
<input type="number" id="n-input" value="10" min="0" max="20">
<button onclick="document.getElementById('interactive-result').textContent = jsFactorial(parseInt(document.getElementById('n-input').value))">Compute</button>
<div id="interactive-result" style="margin-top:10px;"></div>
</div>
<script src="../build/math.js"></script>
</body>
</html>