Files
cl-tty/demo.lisp

133 lines
6.7 KiB
Common Lisp

;;; demo.lisp — cl-tty interactive demo
;;; Run: sbcl --script demo.lisp
(load "~/quicklisp/setup.lisp")
(ql:register-local-projects)
(ql:quickload :cl-tty :silent t)
;;; ─── Low-level input ───────────────────────────────────────────────────────
(defun read-raw (&optional timeout)
(let ((fn (symbol-function (find-symbol "READ-RAW-BYTE" :cl-tty.input))))
(funcall fn :timeout (or timeout 10))))
(defun read-key ()
(let ((b (read-raw)))
(unless b (return-from read-key nil))
(case b
(#x1b
(let ((b2 (read-raw 1)))
(unless b2 (return-from read-key :escape))
(if (= b2 #x5b)
(let ((b3 (read-raw 1)))
(case b3
(#x41 :up) (#x42 :down)
(#x43 :right) (#x44 :left)
(#x48 :home) (#x46 :end)
(t :unknown)))
:unknown)))
(#x03 :ctrl-c)
(#x0d :enter)
(#x09 :tab)
(#x7f :backspace)
(t (code-char b)))))
;;; ─── Tab content renderers ─────────────────────────────────────────────────
(defun render-home (be)
(cl-tty.backend:draw-border be 6 7 68 10 :style :single :title " Welcome ")
(cl-tty.backend:draw-text be 8 9 "cl-tty — Pure CL terminal UI framework"
:bright-white :default :bold t)
(cl-tty.backend:draw-text be 8 11 " - 11 versions, 12 components"
:white :default)
(cl-tty.backend:draw-text be 8 12 " - No ncurses, no FFI, no external deps"
:white :default)
(cl-tty.backend:draw-text be 8 13 " - 280+ tests, 100% passing"
:green :default)
(cl-tty.backend:draw-text be 8 15 "Arrows: switch tabs Enter/q: quit"
:bright-cyan :default :bold t))
(defun render-components (be)
(cl-tty.backend:draw-border be 6 7 68 12 :style :single :title " Components ")
(loop for i from 0 below 6
for pair = (nth i '(("Box" "Bordered containers, title, bg")
("Text" "Styled text, word-wrap, spans")
("ScrollBox" "Scrollable viewport, scrollbars")
("TabBar" "Tab navigation you are using")
("Select" "Dropdown with fuzzy filter")
("Dialog" "Modal overlays + Toast notifs")))
do (cl-tty.backend:draw-text be 8 (+ 9 i) (first pair)
:bright-yellow :default :bold t)
(cl-tty.backend:draw-text be 24 (+ 9 i) (second pair)
:white :default)))
(defun render-stats (be)
(cl-tty.backend:draw-border be 6 7 68 10 :style :single :title " Stats ")
(cl-tty.backend:draw-text be 8 9 "Metric" :bright-white :default :bold t)
(cl-tty.backend:draw-text be 40 9 "Value" :bright-white :default :bold t)
(loop for i from 0 below 8
for pair = (nth i '(("Versions" "11") ("Components" "12")
("Tests" "280+") ("Lines" "~3060")
("Dependencies" "0") ("FFI" "0")
("ncurses" "no") ("License" "GPL-3.0")))
do (cl-tty.backend:draw-text be 8 (+ 11 i) (first pair) :white :default)
(cl-tty.backend:draw-text be 40 (+ 11 i) (second pair)
:bright-green :default :bold t)))
;;; ─── Tab bar ───────────────────────────────────────────────────────────────
(defun render-tabs (be tabs active)
(let ((x 8))
(cl-tty.backend:draw-rect be 6 4 68 1 :bg :default)
(loop for label in tabs for i from 0
do (let* ((text (format nil " ~a " label)) (len (length text)))
(if (= i active)
(progn (cl-tty.backend:draw-rect be x 4 len 1 :bg :bright-blue)
(cl-tty.backend:draw-text be x 4 text
:bright-white :bright-blue :bold t))
(cl-tty.backend:draw-text be x 4 text :bright-white :default))
(incf x (+ len 2))))))
;;; ─── Main loop ─────────────────────────────────────────────────────────────
(defun run-demo ()
(let* ((raw (find-symbol "SET-RAW-MODE" :cl-tty.input))
(restore (find-symbol "RESTORE-TERMINAL-STATE" :cl-tty.input))
(saved (funcall raw)))
(unwind-protect
(let* ((backend (cl-tty.backend:detect-backend))
(tabs '(" Home " " Components " " Stats "))
(active 0) (running t))
(cl-tty.backend:initialize-backend backend)
(cl-tty.backend:cursor-hide backend)
(loop while running
do (cl-tty.backend:backend-clear backend)
(cl-tty.backend:draw-border backend 2 1 76 3
:style :double :title " cl-tty ")
(cl-tty.backend:draw-text backend 4 2
"Interactive demo arrows: tabs q: quit" :bright-white :default)
(render-tabs backend tabs active)
(case active
(0 (render-home backend))
(1 (render-components backend))
(2 (render-stats backend)))
(cl-tty.backend:draw-rect backend 2 23 76 1 :bg :blue)
(cl-tty.backend:draw-text backend 2 23
(format nil " Tab ~d/3: ~a "
(1+ active) (string-trim " " (nth active tabs)))
:bright-white :blue :bold t)
(case (read-key)
((:ctrl-c :enter #\q #\Q) (setf running nil))
((:right :tab) (setf active (mod (1+ active) (length tabs))))
(:left (setf active (mod (1- active) (length tabs))))))
(cl-tty.backend:cursor-show backend)
(cl-tty.backend:backend-clear backend)
(cl-tty.backend:shutdown-backend backend))
(when saved (funcall restore saved)))))
;;; ─── Entry ──────────────────────────────────────────────────────────────────
(if (probe-file "/dev/tty")
(run-demo)
(format t "No TTY detected. Run in a terminal for the interactive demo.~%"))