Add --verbose flag for TUI event logging to ~/jcode.log

ober

0ebb7a856596924e1a263c0cd9f762c9aa70b660

diff --git a/src/jcode/ui/cli.ss b/src/jcode/ui/cli.ss
index fab39ac..40e0f09 100644
--- a/src/jcode/ui/cli.ss
+++ b/src/jcode/ui/cli.ss
@@ -68,6 +68,7 @@
       ((equal? (car args) "-d")        (loop (cdr args) (cons '(--debug . #t) opts)))
       ((equal? (car args) "--tui")     (loop (cdr args) (cons '(--tui . #t) opts)))
       ((equal? (car args) "--no-tui")  (loop (cdr args) (cons '(--no-tui . #t) opts)))
+      ((equal? (car args) "--verbose") (loop (cdr args) (cons '(--verbose . #t) opts)))
       ((and (equal? (car args) "--model") (pair? (cdr args)))
        (loop (cddr args) (cons (cons '--model (cadr args)) opts)))
       ((and (equal? (car args) "-m") (pair? (cdr args)))
@@ -104,6 +105,7 @@ OPTIONS:
     -p, --provider   Provider to use (default: anthropic)
     --tui            Launch terminal UI mode
     --no-tui         Force line-mode REPL (default)
+    --verbose        Log TUI events to ~/jcode.log
 
 COMMANDS:
     session list     List all sessions
diff --git a/src/jcode/ui/tui.ss b/src/jcode/ui/tui.ss
index c760949..0a5075e 100644
--- a/src/jcode/ui/tui.ss
+++ b/src/jcode/ui/tui.ss
@@ -32,6 +32,35 @@
 (def logger (make-logger "tui"))
 (def *version* "0.1.0")
 
+;; ---- TUI verbose logging ----
+;; When --verbose is passed, logs to ~/jcode.log for debugging input/events.
+
+(def *tui-log-port* (make-parameter #f))
+
+(def (tui-log fmt . args)
+  (let ((p (*tui-log-port*)))
+    (when p
+      (let ((msg (apply format fmt args)))
+        (display msg p)
+        (newline p)
+        (flush-output-port p)))))
+
+(def (open-tui-log!)
+  (let ((p (open-file-output-port
+             (string-append (getenv "HOME") "/jcode.log")
+             (file-options no-fail)
+             (buffer-mode line)
+             (make-transcoder (utf-8-codec)))))
+    (*tui-log-port* p)
+    (tui-log "---- jcode TUI log started ----")))
+
+(def (close-tui-log!)
+  (let ((p (*tui-log-port*)))
+    (when p
+      (tui-log "---- jcode TUI log ended ----")
+      (close-port p)
+      (*tui-log-port* #f))))
+
 ;; ---- Spinner animation ----
 
 (def *spinner-frames* '#("⠋" "⠙" "⠹" "⠸" "⠼" "⠴" "⠦" "⠧" "⠇" "⠏"))
@@ -107,14 +136,20 @@
   (session-init-db)
   (init-tools-for-tui)
   (apply-tui-overrides! args)
+  (when (member "--verbose" args) (open-tui-log!))
+  (tui-log "tui-main: starting, args=~a" args)
   (with-tui
-    (tb-set-input-mode! (bitwise-ior TB_INPUT_ALT TB_INPUT_MOUSE))
-    (tb-set-output-mode! TB_OUTPUT_TRUECOLOR)
+    (tui-log "tui-main: tb-init done")
+    (let ((in-mode (bitwise-ior TB_INPUT_ALT TB_INPUT_MOUSE)))
+      (tui-log "tui-main: setting input-mode=~a output-mode=~a" in-mode TB_OUTPUT_TRUECOLOR)
+      (tb-set-input-mode! in-mode)
+      (tb-set-output-mode! TB_OUTPUT_TRUECOLOR))
     (set-theme! theme-dark)
     (let* ((w (tb-width))
            (h (tb-height))
            (state (make-fresh-state w h))
            (session (session-create "New session")))
+      (tui-log "tui-main: terminal ~ax~a, session=~a" w h (session-id session))
       (app-state-session-id-set! state (session-id session))
       ;; Welcome message
       (app-state-messages-set! state
@@ -122,7 +157,9 @@
       (reflow-all! state)
       (draw-all! state)
       (tb-present!)
-      (event-loop state))))
+      (tui-log "tui-main: entering event loop")
+      (event-loop state)
+      (close-tui-log!))))
 
 (def (init-tools-for-tui)
   ;; Import and init tools — same as cli.ss
@@ -154,6 +191,8 @@
       ((or (equal? (car args) "--debug") (equal? (car args) "-d"))
        (current-log-level 'debug)
        (loop (cdr args)))
+      ((equal? (car args) "--verbose")
+       (loop (cdr args)))  ;; handled in tui-main before this
       (#t (loop (cdr args))))))
 
 ;; ---- Event loop ----
@@ -170,12 +209,22 @@
     ;; Poll for terminal events (50ms timeout)
     (let ((ev (tb-peek-event 50)))
       (when ev
+        (tui-log "event: type=~a key=~a ch=~a(~a) mod=~a w=~a h=~a"
+                 (tui-event-type ev) (tui-event-key ev)
+                 (tui-event-ch ev)
+                 (if (> (tui-event-ch ev) 31)
+                   (string (integer->char (tui-event-ch ev)))
+                   "")
+                 (tui-event-mod ev)
+                 (tui-event-w ev) (tui-event-h ev))
         (cond
           ((tui-event-resize? ev)
+           (tui-log "  -> resize ~ax~a" (tui-event-w ev) (tui-event-h ev))
            (handle-resize! state ev))
           ((tui-event-key? ev)
            (handle-key! state ev))
           ((tui-event-mouse? ev)
+           (tui-log "  -> mouse key=~a x=~a y=~a" (tui-event-key ev) (tui-event-x ev) (tui-event-y ev))
            (handle-mouse! state ev)))))
 
     ;; Redraw if dirty
@@ -201,43 +250,59 @@
 
 (def (handle-key! state ev)
   (let ((key (tui-event-key ev))
+        (ch  (tui-event-ch ev))
         (mod (tui-event-mod ev)))
+    (tui-log "  handle-key: key=~a ch=~a(~a) mod=~a busy?=~a"
+             key ch
+             (if (> ch 31) (string (integer->char ch)) "")
+             mod (app-state-agent-busy? state))
     (cond
       ;; Global: Ctrl-B toggle sidebar
       ((= key TB_KEY_CTRL_B)
+       (tui-log "  -> toggle-sidebar")
        (app-state-sidebar-visible?-set! state (not (app-state-sidebar-visible? state)))
        (reflow-all! state)
        (app-state-dirty?-set! state #t))
 
       ;; Global: Ctrl-L redraw
       ((= key TB_KEY_CTRL_L)
+       (tui-log "  -> redraw")
        (tb-clear!)
        (app-state-dirty?-set! state #t))
 
       ;; Global: Ctrl-T cycle theme
       ((= key TB_KEY_CTRL_T)
+       (tui-log "  -> cycle-theme")
        (cycle-theme!)
        (app-state-dirty?-set! state #t))
 
       ;; Global: Page-up/down scroll
       ((= key TB_KEY_PGUP)
+       (tui-log "  -> page-up")
        (app-state-scroll-offset-set! state
          (+ (app-state-scroll-offset state) (quotient (msg-area-height state) 2)))
        (app-state-dirty?-set! state #t))
       ((= key TB_KEY_PGDN)
+       (tui-log "  -> page-down")
        (app-state-scroll-offset-set! state
          (max 0 (- (app-state-scroll-offset state) (quotient (msg-area-height state) 2))))
        (app-state-dirty?-set! state #t))
 
-      ;; Ctrl-C during agent: cancel (TODO: actually cancel the agent)
+      ;; Ctrl-C during agent: cancel
       ((and (= key TB_KEY_CTRL_C) (app-state-agent-busy? state))
+       (tui-log "  -> cancel-agent")
        (app-state-agent-busy?-set! state #f)
        (app-state-dirty?-set! state #t))
 
       ;; Input handling
       (#t
-       (unless (app-state-agent-busy? state)
+       (if (app-state-agent-busy? state)
+         (tui-log "  -> ignored (agent busy)")
          (let ((action (input-handle-key! (app-state-input state) ev)))
+           (tui-log "  -> input action=~a text=~s cursor=~a"
+                    action
+                    (input-state-text (app-state-input state))
+                    (input-state-cursor-pos (app-state-input state)))
            (case action
              ((submit) (handle-submit! state))
              ((quit)   (app-state-quit?-set! state #t))
@@ -265,13 +330,16 @@
 (def (handle-submit! state)
   (let* ((inp (app-state-input state))
          (text (input-submit! inp)))
+    (tui-log "submit: text=~s" text)
     (unless (string-empty? text)
       (cond
         ;; Slash command
         ((char=? (string-ref text 0) #\/)
+         (tui-log "submit: slash-command ~s" text)
          (handle-slash-command! state text))
         ;; Normal message → agent
         (#t
+         (tui-log "submit: sending to agent")
          ;; Add user message
          (add-message! state (msg-block-user text))
          ;; Auto-scroll to bottom