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
14 changes: 14 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,17 @@ gem "rspec", "~> 3.0"
gem "simplecov", "~> 0.22", require: false

gem "standard", "~> 1.3"

# Performance and concurrency testing
gem "rspec-benchmark", "~> 0.6"
gem "memory_profiler", "~> 1.0"
gem "concurrent-ruby", "~> 1.2"
gem "timeout", "~> 0.4"

# Advanced UI components (optional dependencies)
gem "tty-prompt", "~> 0.23", require: false
gem "tty-spinner", "~> 0.9", require: false
gem "tty-progressbar", "~> 0.18", require: false
gem "tty-cursor", "~> 0.7", require: false
gem "tty-screen", "~> 0.8", require: false
gem "pastel", "~> 0.8", require: false
215 changes: 215 additions & 0 deletions examples/advanced_input_demo.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

require "bundler/setup"
require "thor/interactive"
require_relative "../lib/thor/interactive/ui/components/advanced_input"

class AdvancedInputDemo < Thor
include Thor::Interactive::Command

desc "test", "Test the advanced multi-line input"
def test
puts "\n=== Advanced Multi-line Input Test ==="
puts "\nKey bindings:"
puts " Alt+Enter or Ctrl+J : Insert new line"
puts " Enter : Submit (smart detection)"
puts " Escape or Ctrl+C : Cancel"
puts " Tab : Indent"
puts " Ctrl+K : Kill rest of line"
puts " Ctrl+U : Clear line"
puts " Ctrl+W : Delete word"
puts "\nSmart newline detection:"
puts " - Unclosed brackets/quotes → auto-continue"
puts " - Keywords (def, class, if) → auto-continue"
puts " - Indented lines → auto-continue"
puts "\nTry it out:\n"

input = Thor::Interactive::UI::Components::AdvancedInput.new(
prompt: "input> ",
continuation: " ... ",
show_line_numbers: true,
syntax_highlighting: true,
smart_newline: true,
auto_indent: true
)

result = input.read_multiline

if result
puts "\n\nYou entered:"
puts "-" * 40
puts result
puts "-" * 40
puts "\nLines: #{result.lines.count}"
puts "Characters: #{result.length}"
else
puts "\nInput cancelled"
end
end

desc "compare", "Compare input methods"
def compare
puts "\n=== Input Method Comparison ===\n"

# Method 1: Standard Reline
puts "1. Standard Reline (current):"
puts " - Use \\ for line continuation"
puts " - Press Enter twice to submit"
print "\nreline> "
reline_input = $stdin.gets
puts " Result: #{reline_input.inspect}"

# Method 2: Our advanced input
puts "\n2. Advanced Input (new):"
puts " - Alt+Enter or Ctrl+J for new line"
puts " - Smart detection for multi-line"
puts " - Auto-indent and syntax highlighting"

input = Thor::Interactive::UI::Components::AdvancedInput.new(
prompt: "\nadv> ",
show_line_numbers: false,
syntax_highlighting: true
)

advanced_input = input.read_multiline
puts " Result: #{advanced_input.inspect}"

puts "\n=== Comparison Complete ==="
end

desc "code", "Enter code with smart multi-line"
def code
puts "\n=== Code Entry Mode ==="
puts "Enter will automatically continue for unclosed blocks:\n"

input = Thor::Interactive::UI::Components::AdvancedInput.new(
prompt: "code> ",
continuation: " .. ",
show_line_numbers: true,
syntax_highlighting: true,
smart_newline: true,
auto_indent: true
)

while true
result = input.read_multiline

break unless result

puts "\nEvaluating:"
puts result

begin
# Try to evaluate as Ruby code
eval_result = eval(result)
puts "=> #{eval_result.inspect}"
rescue SyntaxError => e
puts "Syntax Error: #{e.message}"
rescue => e
puts "Error: #{e.message}"
end

puts
end

puts "\nCode mode exited"
end

desc "json", "JSON entry with auto-formatting"
def json
require 'json'

puts "\n=== JSON Entry Mode ==="
puts "Brackets will auto-continue, Enter submits when balanced:\n"

input = Thor::Interactive::UI::Components::AdvancedInput.new(
prompt: "json> ",
continuation: " ",
show_line_numbers: false,
smart_newline: true,
auto_indent: true
)

result = input.read_multiline

if result
begin
parsed = JSON.parse(result)
puts "\nParsed successfully!"
puts JSON.pretty_generate(parsed)
rescue JSON::ParserError => e
puts "\nJSON Parse Error: #{e.message}"
puts "\nRaw input:"
puts result
end
else
puts "\nCancelled"
end
end

desc "poem", "Write a poem with easy line breaks"
def poem
puts "\n=== Poetry Mode ==="
puts "Use Alt+Enter for line breaks, Enter when done:\n"

input = Thor::Interactive::UI::Components::AdvancedInput.new(
prompt: "poem> ",
continuation: " ",
smart_newline: false, # Disable smart detection for poetry
auto_indent: false
)

result = input.read_multiline

if result
puts "\n" + "="*50
puts result
puts "="*50

