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.
- checksums.yaml +7 -0
- data/Gemfile +15 -0
- data/LICENSE.md +32 -0
- data/README.md +413 -0
- data/Rakefile +124 -0
- data/bin/load_dev +22 -0
- data/bin/tng +888 -0
- data/binaries/tng.bundle +0 -0
- data/binaries/tng.so +0 -0
- data/lib/generators/tng/install_generator.rb +236 -0
- data/lib/tng/analyzers/controller.rb +114 -0
- data/lib/tng/analyzers/model.rb +131 -0
- data/lib/tng/analyzers/other.rb +277 -0
- data/lib/tng/analyzers/service.rb +150 -0
- data/lib/tng/api/http_client.rb +100 -0
- data/lib/tng/railtie.rb +11 -0
- data/lib/tng/services/direct_generation.rb +320 -0
- data/lib/tng/services/extract_methods.rb +39 -0
- data/lib/tng/services/test_generator.rb +287 -0
- data/lib/tng/services/testng.rb +100 -0
- data/lib/tng/services/user_app_config.rb +76 -0
- data/lib/tng/ui/about_display.rb +66 -0
- data/lib/tng/ui/authentication_warning_display.rb +172 -0
- data/lib/tng/ui/configuration_display.rb +52 -0
- data/lib/tng/ui/controller_test_flow_display.rb +79 -0
- data/lib/tng/ui/display_banner.rb +44 -0
- data/lib/tng/ui/goodbye_display.rb +41 -0
- data/lib/tng/ui/model_test_flow_display.rb +80 -0
- data/lib/tng/ui/other_test_flow_display.rb +78 -0
- data/lib/tng/ui/post_install_box.rb +80 -0
- data/lib/tng/ui/service_test_flow_display.rb +78 -0
- data/lib/tng/ui/show_help.rb +78 -0
- data/lib/tng/ui/system_status_display.rb +128 -0
- data/lib/tng/ui/theme.rb +258 -0
- data/lib/tng/ui/user_stats_display.rb +160 -0
- data/lib/tng/utils.rb +325 -0
- data/lib/tng/version.rb +5 -0
- data/lib/tng.rb +308 -0
- data/tng.gemspec +56 -0
- 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
|