Class: Legion::TTY::App

Inherits:
Object
  • Object
show all
Extended by:
Logging::Helper
Includes:
Logging::Helper
Defined in:
lib/legion/tty/app.rb

Overview

rubocop:disable Metrics/ClassLength

Constant Summary collapse

CONFIG_DIR =
File.expand_path('~/.legionio/settings')
KEY_MAP =

Key normalization: raw escape sequences and control chars to symbols

{
  "\e[A" => :up, "\e[B" => :down, "\e[C" => :right, "\e[D" => :left,
  "\r" => :enter, "\n" => :enter, "\e" => :escape,
  "\e[5~" => :page_up, "\e[6~" => :page_down,
  "\e[H" => :home, "\eOH" => :home, "\e[F" => :end, "\eOF" => :end,
  "\e[1~" => :home, "\e[4~" => :end,
  "\x7f" => :backspace, "\b" => :backspace, "\t" => :tab,
  "\x03" => :ctrl_c, "\x04" => :ctrl_d,
  "\x01" => :ctrl_a, "\x05" => :ctrl_e,
  "\x0B" => :ctrl_k, "\x0C" => :ctrl_l, "\x13" => :ctrl_s,
  "\x15" => :ctrl_u
}.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config_dir: CONFIG_DIR, skip_rain: false) ⇒ App

Returns a new instance of App.



59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/legion/tty/app.rb', line 59

def initialize(config_dir: CONFIG_DIR, skip_rain: false)
  @config_dir = config_dir
  @skip_rain = skip_rain
  @config = load_config
  @credentials = load_credentials
  @screen_manager = ScreenManager.new
  @hotkeys = Hotkeys.new
  @llm_chat = nil
  @input_bar = nil
  @running = false
  @prev_frame = []
  @raw_mode = false
end

Instance Attribute Details

#configObject (readonly)

Returns the value of attribute config.



38
39
40
# File 'lib/legion/tty/app.rb', line 38

def config
  @config
end

#credentialsObject (readonly)

Returns the value of attribute credentials.



38
39
40
# File 'lib/legion/tty/app.rb', line 38

def credentials
  @credentials
end

#hotkeysObject (readonly)

Returns the value of attribute hotkeys.



38
39
40
# File 'lib/legion/tty/app.rb', line 38

def hotkeys
  @hotkeys
end

#input_barObject (readonly)

Returns the value of attribute input_bar.



38
39
40
# File 'lib/legion/tty/app.rb', line 38

def input_bar
  @input_bar
end

#llm_chatObject (readonly)

Returns the value of attribute llm_chat.



38
39
40
# File 'lib/legion/tty/app.rb', line 38

def llm_chat
  @llm_chat
end

#screen_managerObject (readonly)

Returns the value of attribute screen_manager.



38
39
40
# File 'lib/legion/tty/app.rb', line 38

def screen_manager
  @screen_manager
end

Class Method Details

.first_run?(config_dir: CONFIG_DIR) ⇒ Boolean

Returns:

  • (Boolean)


55
56
57
# File 'lib/legion/tty/app.rb', line 55

def self.first_run?(config_dir: CONFIG_DIR)
  !File.exist?(File.join(config_dir, 'identity.json'))
end

.parse_argv(argv) ⇒ Object



49
50
51
52
53
# File 'lib/legion/tty/app.rb', line 49

def self.parse_argv(argv)
  opts = {}
  opts[:skip_rain] = true if argv.include?('--skip-rain')
  opts
end

.run(argv = []) ⇒ Object



40
41
42
43
44
45
46
47
# File 'lib/legion/tty/app.rb', line 40

def self.run(argv = [])
  opts = parse_argv(argv)
  app = new(**opts)
  app.start
rescue Interrupt => e
  log.debug { "app interrupted: #{e.message}" }
  app&.shutdown
end

Instance Method Details

#render_frameObject

Public: called by screens (e.g., Chat during LLM streaming) to force a re-render rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/legion/tty/app.rb', line 82

def render_frame
  width = terminal_width
  height = terminal_height
  active = @screen_manager.active_screen
  return unless active

  has_input = active.respond_to?(:needs_input_bar?) && active.needs_input_bar?
  screen_height = has_input ? height - 1 : height

  lines = active.render(width, screen_height)
  lines << @input_bar.render_line(width: width) if has_input && @input_bar

  lines = lines[0, height] if lines.size > height
  lines += Array.new(height - lines.size, '') if lines.size < height

  lines = composite_overlay(lines, width, height) if @screen_manager.overlay

  write_differential(lines, width)

  if has_input && @input_bar
    col = [@input_bar.cursor_column, width - 1].min
    $stdout.print cursor.move_to(col, height - 1)
  end

  $stdout.flush
rescue StandardError => e
  log.warn { "render_frame failed: #{e.message}" }
end

#shutdownObject



133
134
135
136
# File 'lib/legion/tty/app.rb', line 133

def shutdown
  @running = false
  @screen_manager.teardown_all
end

#startObject



73
74
75
76
77
78
# File 'lib/legion/tty/app.rb', line 73

def start
  setup_hotkeys
  run_onboarding if self.class.first_run?(config_dir: @config_dir)
  setup_for_chat
  run_loop
end

#toggle_dashboardObject



122
123
124
125
126
127
128
129
130
131
# File 'lib/legion/tty/app.rb', line 122

def toggle_dashboard
  active = @screen_manager.active_screen
  if active.is_a?(Screens::Dashboard)
    @screen_manager.pop
  else
    require_relative 'screens/dashboard'
    dashboard = Screens::Dashboard.new(self)
    @screen_manager.push(dashboard)
  end
end

#with_cooked_modeObject

Temporarily exit raw mode for blocking prompts (TTY::Prompt, etc.)



113
114
115
116
117
118
119
120
# File 'lib/legion/tty/app.rb', line 113

def with_cooked_mode(&)
  return yield unless @raw_mode

  $stdout.print cursor.show
  $stdin.cooked(&)
  $stdout.print cursor.hide
  @prev_frame = []
end