lines = result.lines
words = result.split.size
puts "\nPoem statistics:"
puts " Lines: #{lines.count}"
puts " Words: #{words}"
puts " Characters: #{result.length}"
else
puts "\nNo poem today?"
end
end

desc "demo", "Interactive demo of all features"
def demo
loop do
puts "\n=== Advanced Input Demo Menu ==="
puts "1. Test multi-line input"
puts "2. Compare input methods"
puts "3. Code entry mode"
puts "4. JSON entry mode"
puts "5. Poetry mode"
puts "6. Exit"

print "\nChoice: "
choice = $stdin.gets.chomp

case choice
when "1" then test
when "2" then compare
when "3" then code
when "4" then json
when "5" then poem
when "6" then break
else
puts "Invalid choice"
end
end

puts "\nGoodbye!"
end

default_task :demo
end

if __FILE__ == $0
AdvancedInputDemo.start(ARGV)
end
161 changes: 161 additions & 0 deletions examples/advanced_ui_app.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

require "bundler/setup"
require "thor/interactive"

class AdvancedUIApp < Thor
include Thor::Interactive::Command

# Configure with advanced UI mode
configure_interactive(
ui_mode: :advanced,
animations: true,
status_bar: true,
prompt: "ui-demo> ",
default_handler: proc do |input, thor_instance|
puts "Natural language: #{input}"
end
)

desc "process FILES", "Process files with progress bar"
def process(*files)
if files.empty?
puts "No files specified. Using example files..."
files = %w[file1.txt file2.txt file3.txt file4.txt file5.txt]
end

with_progress(total: files.size, title: "Processing files") do |progress|
files.each do |file|
update_status("Processing: #{file}")

# Simulate processing
sleep 0.5

puts " ✓ Processed: #{file}"
progress.advance if progress
end
end

update_status("Complete!")
puts "\nAll files processed successfully!"
end

desc "analyze TEXT", "Analyze text with spinner"
def analyze(text = nil)
text ||= "sample text for analysis"

result = with_spinner("Analyzing '#{text}'...") do |spinner|
# Simulate stages of analysis
sleep 0.5
spinner.update(title: "Tokenizing...") if spinner&.respond_to?(:update)
sleep 0.5

spinner.update(title: "Running NLP...") if spinner&.respond_to?(:update)
sleep 0.5

spinner.update(title: "Generating insights...") if spinner&.respond_to?(:update)
sleep 0.5

# Return analysis result
{
words: text.split.size,
chars: text.length,
sentiment: %w[positive neutral negative].sample
}
end

puts "\nAnalysis Results:"
puts " Words: #{result[:words]}"
puts " Characters: #{result[:chars]}"
puts " Sentiment: #{result[:sentiment]}"
end

desc "download URL", "Download with progress animation"
def download(url = "https://example.com/file.zip")
puts "Downloading from: #{url}"

with_spinner("Connecting...") do |spinner|
sleep 0.5
spinner.update(title: "Downloading...") if spinner&.respond_to?(:update)

# Simulate download progress
10.times do |i|
spinner.update(title: "Downloading... #{(i + 1) * 10}%") if spinner&.respond_to?(:update)
sleep 0.2
end
end

puts "✓ Download complete!"
end

desc "menu", "Interactive menu demonstration"
def menu
if interactive_ui?
choices = {
"Process files" => -> { invoke :process },
"Analyze text" => -> { invoke :analyze },
"Download file" => -> { invoke :download },
"Exit" => -> { puts "Goodbye!" }
}

# Try to use TTY::Prompt if available
begin
require 'tty-prompt'
prompt = TTY::Prompt.new
choice = prompt.select("Choose an action:", choices.keys)
choices[choice].call
rescue LoadError
puts "Menu options:"
choices.keys.each_with_index do |opt, i|
puts " #{i + 1}. #{opt}"
end
print "Choose (1-#{choices.size}): "
choice_idx = gets.to_i - 1
if choice_idx >= 0 && choice_idx < choices.size
choices.values[choice_idx].call
end
end
else
puts "Interactive UI not enabled. Run in interactive mode for menu."
end
end

desc "demo", "Run all UI demonstrations"
def demo
puts "=" * 50
puts "Thor Interactive Advanced UI Demo"
puts "=" * 50
puts

puts "1. Spinner Animation Demo:"
puts "-" * 30
invoke :analyze, ["Advanced UI features are working great!"]
puts

puts "2. Progress Bar Demo:"
puts "-" * 30
invoke :process, %w[doc1.pdf doc2.pdf doc3.pdf]
puts

puts "3. Download Animation Demo:"
puts "-" * 30
invoke :download
puts

puts "=" * 50
puts "Demo complete!"
end
end

if __FILE__ == $0
# Start in interactive mode if no arguments
if ARGV.empty?
puts "Starting Advanced UI Demo in interactive mode..."
puts "Try commands like: /demo, /analyze hello world, /process"
puts
AdvancedUIApp.start(["interactive"])
else
AdvancedUIApp.start(ARGV)
end
end
Loading
Loading