Fix ANSI style preservation in visible-subs and modal rendering
- Track active styles before substring range and prepend them when entering range - Add reset codes around modal lines to prevent style bleeding between layers Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
+31
-14
@@ -159,11 +159,13 @@
|
||||
(defn visible-subs
|
||||
"Substring based on visible character positions (ignoring ANSI escape codes).
|
||||
Returns substring from visible position start to end (or end of string).
|
||||
Preserves ANSI escape sequences that affect the visible portion."
|
||||
Preserves ANSI escape sequences that affect the visible portion, including
|
||||
styles that were set before the start position but still active."
|
||||
([s start] (visible-subs s start nil))
|
||||
([s start end]
|
||||
(when s
|
||||
(let [ansi-pattern #"\u001b\[[0-9;]*m"
|
||||
reset-pattern #"\u001b\[0m"
|
||||
;; Split string into segments: ANSI codes and regular text
|
||||
segments (loop [remaining s
|
||||
result []]
|
||||
@@ -180,22 +182,32 @@
|
||||
(conj result {:type :text :text (subs remaining 0 idx)}))))
|
||||
;; No more ANSI codes, rest is text
|
||||
(conj result {:type :text :text remaining}))))
|
||||
;; Build result by tracking visible position
|
||||
;; Build result by tracking visible position and active styles
|
||||
result (loop [segs segments
|
||||
visible-pos 0
|
||||
output []
|
||||
in-range? false]
|
||||
in-range? false
|
||||
active-styles []] ;; Track styles set before range
|
||||
(if (empty? segs)
|
||||
output
|
||||
(let [{:keys [type text]} (first segs)]
|
||||
(if (= type :ansi)
|
||||
;; Always include ANSI codes that appear in or after range start
|
||||
(recur (rest segs)
|
||||
visible-pos
|
||||
(if (or in-range? (>= visible-pos start))
|
||||
(if (or in-range? (>= visible-pos start))
|
||||
;; In range - include ANSI codes directly
|
||||
(recur (rest segs)
|
||||
visible-pos
|
||||
(conj output text)
|
||||
output)
|
||||
in-range?)
|
||||
in-range?
|
||||
active-styles)
|
||||
;; Before range - track active styles
|
||||
(let [new-styles (if (re-matches reset-pattern text)
|
||||
[] ;; Reset clears all active styles
|
||||
(conj active-styles text))]
|
||||
(recur (rest segs)
|
||||
visible-pos
|
||||
output
|
||||
in-range?
|
||||
new-styles)))
|
||||
;; Text segment
|
||||
(let [seg-len (count text)
|
||||
seg-end (+ visible-pos seg-len)
|
||||
@@ -203,19 +215,24 @@
|
||||
(cond
|
||||
;; Segment entirely before range - skip
|
||||
(<= seg-end start)
|
||||
(recur (rest segs) seg-end output false)
|
||||
(recur (rest segs) seg-end output false active-styles)
|
||||
|
||||
;; Segment entirely within or after range end - take partial or stop
|
||||
(>= visible-pos effective-end)
|
||||
output
|
||||
|
||||
;; Segment overlaps range
|
||||
;; Segment overlaps range - entering range, prepend active styles
|
||||
:else
|
||||
(let [take-start (max 0 (- start visible-pos))
|
||||
take-end (min seg-len (- effective-end visible-pos))
|
||||
portion (subs text take-start take-end)]
|
||||
portion (subs text take-start take-end)
|
||||
;; Prepend active styles when first entering range
|
||||
output-with-styles (if in-range?
|
||||
output
|
||||
(into output active-styles))]
|
||||
(recur (rest segs)
|
||||
seg-end
|
||||
(conj output portion)
|
||||
true))))))))]
|
||||
(conj output-with-styles portion)
|
||||
true
|
||||
active-styles))))))))]
|
||||
(apply str result)))))
|
||||
|
||||
+1
-1
@@ -282,7 +282,7 @@
|
||||
bg-after (if (< bg-after-start bg-width)
|
||||
(ansi/visible-subs padded-bg-line bg-after-start)
|
||||
"")]
|
||||
(str bg-before modal-line bg-after))
|
||||
(str bg-before ansi/reset modal-line ansi/reset bg-after))
|
||||
;; No modal on this row, just background
|
||||
(ansi/pad-right bg-line bg-width))))))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user