Fix TUI terminal: command output, prompt colors, and missing TLS stubs

ober

139b5b2c12315d0bbf1aa12a25600dca4fbe4b51

diff --git a/build-binary.ss b/build-binary.ss
index 243411f..d169592 100644
--- a/build-binary.ss
+++ b/build-binary.ss
@@ -525,7 +525,32 @@ echo OK"
   (let* ((cmd "gcc -c -O2 -o jemacs-jsh-coreutils-stubs.o support/jsh_coreutils_stubs.c -Wall 2>&1"))
     (unless (= 0 (system cmd))
       (display "Error: jsh_coreutils_stubs.c (dynamic) compilation failed\n")
-      (exit 1))))
+      (exit 1)))
+  ;; TLS rustls stubs (jerboa_tls_* Rust FFI symbols — jemacs TUI doesn't use HTTPS)
+  (let ((stub-file "jemacs-tls-stubs.c"))
+    (call-with-output-file stub-file
+      (lambda (out)
+        (fprintf out "/* Stub — jemacs TUI doesn't use rustls TLS */~n")
+        (fprintf out "#include <stddef.h>~n#include <stdint.h>~n")
+        (fprintf out "uint64_t jerboa_tls_server_new(const char *c, const char *k) { return 0; }~n")
+        (fprintf out "uint64_t jerboa_tls_server_new_mtls(const char *c, const char *k, const char *ca) { return 0; }~n")
+        (fprintf out "void jerboa_tls_server_free(uint64_t s) {}~n")
+        (fprintf out "uint64_t jerboa_tls_accept(uint64_t s, int fd) { return 0; }~n")
+        (fprintf out "uint64_t jerboa_tls_connect(const char *h, int p) { return 0; }~n")
+        (fprintf out "uint64_t jerboa_tls_connect_pinned(const char *h, int p, const uint8_t *fp, size_t fplen) { return 0; }~n")
+        (fprintf out "uint64_t jerboa_tls_connect_mtls(const char *h, int p, const char *c, const char *k, const char *ca) { return 0; }~n")
+        (fprintf out "int jerboa_tls_read(uint64_t s, uint8_t *buf, uint64_t len) { return -1; }~n")
+        (fprintf out "int jerboa_tls_write(uint64_t s, const uint8_t *buf, uint64_t len) { return -1; }~n")
+        (fprintf out "int jerboa_tls_flush(uint64_t s) { return -1; }~n")
+        (fprintf out "void jerboa_tls_close(uint64_t s) {}~n")
+        (fprintf out "int jerboa_tls_set_nonblock(uint64_t s, int nb) { return -1; }~n")
+        (fprintf out "int jerboa_tls_get_fd(uint64_t s) { return -1; }~n")
+        (fprintf out "size_t jerboa_last_error(uint8_t *buf, size_t len) { return 0; }~n"))
+      'replace)
+    (let ((cmd "gcc -c -O2 -o jemacs-tls-stubs.o jemacs-tls-stubs.c -Wall 2>&1"))
+      (unless (= 0 (system cmd))
+        (display "Error: TLS stubs compilation failed\n")
+        (exit 1)))))
 
 ;; jemacs-main.c
 (let* ((static-flag (if jemacs-static? "-DJEMACS_STATIC_BUILD" ""))
@@ -575,6 +600,7 @@ tui_static_symbols.o \
 jemacs-main.o jemacs-pcre2-shim.o jemacs-repl-shim.o \
 jemacs-jsh-ffi.o jemacs-libcoreutils.o jemacs-embed-crypto.o \
 jemacs-ssh-agent-stub.o jemacs-jsh-coreutils-stubs.o \
+jemacs-tls-stubs.o \
 -L~a -lkernel -llz4 -lz \
 ~a ~a \
 -lm -ldl -lpthread 2>&1"
diff --git a/src/jerboa-emacs/app.ss b/src/jerboa-emacs/app.ss
index 41b1113..2f8bdbc 100644
--- a/src/jerboa-emacs/app.ss
+++ b/src/jerboa-emacs/app.ss
@@ -429,14 +429,18 @@
                ;; Feed data to VT100 screen buffer, then render
                (begin
                  (vtscreen-feed! vt data)
-                 (let* ((rendered (vtscreen-render vt))
-                        (full (if (vtscreen-alt-screen? vt)
-                                rendered
-                                (string-append (or (terminal-state-pre-pty-text ts) "")
-                                               rendered))))
-                   (editor-set-text ed full)
-                   (editor-goto-pos ed (editor-get-text-length ed))
-                   (editor-scroll-caret ed)))
+                 (if (vtscreen-alt-screen? vt)
+                   ;; Alt-screen (vim, top): replace entire content
+                   (let ((rendered (vtscreen-render vt)))
+                     (editor-set-text ed rendered)
+                     (editor-goto-pos ed (editor-get-text-length ed))
+                     (editor-scroll-caret ed))
+                   ;; Normal command output: vtscreen accumulates it,
+                   ;; but we don't replace text (that nukes styles).
+                   ;; Just scroll to end — 'done handler will append final output.
+                   (begin
+                     (editor-goto-pos ed (editor-get-text-length ed))
+                     (editor-scroll-caret ed))))
                ;; Fallback: strip ANSI and append (no vtscreen)
                (let* ((segments (parse-ansi-segments data))
                       (start-pos (editor-get-text-length ed)))
@@ -452,16 +456,17 @@
            (when win
              (let ((ed (edit-window-editor win)))
                ;; For full-screen programs: restore pre-PTY text
-               ;; For simple commands: keep output
-               (when pre-text
-                 (if alt-screen?
-                   (editor-set-text ed pre-text)
-                   (let* ((output (or final-render ""))
-                          (sep (if (and (> (string-length output) 0)
-                                       (not (char=? (string-ref output (- (string-length output) 1)) #\newline)))
-                                 "\n" ""))
-                          (full (string-append pre-text output sep)))
-                     (editor-set-text ed full))))
+               ;; For simple commands: append output (preserving existing styles)
+               (if (and pre-text alt-screen?)
+                 ;; Alt-screen (vim, less): restore original text
+                 (editor-set-text ed pre-text)
+                 ;; Normal commands: append rendered output after existing text
+                 ;; Don't use editor-set-text — it nukes all Scintilla styles
+                 (let ((output (or final-render "")))
+                   (when (> (string-length output) 0)
+                     (editor-append-text ed output)
+                     (unless (char=? (string-ref output (- (string-length output) 1)) #\newline)
+                       (editor-append-text ed "\n")))))
                (let* ((raw-prompt (terminal-prompt-raw ts))
                       (segments (parse-ansi-segments raw-prompt))
                       (start-pos (editor-get-text-length ed)))
diff --git a/src/jerboa-emacs/editor-core.ss b/src/jerboa-emacs/editor-core.ss
index 8bea6d8..88c0984 100644
--- a/src/jerboa-emacs/editor-core.ss
+++ b/src/jerboa-emacs/editor-core.ss
@@ -2154,7 +2154,10 @@
                (text-byte-len (bytevector-length text-bytes))
                (prompt-byte-pos (terminal-state-prompt-pos ts))  ;; byte position
                (input (if (< prompt-byte-pos text-byte-len)
-                        (utf8->string (bytevector-copy text-bytes prompt-byte-pos text-byte-len))
+                        (let* ((input-len (- text-byte-len prompt-byte-pos))
+                               (sub (make-bytevector input-len)))
+                          (bytevector-copy! text-bytes prompt-byte-pos sub 0 input-len)
+                          (utf8->string sub))
                         "")))
           ;; Append newline after user input
           (editor-append-text ed "\n")