ruby_jard 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +4 -1
  3. data/CHANGELOG.md +18 -0
  4. data/CNAME +1 -0
  5. data/README.md +206 -33
  6. data/_config.yml +1 -0
  7. data/docs/_config.yml +8 -0
  8. data/docs/demo.png +0 -0
  9. data/docs/index.md +238 -0
  10. data/docs/logo.jpg +0 -0
  11. data/docs/screen-backtrace.png +0 -0
  12. data/docs/screen-repl.png +0 -0
  13. data/docs/screen-source.png +0 -0
  14. data/docs/screen-threads.png +0 -0
  15. data/docs/screen-variables.png +0 -0
  16. data/images/bg_hr.png +0 -0
  17. data/images/blacktocat.png +0 -0
  18. data/images/body-bg.jpg +0 -0
  19. data/images/download-button.png +0 -0
  20. data/images/github-button.png +0 -0
  21. data/images/header-bg.jpg +0 -0
  22. data/images/highlight-bg.jpg +0 -0
  23. data/images/icon_download.png +0 -0
  24. data/images/sidebar-bg.jpg +0 -0
  25. data/images/sprite_download.png +0 -0
  26. data/javascripts/main.js +1 -0
  27. data/lib/ruby_jard.rb +3 -18
  28. data/lib/ruby_jard/box_drawer.rb +68 -22
  29. data/lib/ruby_jard/color_scheme.rb +28 -0
  30. data/lib/ruby_jard/color_schemes.rb +26 -0
  31. data/lib/ruby_jard/color_schemes/256_color_scheme.rb +64 -0
  32. data/lib/ruby_jard/color_schemes/deep_space_color_scheme.rb +63 -0
  33. data/lib/ruby_jard/column.rb +12 -6
  34. data/lib/ruby_jard/commands/continue_command.rb +1 -0
  35. data/lib/ruby_jard/commands/list_command.rb +29 -0
  36. data/lib/ruby_jard/commands/next_command.rb +1 -0
  37. data/lib/ruby_jard/commands/step_command.rb +1 -0
  38. data/lib/ruby_jard/commands/step_out_command.rb +1 -0
  39. data/lib/ruby_jard/console.rb +102 -18
  40. data/lib/ruby_jard/control_flow.rb +9 -8
  41. data/lib/ruby_jard/decorators/color_decorator.rb +46 -57
  42. data/lib/ruby_jard/decorators/inspection_decorator.rb +76 -0
  43. data/lib/ruby_jard/decorators/loc_decorator.rb +83 -107
  44. data/lib/ruby_jard/decorators/source_decorator.rb +1 -1
  45. data/lib/ruby_jard/keys.rb +1 -0
  46. data/lib/ruby_jard/layout.rb +9 -99
  47. data/lib/ruby_jard/layout_calculator.rb +151 -0
  48. data/lib/ruby_jard/layouts/narrow_layout.rb +41 -0
  49. data/lib/ruby_jard/layouts/wide_layout.rb +17 -103
  50. data/lib/ruby_jard/repl_processor.rb +5 -1
  51. data/lib/ruby_jard/repl_proxy.rb +2 -1
  52. data/lib/ruby_jard/row.rb +5 -5
  53. data/lib/ruby_jard/row_renderer.rb +108 -0
  54. data/lib/ruby_jard/screen.rb +20 -115
  55. data/lib/ruby_jard/screen_drawer.rb +8 -75
  56. data/lib/ruby_jard/screen_manager.rb +78 -55
  57. data/lib/ruby_jard/screen_renderer.rb +138 -0
  58. data/lib/ruby_jard/screens/backtrace_screen.rb +67 -69
  59. data/lib/ruby_jard/screens/menu_screen.rb +43 -38
  60. data/lib/ruby_jard/screens/source_screen.rb +43 -27
  61. data/lib/ruby_jard/screens/threads_screen.rb +91 -39
  62. data/lib/ruby_jard/screens/variables_screen.rb +72 -56
  63. data/lib/ruby_jard/session.rb +16 -0
  64. data/lib/ruby_jard/span.rb +8 -6
  65. data/lib/ruby_jard/version.rb +1 -1
  66. data/ruby_jard.gemspec +4 -4
  67. data/stylesheets/github-light.css +130 -0
  68. data/stylesheets/normalize.css +424 -0
  69. data/stylesheets/print.css +228 -0
  70. data/stylesheets/stylesheet.css +245 -0
  71. metadata +52 -21
  72. data/lib/ruby_jard/templates/column_template.rb +0 -17
  73. data/lib/ruby_jard/templates/row_template.rb +0 -22
  74. data/lib/ruby_jard/templates/space_template.rb +0 -15
  75. data/lib/ruby_jard/templates/span_template.rb +0 -25
