tng 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 (40) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +15 -0
  3. data/LICENSE.md +32 -0
  4. data/README.md +413 -0
  5. data/Rakefile +124 -0
  6. data/bin/load_dev +22 -0
  7. data/bin/tng +888 -0
  8. data/binaries/tng.bundle +0 -0
  9. data/binaries/tng.so +0 -0
  10. data/lib/generators/tng/install_generator.rb +236 -0
  11. data/lib/tng/analyzers/controller.rb +114 -0
  12. data/lib/tng/analyzers/model.rb +131 -0
  13. data/lib/tng/analyzers/other.rb +277 -0
  14. data/lib/tng/analyzers/service.rb +150 -0
  15. data/lib/tng/api/http_client.rb +100 -0
  16. data/lib/tng/railtie.rb +11 -0
  17. data/lib/tng/services/direct_generation.rb +320 -0
  18. data/lib/tng/services/extract_methods.rb +39 -0
  19. data/lib/tng/services/test_generator.rb +287 -0
  20. data/lib/tng/services/testng.rb +100 -0
  21. data/lib/tng/services/user_app_config.rb +76 -0
  22. data/lib/tng/ui/about_display.rb +66 -0
  23. data/lib/tng/ui/authentication_warning_display.rb +172 -0
  24. data/lib/tng/ui/configuration_display.rb +52 -0
  25. data/lib/tng/ui/controller_test_flow_display.rb +79 -0
  26. data/lib/tng/ui/display_banner.rb +44 -0
  27. data/lib/tng/ui/goodbye_display.rb +41 -0
  28. data/lib/tng/ui/model_test_flow_display.rb +80 -0
  29. data/lib/tng/ui/other_test_flow_display.rb +78 -0
  30. data/lib/tng/ui/post_install_box.rb +80 -0
  31. data/lib/tng/ui/service_test_flow_display.rb +78 -0
  32. data/lib/tng/ui/show_help.rb +78 -0
  33. data/lib/tng/ui/system_status_display.rb +128 -0
  34. data/lib/tng/ui/theme.rb +258 -0
  35. data/lib/tng/ui/user_stats_display.rb +160 -0
  36. data/lib/tng/utils.rb +325 -0
  37. data/lib/tng/version.rb +5 -0
  38. data/lib/tng.rb +308 -0
  39. data/tng.gemspec +56 -0
  40. metadata +293 -0
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "tty-prompt"
4
+ require "tty-spinner"
5
+ require "pastel"
6
+ require "tty-screen"
7
+ require_relative "theme"
8
+
9
+ class ModelTestFlowDisplay
10
+ def initialize(prompt, pastel)
11
+ @prompt = prompt
12
+ @pastel = pastel
13
+ @terminal_width = begin
14
+ TTY::Screen.width
15
+ rescue StandardError
16
+ 80
17
+ end
18
+ end
19
+
20
+ def select_model(models)
21
+ header = @pastel.public_send(Tng::UI::Theme.color(:primary)).bold("#{Tng::UI::Theme.icon(:config)} Select model to test:")
22
+ puts Tng::UI::Theme.center_text(header, @terminal_width)
23
+
24
+ @prompt.select(
25
+ "",
26
+ cycle: true,
27
+ per_page: 12,
28
+ filter: true,
29
+ symbols: { marker: Tng::UI::Theme.icon(:marker) }
30
+ ) do |menu|
31
+ models.each do |model|
32
+ display_name = "#{model[:name]} #{@pastel.public_send(Tng::UI::Theme.color(:muted), "(#{model[:path]})")}"
33
+ menu.choice display_name, model
34
+ end
35
+ menu.choice @pastel.public_send(Tng::UI::Theme.color(:secondary), "#{Tng::UI::Theme.icon(:back)} Back"), :back
36
+ end
37
+ end
38
+
39
+ def show_no_models_message
40
+ puts
41
+ puts Tng::UI::Theme.center_text(
42
+ @pastel.public_send(Tng::UI::Theme.color(:warning),
43
+ "#{Tng::UI::Theme.icon(:warning)} No models found in app/models"), @terminal_width
44
+ )
45
+ puts Tng::UI::Theme.center_text(
46
+ @pastel.public_send(Tng::UI::Theme.color(:muted),
47
+ "Make sure you're in a Rails project with models"), @terminal_width
48
+ )
49
+ puts
50
+ end
51
+
52
+ def select_model_method(model, methods)
53
+ header = @pastel.public_send(Tng::UI::Theme.color(:primary)).bold("#{Tng::UI::Theme.icon(:rocket)} Select method to test in #{model[:name]}")
54
+ puts Tng::UI::Theme.center_text(header, @terminal_width)
55
+
56
+ @prompt.select(
57
+ "",
58
+ cycle: true,
59
+ per_page: 10,
60
+ filter: true,
61
+ symbols: { marker: Tng::UI::Theme.icon(:marker) }
62
+ ) do |menu|
63
+ methods.each do |method|
64
+ menu.choice method[:name], method
65
+ end
66
+ menu.choice @pastel.public_send(Tng::UI::Theme.color(:secondary), "#{Tng::UI::Theme.icon(:back)} Back"), :back
67
+ end
68
+ end
69
+
70
+ def show_no_methods_message(model)
71
+ error_msg = "#{@pastel.public_send(Tng::UI::Theme.color(:error)).bold("#{Tng::UI::Theme.icon(:error)} No methods found in #{model[:name]}")}\n#{@pastel.public_send(
72
+ Tng::UI::Theme.color(:muted), "Model may be empty or have syntax errors"
73
+ )}"
74
+ puts Tng::UI::Theme.center_text(error_msg, @terminal_width)
75
+ @prompt.keypress(Tng::UI::Theme.center_text(
76
+ @pastel.public_send(Tng::UI::Theme.color(:muted),
77
+ "Press any key to continue..."), @terminal_width
78
+ ))
79
+ end
80
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "tty-prompt"
4
+ require "tty-spinner"
5
+ require "pastel"
6
+ require "tty-screen"
7
+ require_relative "theme"
8
+
9
+ class OtherTestFlowDisplay
10
+ def initialize(prompt, pastel)
11
+ @prompt = prompt
12
+ @pastel = pastel
13
+ @terminal_width = begin
14
+ TTY::Screen.width
15
+ rescue StandardError
16
+ 80
17
+ end
18
+ end
19
+
20
+ def select_other_file(other_files)
21
+ header = @pastel.public_send(Tng::UI::Theme.color(:primary)).bold("#{Tng::UI::Theme.icon(:config)} Select file to test:")
22
+ puts Tng::UI::Theme.center_text(header, @terminal_width)
23
+
24
+ @prompt.select(
25
+ "",
26
+ cycle: true,
27
+ per_page: 12,
28
+ filter: true,
29
+ symbols: { marker: Tng::UI::Theme.icon(:marker) }
30
+ ) do |menu|
31
+ other_files.each do |file|
32
+ display_name = "#{file[:name]} #{@pastel.public_send(Tng::UI::Theme.color(:muted), "(#{file[:path]})")}"
33
+ menu.choice display_name, file
34
+ end
35
+ menu.choice @pastel.public_send(Tng::UI::Theme.color(:secondary), "#{Tng::UI::Theme.icon(:back)} Back"), :back
36
+ end
37
+ end
38
+
39
+ def show_no_other_files_message
40
+ error_msg = "#{@pastel.public_send(Tng::UI::Theme.color(:error)).bold("#{Tng::UI::Theme.icon(:error)} No other files found in your application")}\n#{@pastel.public_send(
41
+ Tng::UI::Theme.color(:muted), "Make sure you have files in supported directories (app/jobs, app/helpers, lib/, app/policies, app/presenters, app/mailers, app/graphql, etc.)"
42
+ )}"
43
+ puts Tng::UI::Theme.center_text(error_msg, @terminal_width)
44
+ @prompt.keypress(Tng::UI::Theme.center_text(
45
+ @pastel.public_send(Tng::UI::Theme.color(:muted),
46
+ "Press any key to continue..."), @terminal_width
47
+ ))
48
+ end
49
+
50
+ def select_other_method(other_file, methods)
51
+ header = @pastel.public_send(Tng::UI::Theme.color(:primary)).bold("#{Tng::UI::Theme.icon(:rocket)} Select method to test in #{other_file[:name]}")
52
+ puts Tng::UI::Theme.center_text(header, @terminal_width)
53
+
54
+ @prompt.select(
55
+ "",
56
+ cycle: true,
57
+ per_page: 10,
58
+ filter: true,
59
+ symbols: { marker: Tng::UI::Theme.icon(:marker) }
60
+ ) do |menu|
61
+ methods.each do |method|
62
+ menu.choice method[:name], method
63
+ end
64
+ menu.choice @pastel.public_send(Tng::UI::Theme.color(:secondary), "#{Tng::UI::Theme.icon(:back)} Back"), :back
65
+ end
66
+ end
67
+
68
+ def show_no_methods_message(other_file)
69
+ error_msg = "#{@pastel.public_send(Tng::UI::Theme.color(:error)).bold("#{Tng::UI::Theme.icon(:error)} No methods found in #{other_file[:name]}")}\n#{@pastel.public_send(
70
+ Tng::UI::Theme.color(:muted), "File may be empty or have syntax errors"
71
+ )}"
72
+ puts Tng::UI::Theme.center_text(error_msg, @terminal_width)
73
+ @prompt.keypress(Tng::UI::Theme.center_text(
74
+ @pastel.public_send(Tng::UI::Theme.color(:muted),
75
+ "Press any key to continue..."), @terminal_width
76
+ ))
77
+ end
78
+ end
@@ -0,0 +1,80 @@
1
+ require "tty-box"
2
+ require "pastel"
3
+ require_relative "theme"
4
+
5
+ class PostInstallBox
6
+ attr_reader :version, :pastel
7
+
8
+ def initialize(version)
9
+ @version = version
10
+ @pastel = Pastel.new
11
+ @terminal_width = begin
12
+ TTY::Screen.width
13
+ rescue StandardError
14
+ 80
15
+ end
16
+ end
17
+
18
+ def content
19
+ [
20
+ pastel.public_send(Tng::UI::Theme.color(:success)).bold("#{Tng::UI::Theme.icon(:success)} Tng installed successfully!"),
21
+ "",
22
+ pastel.public_send(Tng::UI::Theme.color(:warning)).bold("#{Tng::UI::Theme.icon(:config)} SETUP REQUIRED"),
23
+ "",
24
+ pastel.public_send(Tng::UI::Theme.color(:primary), "1. Generate configuration:"),
25
+ pastel.public_send(Tng::UI::Theme.color(:secondary), " rails g tng:install"),
26
+ "",
27
+ pastel.public_send(Tng::UI::Theme.color(:primary), "2. Edit configuration file:"),
28
+ pastel.public_send(Tng::UI::Theme.color(:secondary), " config/initializers/tng.rb"),
29
+ "",
30
+ pastel.public_send(Tng::UI::Theme.color(:primary), "3. Add your license key:"),
31
+ pastel.public_send(Tng::UI::Theme.color(:secondary), " config.api_key = 'your-license-key-here'"),
32
+ "",
33
+ pastel.public_send(Tng::UI::Theme.color(:primary),
34
+ "#{Tng::UI::Theme.icon(:config)} Check documentation for the correct authentication setup"),
35
+ "",
36
+ pastel.public_send(Tng::UI::Theme.color(:accent)).bold("#{Tng::UI::Theme.icon(:rocket)} Usage:"),
37
+ "",
38
+ pastel.public_send(Tng::UI::Theme.color(:primary), "Interactive mode:"),
39
+ pastel.public_send(Tng::UI::Theme.color(:success),
40
+ "#{Tng::UI::Theme.icon(:bullet)} bundle exec tng") + pastel.public_send(Tng::UI::Theme.color(:muted),
41
+ " - Interactive CLI with method selection"),
42
+ "",
43
+ pastel.public_send(Tng::UI::Theme.color(:primary), "Features:"),
44
+ pastel.public_send(Tng::UI::Theme.color(:success),
45
+ "#{Tng::UI::Theme.icon(:bullet)} Test 20+ file types: Controllers, Models, Services + Jobs, Helpers, Lib, Policies, Presenters, Mailers, GraphQL, and more"),
46
+ pastel.public_send(Tng::UI::Theme.color(:success),
47
+ "#{Tng::UI::Theme.icon(:bullet)} Select specific methods to test"),
48
+ pastel.public_send(Tng::UI::Theme.color(:success),
49
+ "#{Tng::UI::Theme.icon(:bullet)} Search and filter through methods"),
50
+ "",
51
+ pastel.public_send(Tng::UI::Theme.color(:primary), "Help:"),
52
+ pastel.public_send(Tng::UI::Theme.color(:success),
53
+ "#{Tng::UI::Theme.icon(:bullet)} bundle exec tng --help") + pastel.public_send(Tng::UI::Theme.color(:muted),
54
+ " - Show help information"),
55
+ "",
56
+ pastel.public_send(Tng::UI::Theme.color(:muted),
57
+ "#{Tng::UI::Theme.icon(:lightbulb)} Generate tests for individual methods with precision")
58
+ ].join("\n")
59
+ end
60
+
61
+ def render
62
+ # Use a reasonable width that won't cause issues
63
+ box_width = [@terminal_width, 70].min
64
+
65
+ style = Tng::UI::Theme.box_style(:default)
66
+ TTY::Box.frame(
67
+ title: { top_left: " TNG ", bottom_right: " v#{@version} " },
68
+ style: style[:style],
69
+ padding: style[:padding],
70
+ align: :left,
71
+ width: box_width
72
+ ) do
73
+ content
74
+ end
75
+ rescue StandardError
76
+ "TNG v#{@version} installed successfully!\n" +
77
+ "Run 'rails g tng:install' to get started.\n" +
78
+ "Use 'bundle exec tng --help' for usage information."
79
+ end
80
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "tty-prompt"
4
+ require "tty-spinner"
5
+ require "pastel"
6
+ require "tty-screen"
7
+ require_relative "theme"
8
+
9
+ class ServiceTestFlowDisplay
10
+ def initialize(prompt, pastel)
11
+ @prompt = prompt
12
+ @pastel = pastel
13
+ @terminal_width = begin
14
+ TTY::Screen.width
15
+ rescue StandardError
16
+ 80
17
+ end
18
+ end
19
+
20
+ def select_service(services)
21
+ header = @pastel.public_send(Tng::UI::Theme.color(:primary)).bold("#{Tng::UI::Theme.icon(:config)} Select service to test:")
22
+ puts Tng::UI::Theme.center_text(header, @terminal_width)
23
+
24
+ @prompt.select(
25
+ "",
26
+ cycle: true,
27
+ per_page: 12,
28
+ filter: true,
29
+ symbols: { marker: Tng::UI::Theme.icon(:marker) }
30
+ ) do |menu|
31
+ services.each do |service|
32
+ display_name = "#{service[:name]} #{@pastel.public_send(Tng::UI::Theme.color(:muted), "(#{service[:path]})")}"
33
+ menu.choice display_name, service
34
+ end
35
+ menu.choice @pastel.public_send(Tng::UI::Theme.color(:secondary), "#{Tng::UI::Theme.icon(:back)} Back"), :back
36
+ end
37
+ end
38
+
39
+ def show_no_services_message
40
+ error_msg = "#{@pastel.public_send(Tng::UI::Theme.color(:error)).bold("#{Tng::UI::Theme.icon(:error)} No services found in your application")}\n#{@pastel.public_send(
41
+ Tng::UI::Theme.color(:muted), "Make sure you have services in app/services/ or app/service/"
42
+ )}"
43
+ puts Tng::UI::Theme.center_text(error_msg, @terminal_width)
44
+ @prompt.keypress(Tng::UI::Theme.center_text(
45
+ @pastel.public_send(Tng::UI::Theme.color(:muted),
46
+ "Press any key to continue..."), @terminal_width
47
+ ))
48
+ end
49
+
50
+ def select_service_method(service, methods)
51
+ header = @pastel.public_send(Tng::UI::Theme.color(:primary)).bold("#{Tng::UI::Theme.icon(:rocket)} Select method to test for #{service[:name]}")
52
+ puts Tng::UI::Theme.center_text(header, @terminal_width)
53
+
54
+ @prompt.select(
55
+ "",
56
+ cycle: true,
57
+ per_page: 12,
58
+ filter: true,
59
+ symbols: { marker: Tng::UI::Theme.icon(:marker) }
60
+ ) do |menu|
61
+ methods.each do |method|
62
+ menu.choice "#{method[:name]}", method
63
+ end
64
+ menu.choice @pastel.public_send(Tng::UI::Theme.color(:secondary), "#{Tng::UI::Theme.icon(:back)} Back"), :back
65
+ end
66
+ end
67
+
68
+ def show_no_methods_message(service)
69
+ error_msg = "#{@pastel.public_send(Tng::UI::Theme.color(:error)).bold("#{Tng::UI::Theme.icon(:error)} No methods found in #{service[:name]}")}\n#{@pastel.public_send(
70
+ Tng::UI::Theme.color(:muted), "The service might not have any public methods or could not be analyzed."
71
+ )}"
72
+ puts Tng::UI::Theme.center_text(error_msg, @terminal_width)
73
+ @prompt.keypress(Tng::UI::Theme.center_text(
74
+ @pastel.public_send(Tng::UI::Theme.color(:muted),
75
+ "Press any key to continue..."), @terminal_width
76
+ ))
77
+ end
78
+ end
@@ -0,0 +1,78 @@
1
+ require "tty-box"
2
+ require "pastel"
3
+ require "tty-screen"
4
+ require_relative "theme"
5
+
6
+ class ShowHelp
7
+ attr_reader :pastel
8
+
9
+ def initialize(pastel, version)
10
+ @pastel = pastel
11
+ @version = version
12
+ @terminal_width = begin
13
+ TTY::Screen.width
14
+ rescue StandardError
15
+ 80
16
+ end
17
+ end
18
+
19
+ def content
20
+ [
21
+ @pastel.public_send(Tng::UI::Theme.color(:secondary)).bold("TNG - LLM-Powered Rails Test Generator"),
22
+ @pastel.public_send(Tng::UI::Theme.color(:muted), "Version: #{@version}"),
23
+ "",
24
+ @pastel.public_send(Tng::UI::Theme.color(:accent)).bold("Usage:"),
25
+ @pastel.public_send(Tng::UI::Theme.color(:primary),
26
+ " bundle exec tng") + @pastel.public_send(Tng::UI::Theme.color(:muted),
27
+ " # Interactive mode only"),
28
+ "",
29
+ @pastel.public_send(Tng::UI::Theme.color(:accent)).bold("Features:"),
30
+ @pastel.public_send(Tng::UI::Theme.color(:success),
31
+ " #{Tng::UI::Theme.icon(:bullet)} Controllers, Models, Services"),
32
+ @pastel.public_send(Tng::UI::Theme.color(:success),
33
+ " #{Tng::UI::Theme.icon(:bullet)} 17+ other file types (Jobs, Helpers, Lib, Policies, Presenters, Mailers, GraphQL, etc.)"),
34
+ @pastel.public_send(Tng::UI::Theme.color(:success),
35
+ " #{Tng::UI::Theme.icon(:bullet)} Per-method test generation"),
36
+ @pastel.public_send(Tng::UI::Theme.color(:success),
37
+ " #{Tng::UI::Theme.icon(:bullet)} Searchable method lists"),
38
+ "",
39
+ @pastel.public_send(Tng::UI::Theme.color(:accent)).bold("Options:"),
40
+ @pastel.public_send(Tng::UI::Theme.color(:primary),
41
+ " -h, --help") + @pastel.public_send(Tng::UI::Theme.color(:muted),
42
+ " Show this help message"),
43
+ "",
44
+ @pastel.public_send(Tng::UI::Theme.color(:accent)).bold("How to Use:"),
45
+ @pastel.public_send(Tng::UI::Theme.color(:muted),
46
+ " #{Tng::UI::Theme.icon(:bullet)} Run 'bundle exec tng' to start the interactive interface"),
47
+ @pastel.public_send(Tng::UI::Theme.color(:muted),
48
+ " #{Tng::UI::Theme.icon(:bullet)} Select Controller, Model, Service, or Other to test"),
49
+ @pastel.public_send(Tng::UI::Theme.color(:muted),
50
+ " #{Tng::UI::Theme.icon(:bullet)} Choose a specific method from the list"),
51
+ @pastel.public_send(Tng::UI::Theme.color(:muted),
52
+ " #{Tng::UI::Theme.icon(:bullet)} Use search/filter to find methods quickly"),
53
+ "",
54
+ @pastel.public_send(Tng::UI::Theme.color(:secondary)).bold("Happy testing! ") + @pastel.public_send(
55
+ Tng::UI::Theme.color(:accent), "★"
56
+ )
57
+ ].join("\n")
58
+ end
59
+
60
+ def render
61
+ # Use a reasonable width that matches other UI components
62
+ box_width = Tng::UI::Theme.calculate_box_width(@terminal_width)
63
+ box_width = [box_width, 80].min # Allow wider for help content
64
+ style = Tng::UI::Theme.box_style(:default)
65
+
66
+ box = TTY::Box.frame(
67
+ title: { top_left: " TNG Help ", bottom_right: " v#{@version} " },
68
+ style: style[:style],
69
+ padding: style[:padding],
70
+ align: :left,
71
+ width: box_width
72
+ ) do
73
+ content
74
+ end
75
+
76
+ Tng::UI::Theme.center_box(box, box_width, @terminal_width)
77
+ end
78
+ end
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pastel"
4
+ require_relative "theme"
5
+
6
+ class SystemStatusDisplay
7
+ def initialize(pastel, args)
8
+ @pastel = pastel
9
+ @args = args
10
+ end
11
+
12
+ def display(status)
13
+ case status[:status]
14
+ when :ok
15
+ unless @args[:type] && @args[:file]
16
+ puts @pastel.public_send(Tng::UI::Theme.color(:success),
17
+ "#{Tng::UI::Theme.icon(:success)} System operational")
18
+ end
19
+ true
20
+ when :version_mismatch
21
+ display_status_message(
22
+ title: "Version Mismatch",
23
+ icon: Tng::UI::Theme.icon(:error),
24
+ color: Tng::UI::Theme.color(:error),
25
+ content: [
26
+ "Your gem version: #{status[:gem_version]}",
27
+ "Required version: #{status[:current_version]}",
28
+ "",
29
+ "Quick fix:",
30
+ "• gem update tng",
31
+ "• bundle update tng"
32
+ ]
33
+ )
34
+ false
35
+ when :base_url_mismatch
36
+ display_status_message(
37
+ title: "URL Mismatch",
38
+ icon: Tng::UI::Theme.icon(:error),
39
+ color: Tng::UI::Theme.color(:error),
40
+ content: [
41
+ "Your URL: #{status[:user_base_url]}",
42
+ "Server URL: #{status[:server_base_url]}",
43
+ "",
44
+ "Quick fix:",
45
+ "• Edit config/initializers/tng.rb",
46
+ "• Set: config.base_url = '#{status[:server_base_url]}'"
47
+ ]
48
+ )
49
+ false
50
+ when :service_down
51
+ display_status_message(
52
+ title: "Service Unavailable",
53
+ icon: Tng::UI::Theme.icon(:error),
54
+ color: Tng::UI::Theme.color(:error),
55
+ content: [
56
+ status[:message] || "TNG service is currently down",
57
+ "",
58
+ "Please try again later or contact support"
59
+ ]
60
+ )
61
+ false
62
+ when :error
63
+ display_status_message(
64
+ title: "Connection Error",
65
+ icon: Tng::UI::Theme.icon(:error),
66
+ color: Tng::UI::Theme.color(:error),
67
+ content: [
68
+ status[:message] || "Unable to connect to TNG service",
69
+ "",
70
+ "Please check your internet connection and try again"
71
+ ]
72
+ )
73
+ false
74
+ else
75
+ display_status_message(
76
+ title: "Unknown Error",
77
+ icon: Tng::UI::Theme.icon(:error),
78
+ color: Tng::UI::Theme.color(:error),
79
+ content: [status[:message] || "An unknown error occurred"]
80
+ )
81
+ false
82
+ end
83
+ end
84
+
85
+ private
86
+
87
+ def display_status_message(title:, icon:, color:, content:)
88
+ require "tty-box"
89
+ require "tty-screen"
90
+
91
+ terminal_width = begin
92
+ TTY::Screen.width
93
+ rescue StandardError
94
+ 80
95
+ end
96
+
97
+ formatted_content = content.map do |line|
98
+ case line
99
+ when /^•/
100
+ @pastel.public_send(Tng::UI::Theme.color(:muted), line)
101
+ when /^Quick fix:/
102
+ @pastel.public_send(Tng::UI::Theme.color(:secondary), line)
103
+ when ""
104
+ line
105
+ else
106
+ @pastel.public_send(Tng::UI::Theme.color(:warning), line)
107
+ end
108
+ end.join("\n")
109
+
110
+ box_width = Tng::UI::Theme.calculate_box_width(terminal_width)
111
+
112
+ status_box = TTY::Box.frame(
113
+ title: { top_left: " #{title} " },
114
+ style: {
115
+ fg: :bright_white,
116
+ border: { fg: color },
117
+ title: { fg: color == Tng::UI::Theme.color(:error) ? :bright_red : color }
118
+ },
119
+ padding: [1, 2],
120
+ width: box_width
121
+ ) do
122
+ formatted_content
123
+ end
124
+
125
+ puts Tng::UI::Theme.center_box(status_box, box_width, terminal_width)
126
+ puts
127
+ end
128
+ end