Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions lib/cli/ui/ansi.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
module CLI
module UI
module ANSI
autoload :Replay, 'cli/ui/ansi/replay'

ESC = "\x1b"
# https://ghostty.org/docs/vt/concepts/sequences#csi-sequences
CSI_SEQUENCE = /\x1b\[[\d;:]+[\x20-\x2f]*?[\x40-\x7e]/
Expand Down Expand Up @@ -48,6 +50,31 @@ def strip_codes(str)
str.gsub(Regexp.union(CSI_SEQUENCE, OSC_SEQUENCE, /\r/), '')
end

# Replays the viewport-independent cursor controls in a captured
# terminal stream, so repaints (spinners, progress bars) collapse
# onto their final state instead of accumulating one frame per tick.
#
# Where +strip_codes+ deletes control sequences, this applies them.
# Operations that assume a viewport -- screen-relative positioning,
# display erasure, wrapping, alternate screens -- are ignored: a
# capture does not record scrolling, so screen coordinates have no
# buffer row to map onto. Commands a repaint has no use for, from
# character editing to charset translation, are dropped without
# effect. The stream is decoded as UTF-8 whatever its tagged
# encoding, replacing bytes that don't decode. Text is tracked one
# character per column, so wide or combining characters can misalign
# when overwritten. Trailing whitespace on every line is trimmed: a
# terminal renders nothing there.
#
# ==== Attributes
#
# - +str+ - The captured terminal stream to replay
#
#: (String str) -> String
def replay(str)
Replay.render(str)
end

# Returns an ANSI control sequence
#
# ==== Attributes
Expand Down
Loading
Loading