@@ -120,7 +120,7 @@ module RubyJard
120
120
  # Do nothing
121
121
  end
122
122
 
123
- def handle_key_binding_command(options)
123
+ def handle_key_binding_command(options = {})
124
124
  method_name = "handle_#{options[:action]}_command"
125
125
  if respond_to?(method_name, true)
126
126
  send(method_name)
@@ -129,5 +129,9 @@ module RubyJard
129
129
  "Fail to handle key binding `#{options[:action]}`"
130
130
  end
131
131
  end
132
+
133
+ def handle_list_command(_options = {})
134
+ process_commands
135
+ end
132
136
  end
133
137
  end
@@ -7,6 +7,7 @@ require 'ruby_jard/commands/next_command'
7
7
  require 'ruby_jard/commands/step_command'
8
8
  require 'ruby_jard/commands/step_out_command'
9
9
  require 'ruby_jard/commands/frame_command'
10
+ require 'ruby_jard/commands/list_command'
10
11
 
11
12
  module RubyJard
12
13
  ##
@@ -80,7 +81,7 @@ module RubyJard
80
81
  end
81
82
 
82
83
  def read_key
83
- STDIN.getch(min: 0, time: KEYPRESS_POLLING)
84
+ RubyJard::Console.getch(STDIN, KEYPRESS_POLLING)
84
85
  end
85
86
 
86
87
  def repl(current_binding)
@@ -4,13 +4,13 @@ module RubyJard
4
4
  class Row
5
5
  extend Forwardable
6
6
 
7
- attr_accessor :row_template, :columns
7
+ attr_accessor :columns, :line_limit, :content
8
8
 
9
- def_delegators :@row_template, :line_limit
10
-
11
- def initialize(row_template:, columns: [])
12
- @row_template = row_template
9
+ def initialize(line_limit: 1, columns: [], ellipsis: true)
10
+ @content = []
13
11
  @columns = columns
12
+ @ellipsis = ellipsis
13
+ @line_limit = line_limit
14
14
  end
15
15
  end
