feat: Add live-coding support via nREPL and SSE reload
Add the infrastructure for live-coding Sigil web applications. An AI agent or editor connects to a running app's nREPL, redefines handlers or views, and triggers a browser refresh — no page reload needed.
New modules: - (sigil web dev): Dev broadcast channel, dev-reload!, dev-morph!, dev-error!, dev-sse-handler for SSE-based browser refresh - (sigil nrepl client): Reusable nREPL client for connecting to running Sigil apps over TCP - (sigil mcp dev-tools): MCP tools (dev-connect, dev-eval, dev-reload, dev-status, dev-disconnect) that proxy to nREPL
Changes: - (sigil web ui): Add sse-reload event for page re-fetch + Idiomorph morph - sigil-web-ui.js: Handle sigil:reload event with fetch + morph cycle - Vinyl Vault demo: Wire up nREPL server (port 7888) and dev SSE endpoint
package.sgl | 1 +
src/sigil/web/demo.sgl | 17 +++++++++++++++--
src/sigil/web/demo/views.sgl | 3 ++-
3 files changed, 18 insertions(+), 3 deletions(-)package.sglmodified
(from-workspace name: "sigil-stdlib") (from-workspace name: "sigil-http") (from-workspace name: "sigil-web") (from-workspace name: "sigil-nrepl") (from-workspace name: "sigil-sqlite") (from-workspace name: "sigil-sxml") (from-workspace name: "sigil-css")))src/sigil/web/demo.sglmodified
(sigil http request) (sigil web) (sigil web ui) (sigil web dev) (sigil nrepl) (sigil sqlite) (sigil web demo db) (sigil web demo views)) (route method: POST pattern: "/records" handler: create-record-handler) (route method: POST pattern: "/records/:id" handler: update-record-handler) (route method: DELETE pattern: "/records/:id" handler: delete-record-handler) (route method: GET pattern: "/js/sigil-web-ui.js" handler: (sigil-web-ui-handler)))) (route method: GET pattern: "/js/sigil-web-ui.js" handler: (sigil-web-ui-handler)) (route method: GET pattern: "/__dev/events" handler: (dev-sse-handler)))) (with-db _ db) (with-logging) (with-content-type default: "text/html; charset=utf-8") ;;; Start the demo server. (define (start-demo) (let ((db (sqlite-open "vinyl-vault.db"))) (let ((db (sqlite-open "vinyl-vault.db")) (dev (make-dev-broadcast))) (init-db db) (display "Vinyl Vault running at http://localhost:8080\n") (display "nREPL at port 7888\n") (with-async ;; nREPL goroutine (let ((nrepl (nrepl-start 7888))) (when nrepl (go (let loop () (nrepl-process-pending nrepl) (sleep 0.05) (loop))))) ;; HTTP server (http-serve (make-app db) port: 8080)))) ))src/sigil/web/demo/views.sglmodified
(li (a (@ (href "/records/new")) "Add Record"))))) (div (@ (id "sg-flash-container"))) (main (@ (class "container")) ,body-content)))) ,body-content) (div (@ (data-sg-sse "/__dev/events") (style "display:none")))))) ;; ============================================================ ;; Pages