16
16
  end
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyJard
4
+ ##
5
+ # Generate bitmap lines from a row's data
6
+ class RowRenderer
7
+ ELLIPSIS = ' »'
8
+
9
+ def initialize(row:, width:, height:, color_scheme:)
10
+ @row = row
11
+ @width = width
12
+ @height = height
13
+ @color_decorator = RubyJard::Decorators::ColorDecorator.new(color_scheme)
14
+ end
15
+
16
+ def render
17
+ @x = 0
18
+ @y = 0
19
+ @content_map = []
20
+
21
+ original_x = 0
22
+ @row.columns.each_with_index do |column, index|
23
+ @y = 0
24
+ @x = original_x
25
+ content_width = column.width
26
+ content_width -= 1 if index < @row.columns.length - 1
27
+ render_column(column, original_x, content_width)
28
+
29
+ original_x += column.width
30
+ end
31
+
32
+ generate_bitmap
33
+ end
34
+
35
+ def render_column(column, original_x, content_width)
36
+ width = 0
37
+ lines = 1
38
+
39
+ column.spans.each do |span|
40
+ line_content = span.content
41
+
42
+ until line_content.nil? || line_content.empty?
43
+ if column.word_wrap == RubyJard::Column::WORD_WRAP_NORMAL
44
+ if content_width - width < line_content.length && width != 0
45
+ width = 0
46
+ lines += 1
47
+ @y += 1
48
+ @x = original_x
49
+ end
50
+ elsif column.word_wrap == RubyJard::Column::WORD_WRAP_BREAK_WORD
51
+ if content_width - width <= 0
52
+ width = 0
53
+ lines += 1
54
+ @y += 1
55
+ @x = original_x
56
+ end
57
+ elsif content_width - width <= 0
58
+ return
59
+ end
60
+ drawing_content = line_content[0..content_width - width - 1]
61
+ line_content = line_content[content_width - width..-1]
62
+ width += drawing_content.length
63
+
64
+ if !@row.line_limit.nil? && lines >= @row.line_limit && !line_content.nil? && !line_content.empty?
65
+ drawing_content[drawing_content.length - ELLIPSIS.length..-1] = ELLIPSIS
66
+ draw_content(drawing_content, span.styles)
67
+ return
68
+ else
69
+ draw_content(drawing_content, span.styles)
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ def draw_content(drawing_content, styles)
76
+ return if @y < 0 || @y >= @height
77
+
78
+ @content_map[@y] ||= []
79
+ @content_map[@y][@x] = [styles, drawing_content]
80
+ @x += drawing_content.length
81
+ end
82
+
83
+ def generate_bitmap
84
+ @row.content = []
85
+ @content_map.each do |line|
86
+ line_content = ''
87
+ pending_content = ''
88
+
89
+ cell_index = 0
90
+ while cell_index < @width
91
+ cell = line[cell_index]
92
+ if cell.nil? || cell[1].empty?
93
+ pending_content += ' '
94
+ cell_index += 1
95
+ else
96
+ line_content += @color_decorator.decorate(:background, pending_content)
97
+ line_content += @color_decorator.decorate(cell[0], cell[1])
98
+ pending_content = ''
99
+ cell_index += cell[1].length
100
+ end
101
+ end
102
+
103
+ line_content += @color_decorator.decorate(:background, pending_content) unless pending_content.empty?
104
+ @row.content << line_content
105
+ end
106
+ end
107
+ end
108
+ end
@@ -6,134 +6,39 @@ module RubyJard
6
6
  # generated based on input layout specifiation, screen data, and top-left
7
7
  # corner cordination.
8
8
  class Screen
9
- attr_accessor :output, :rows, :width, :height, :x, :y
9
+ attr_accessor :layout, :rows, :color_scheme, :window, :cursor, :selected
10
10
 
11
- def initialize(screen_template:, session: nil, width:, height:, x:, y:)
11
+ def initialize(session: nil, layout:, color_scheme:)
12
12
  @session = session || RubyJard.current_session
13
- @screen_template = screen_template
14
- @width = width
15
- @height = height
16
- @x = x
17
- @y = y
18
- end
19
-
20
- def draw(output)
21
- calculate
22
- drawer = RubyJard::ScreenDrawer.new(
23
- output: output,
24
- screen: self,
25
- x: @x,
26
- y: @y
27
- )
28
- drawer.draw
29
- end
30
-
31
- def data_size
32
- raise NotImplementedError, "#{self.class} must implement #data_size method"
33
- end
34
-
35
- def data_window
36
- raise NotImplementedError, "#{self.class} must implement #data_window method"
37
- end
38
-
39
- def calculate
13
+ @layout = layout
14
+ @color_scheme = color_scheme
15
+ @window = []
16
+ @cursor = nil
17
+ @selected = 0
40
18
  @rows = []
41
- row_template = @screen_template.row_template
42
- @rows = data_window.map.with_index do |data_row, index|
43
- create_row(row_template, data_row, index)
44
- end
45
- column_widths = calculate_column_widths(row_template, @rows)
46
- fill_column_widths(@rows, column_widths)
19
+ @need_to_render = true
47
20
  end
48
21
 
49
- private
22
+ def move_down; end
50
23
 
51
- def calculate_column_widths(row_template, rows)
52
- column_widths = {}
53
- ideal_column_width = @width / row_template.columns.length
54
- row_template.columns.each_with_index do |_column_template, column_index|
55
- column_widths[column_index] ||= 0
56
- rows.each do |row|
57
- column = row.columns[column_index]
58
- if column.content_length > ideal_column_width
59
- column_widths[column_index] = nil
60
- break
61
- elsif column.content_length > column_widths[column_index]
62
- column_widths[column_index] = column.content_length
63
- end
64
- end
65
- end
66
- column_widths
67
- end
24
+ def move_up; end
68
25
 
69
- def fill_column_widths(rows, column_widths)
70
- fixed_count = column_widths.length
71
- fixed_width = column_widths.values.inject(0) do |sum, col|
72
- col.nil? ? sum : sum + col
73
- end
26
+ def page_up; end
74
27
 
75
- rows.each do |row|
76
- total_width = 0
77
- row.columns.each_with_index do |column, column_index|
78
- column.width =
79
- if column_index == row.columns.length - 1
80
- @width - total_width
81
- elsif column_widths[column_index].nil?
82
- (@width - fixed_width) / fixed_count
83
- else
84
- column_widths[column_index]
85
- end
86
- total_width += column.width
87
- end
88
- end
89
- end
28
+ def page_down; end
90
29
 
91
- def create_row(row_template, data_row, index)
92
- row = Row.new(row_template: row_template)
93
- row.columns = row_template.columns.map do |column_template|
94
- create_column(column_template, data_row, index)
95
- end
96
- row
97
- end
30
+ def click(relative_x, relative_y); end
98
31
 
99
- def create_column(column_template, data_row, index)
100
- column = Column.new(column_template: column_template)
101
- column.spans = column_template.spans.map do |span_template|
102
- create_span(span_template, data_row, index)
103
- end.flatten
104
- column.content_length =
105
- column.spans.map(&:content_length).inject(&:+) +
106
- column.margin_left +
107
- column.margin_right
108
- column
32
+ def build
33
+ raise NotImplementedError, "#{self.class} should implement this method."
109
34
  end
110
35
 
111
- def create_span(span_template, data_row, index)
112
- span = Span.new(span_template: span_template)
113
- span_content_method = "span_#{span_template.name}".to_sym
114
-
115
- if respond_to?(span_content_method)
116
- content, styles = send(span_content_method, data_row, index)
117
- if content.nil?
118
- span.content = ''
119
- span.content_length = 0
120
- elsif content.is_a?(Array)
121
- content.each do |sub_span|
122
- sub_span.styles += Array(styles).flatten.compact
123
- end
124
- return content
125
- else
126
- content = ' ' * span_template.margin_left + content if span_template.margin_left
127
- content += ' ' * span_template.margin_right if span_template.margin_right
128
- span.content = content
129
- span.styles = Array(styles).flatten.compact
130
- span.content_length = span.content.length
131
- end
132
- else
133
- raise NotImplementedError, "#{self.class} must implement #{span_content_method} method"
134
- end
36
+ def need_to_render?
37
+ @need_to_render == true
38
+ end
135
39
 
136
- span
40
+ def mark_rendered!
41
+ @need_to_render = false
137
42
  end
138
43
  end
139
44
  end
@@ -1,89 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RubyJard
4
- ##
5
- # Draw a screen and its rows into the output interface.
6
4
  class ScreenDrawer
7
- attr_reader :output
8
-
9
- ELLIPSIS = ' »'
10
-
11
- def initialize(output:, screen:, x:, y:)
5
+ def initialize(output:, screen:, color_scheme:)
12
6
  @output = output
13
- @color_decorator = RubyJard::Decorators::ColorDecorator.new
14
- @x = x
15
- @y = y
16
- @original_x = x
17
- @original_y = y
18
7
  @screen = screen
8
+ @color_decorator = RubyJard::Decorators::ColorDecorator.new(color_scheme)
19
9
  end
20
10
 
21
11
  def draw
22
- @original_x = @x
23
- @screen.rows.each do |row|
24
- draw_columns(row, row.columns)
25
- @y += 1
26
- @x = @original_x
12
+ @screen.window.each_with_index do |line, index|
13
+ RubyJard::Console.move_to(@output, @screen.layout.x, @screen.layout.y + index)
14
+ @output.print line
27
15
  end
28
- end
29
-
30
- private
31
-
32
- def draw_columns(row, columns)
33
- columns.each do |column|
34
- width = 0
35
- lines = 1
36
- column_content_width = column.width - column.margin_left - column.margin_right
37
- @x += column.margin_left
38
- RubyJard::Console.move_to(@output, @x, @y)
39
-
40
- column.spans.each do |span|
41
- line_content = span.content
42
-
43
- until line_content.nil? || line_content.empty?
44
- if column_content_width - width <= 0
45
- width = 0
46
- lines += 1
47
- @y += 1
48
- RubyJard::Console.move_to(@output, @x, @y)
49
- end
50
- drawing_content = line_content[0..column_content_width - width - 1]
51
- line_content = line_content[column_content_width - width..-1]
52
- width += drawing_content.length
53
-
54
- if !row.line_limit.nil? && lines >= row.line_limit && !line_content.nil? && !line_content.empty?
55
- drawing_content[drawing_content.length - ELLIPSIS.length..-1] = ELLIPSIS
56
- protected_print @color_decorator.decorate(drawing_content, *span.styles)
57
- break
58
- else
59
- protected_print @color_decorator.decorate(drawing_content, *span.styles)
60
- end
61
- end
62
- end
63
- @x += column_content_width + column.margin_right
16
+ (@screen.window.length..@screen.layout.height - 1).each do |index|
17
+ RubyJard::Console.move_to(@output, @screen.layout.x, @screen.layout.y + index)
18
+ @output.print @color_decorator.decorate(:background, ' ' * @screen.layout.width)
64
19
  end
65
20
  end
66
-
67
- def protected_print(content)
68
- # TODO: currently, only row overflow is detected. Definitely should handle column overflow
69
- return if @y < @original_y || @y > @original_y + @screen.height - 1
70
-
71
- @output.print content
72
- end
73
-
74
- def default_frame_styles
75
- {
76
- style: {
77
- fg: :white
78
- },
79
- border: {
80
- bottom_left: false,
81
- bottom_right: false,
82
- bottom: false,
83
- left: :line,
84
- right: false
85
- }
86
- }
87
- end
88
21
  end
89
22
  end
@@ -6,11 +6,24 @@ require 'ruby_jard/decorators/color_decorator'
6
6
  require 'ruby_jard/decorators/path_decorator'
7
7
  require 'ruby_jard/decorators/loc_decorator'
8
8
  require 'ruby_jard/decorators/source_decorator'
9
+ require 'ruby_jard/decorators/inspection_decorator'
9
10
 
10
11
  require 'ruby_jard/screen'
12
+ require 'ruby_jard/screens'
13
+
14
+ require 'ruby_jard/color_scheme'
15
+ require 'ruby_jard/color_schemes'
16
+ require 'ruby_jard/color_schemes/deep_space_color_scheme'
17
+ require 'ruby_jard/color_schemes/256_color_scheme'
18
+
19
+ require 'ruby_jard/row'
20
+ require 'ruby_jard/column'
21
+ require 'ruby_jard/span'
22
+ require 'ruby_jard/row_renderer'
23
+ require 'ruby_jard/screen_renderer'
11
24
  require 'ruby_jard/box_drawer'
12
25
  require 'ruby_jard/screen_drawer'
13
- require 'ruby_jard/screens'
26
+
14
27
  require 'ruby_jard/screens/source_screen'
15
28
  require 'ruby_jard/screens/backtrace_screen'
16
29
  require 'ruby_jard/screens/threads_screen'
@@ -19,16 +32,11 @@ require 'ruby_jard/screens/menu_screen'
19
32
 
20
33
  require 'ruby_jard/templates/layout_template'
21
34
  require 'ruby_jard/templates/screen_template'
22
- require 'ruby_jard/templates/row_template'
23
- require 'ruby_jard/templates/column_template'
24
- require 'ruby_jard/templates/span_template'
25
- require 'ruby_jard/templates/space_template'
26
35
 
27
36
  require 'ruby_jard/layouts/wide_layout'
37
+ require 'ruby_jard/layouts/narrow_layout'
28
38
  require 'ruby_jard/layout'
29
- require 'ruby_jard/row'
30
- require 'ruby_jard/column'
31
- require 'ruby_jard/span'
39
+ require 'ruby_jard/layout_calculator'
32
40
 
33
41
  module RubyJard
34
42
  ##
@@ -59,7 +67,7 @@ module RubyJard
59
67
  return if started?
60
68
 
61
69
  RubyJard::Console.start_alternative_terminal(@output)
62
- RubyJard::Console.hard_clear_screen(@output)
70
+ RubyJard::Console.clear_screen(@output)
63
71
 
64
72
  def $stdout.write(string)
65
73
  if !RubyJard::ScreenManager.updating? && RubyJard::ScreenManager.started?
@@ -86,9 +94,9 @@ module RubyJard
86
94
  @started = false
87
95
 
88
96
  RubyJard::Console.stop_alternative_terminal(@output)
89
- RubyJard::Console.cooked!(@output)
90
- RubyJard::Console.echo!(@output)
91
- RubyJard::Console.show_cursor(@output)
97
+ RubyJard::Console.cooked!
98
+ RubyJard::Console.enable_echo!
99
+ RubyJard::Console.enable_cursor!
92
100
 
93
101
  unless @output_storage.string.empty?
94
102
  @output.puts ''
@@ -102,25 +110,35 @@ module RubyJard
102
110
  start unless started?
103
111
  @updating = true
104
112
 
105
- RubyJard::Console.hide_cursor(@output)
106
- clear_screen
113
+ RubyJard::Console.disable_cursor!
107
114
  width, height = RubyJard::Console.screen_size(@output)
108
- screen_layouts = calculate_layouts(width, height)
109
- draw_screens(screen_layouts)
110
- jump_to_prompt(screen_layouts)
115
+
116
+ @layouts = calculate_layouts(width, height)
117
+ @screens = build_screens(@layouts)
118
+ draw_box(@screens)
119
+ draw_screens(@screens)
120
+
121
+ RubyJard::Console.move_to(@output, 0, total_screen_height(@layouts) + 1)
122
+ RubyJard::Console.clear_screen_to_end(@output)
123
+
111
124
  draw_debug(width, height)
112
125
  rescue StandardError => e
113
- clear_screen
126
+ RubyJard::Console.clear_screen(@output)
114
127
  draw_error(e, height)
115
128
  ensure
116
129
  # You don't want to mess up previous user TTY no matter happens
117
- RubyJard::Console.cooked!(@output)
118
- RubyJard::Console.echo!(@output)
119
- RubyJard::Console.show_cursor(@output)
130
+ RubyJard::Console.cooked!
131
+ RubyJard::Console.enable_echo!
132
+ RubyJard::Console.enable_cursor!
120
133
  @updating = false
121
134
  end
122
135
 
136
+ def scroll; end
137
+
138
+ def click; end
139
+
123
140
  def draw_error(exception, height = 0)
141
+ @output.print RubyJard::Decorators::ColorDecorator::CSI_RESET
124
142
  @output.puts '--- Error ---'
125
143
  @output.puts "Internal error from Jard. I'm sorry to mess up your debugging experience."
126
144
  @output.puts 'It would be great if you can submit an issue in https://github.com/nguyenquangminh0711/ruby_jard/issues'
@@ -129,52 +147,62 @@ module RubyJard
129
147
  if height == 0
130
148
  @output.puts exception.backtrace
131
149
  else
132
- @output.puts exception.backtrace.first(height - 5)
150
+ @output.puts exception.backtrace.first(10)
133
151
  end
134
152
  @output.puts '-------------'
135
153
  end
136
154
 
137
-
138
155
  private
139
156
 
140
157
  def calculate_layouts(width, height)
141
158
  layout = pick_layout(width, height)
142
- RubyJard::Layout.calculate(
143
- layout: layout,
159
+ RubyJard::LayoutCalculator.calculate(
160
+ layout_template: layout,
144
161
  width: width, height: height,
145
162
  x: 0, y: 0
146
163
  )
147
164
  end
148
165
 
166
+ def build_screens(layouts)
167
+ layouts.map do |layout|
168
+ screen_class = fetch_screen(layout.template.screen)
169
+ screen = screen_class.new(
170
+ layout: layout,
171
+ color_scheme: pick_color_scheme
172
+ )
173
+ screen.build
174
+ render_screen(screen)
175
+ screen
176
+ end
177
+ end
178
+
149
179
  def draw_box(screens)
150
180
  RubyJard::BoxDrawer.new(
151
181
  output: @output,
152
- screens: screens
182
+ screens: screens,
183
+ color_scheme: pick_color_scheme
153
184
  ).draw
154
185
  end
155
186
 
156
- def draw_screens(screen_layouts)
157
- screens = screen_layouts.map do |screen_template, width, height, x, y|
158
- screen = fetch_screen(screen_template.screen)
159
- screen&.new(
160
- screen_template: screen_template,
161
- width: width, height: height,
162
- x: x, y: y
163
- )
164
- end
165
- draw_box(screens)
166
- adjust_screen_contents(screens)
187
+ def draw_screens(screens)
167
188
  screens.each do |screen|
168
- screen.draw(@output)
189
+ RubyJard::ScreenDrawer.new(
190
+ output: @output,
191
+ screen: screen,
192
+ color_scheme: pick_color_scheme
193
+ ).draw
169
194
  end
170
195
  end
171
196
 
172
- def jump_to_prompt(screen_layouts)
173
- prompt_y = screen_layouts.map { |_template, _width, screen_height, _x, y| y + screen_height }.max
174
- RubyJard::Console.move_to(@output, 0, prompt_y)
197
+ def render_screen(screen)
198
+ RubyJard::ScreenRenderer.new(
199
+ screen: screen,
200
+ color_scheme: pick_color_scheme
201
+ ).render
175
202
  end
176
203
 
177
204
  def draw_debug(_width, height)
205
+ @output.print RubyJard::Decorators::ColorDecorator::CSI_RESET
178
206
  unless RubyJard.debug_info.empty?
179
207
  @output.puts '--- Debug ---'
180
208
  RubyJard.debug_info.first(height - 2).each do |line|
@@ -185,24 +213,14 @@ module RubyJard
185
213
  RubyJard.clear_debug
186
214
  end
187
215
 
188
- def adjust_screen_contents(screens)
189
- # After drawing the box, screen sizes should be updated to reflect content-only area
190
- screens.each do |screen|
191
- screen.width -= 2
192
- screen.height -= 2
193
- screen.x += 1
194
- screen.y += 1
195
- end
196
- end
197
-
198
- def clear_screen
199
- RubyJard::Console.clear_screen(@output)
200
- end
201
-
202
216
  def fetch_screen(name)
203
217
  RubyJard::Screens[name]
204
218
  end
205
219
 
220
+ def total_screen_height(layouts)
221
+ layouts.map { |layout| layout.y + layout.height }.max
222
+ end
223
+
206
224
  def pick_layout(width, height)
207
225
  RubyJard::DEFAULT_LAYOUT_TEMPLATES.each do |template|
208
226
  matched = true
@@ -218,5 +236,10 @@ module RubyJard
218
236
  end
219
237
  RubyJard::DEFAULT_LAYOUT_TEMPLATES.first
220
238
  end
239
+
240
+ def pick_color_scheme
241
+ # TODO: Fallback to a default color scheme if not found
242
+ RubyJard::ColorSchemes[RubyJard::DEFAULT_COLOR_SCHEME].new
243
+ end
221
244
  end
222
245
  end