Class: Clacky::RichUI::ApprovalDialog

Inherits:
Object
  • Object
show all
Includes:
Components::BaseComponent
Defined in:
lib/clacky/rich_ui/components/dialogs/approval_dialog.rb

Constant Summary collapse

RISK_LEVELS =
{
  low:      { label: "Low",      color: :green,  bar: "●○○○" },
  medium:   { label: "Medium",   color: :yellow, bar: "●●○○" },
  high:     { label: "High",     color: :yellow, bar: "●●●○" },
  critical: { label: "Critical", color: :red,    bar: "●●●●" }
}.freeze
CATEGORY_COLORS =
{
  file: :blue, shell: :yellow, network: :cyan, paid: :magenta
}.freeze
CHOICES =
[
  { key: :approve,       label: "Approve",       color: :green  },
  { key: :deny,          label: "Deny",           color: :red    },
  { key: :always_allow,  label: "Always allow",   color: :cyan   }
].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Components::BaseComponent

#colored, #muted, #status_marker, #theme, #truncate

Constructor Details

#initialize(tool_name:, message:, params: {}, risk: :medium, category: :file) ⇒ ApprovalDialog

Returns a new instance of ApprovalDialog.



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/clacky/rich_ui/components/dialogs/approval_dialog.rb', line 30

def initialize(tool_name:, message:, params: {}, risk: :medium, category: :file)
  @tool_name = tool_name
  @message = message
  @params = params
  @risk = RISK_LEVELS[risk] || RISK_LEVELS[:medium]
  @category = category
  @category_color = CATEGORY_COLORS[category] || :blue
  @selected_index = 0
  @width = 72
  @height = [params.length + 10, 12].max
  @event_listeners = {}
  @mutex = Mutex.new
  @condition = ConditionVariable.new
  @finished = false
  @result = nil
  @panel = RubyRich::Panel.new("", title: "Approval", border_style: @risk[:color], title_align: :center)
  @layout = RubyRich::Layout.new(name: :approval_dialog, width: @width, height: @height)
  @layout.update_content(@panel)
  @layout.calculate_dimensions(@width, @height)
  wire_keys
end

Instance Attribute Details

#heightObject

Returns the value of attribute height.



28
29
30
# File 'lib/clacky/rich_ui/components/dialogs/approval_dialog.rb', line 28

def height
  @height
end

#widthObject

Returns the value of attribute width.



28
29
30
# File 'lib/clacky/rich_ui/components/dialogs/approval_dialog.rb', line 28

def width
  @width
end

Instance Method Details

#category_badgeObject



136
137
138
139
# File 'lib/clacky/rich_ui/components/dialogs/approval_dialog.rb', line 136

def category_badge
  label = @category.to_s.capitalize
  colored("[#{label}]", @category_color)
end

#finish(value) ⇒ Object



52
53
54
55
56
57
58
59
# File 'lib/clacky/rich_ui/components/dialogs/approval_dialog.rb', line 52

def finish(value)
  @mutex.synchronize do
    @result = value
    @finished = true
    @condition.signal
  end
  true
end

#key(event_name, priority = 0, &block) ⇒ Object



66
67
68
69
70
# File 'lib/clacky/rich_ui/components/dialogs/approval_dialog.rb', line 66

def key(event_name, priority = 0, &block)
  @event_listeners[event_name] ||= []
  @event_listeners[event_name] << { priority: priority, block: block }
  @event_listeners[event_name].sort_by! { |l| -l[:priority] }
end

#move_selection(delta) ⇒ Object



100
101
102
# File 'lib/clacky/rich_ui/components/dialogs/approval_dialog.rb', line 100

def move_selection(delta)
  @selected_index = (@selected_index + delta) % CHOICES.length
end

#notify_listeners(event_data) ⇒ Object



72
73
74
# File 'lib/clacky/rich_ui/components/dialogs/approval_dialog.rb', line 72

def notify_listeners(event_data)
  Array(@event_listeners[event_data[:name]]).each { |l| l[:block].call(event_data, nil) }
end

#render_choicesObject



127
128
129
130
131
132
133
134
# File 'lib/clacky/rich_ui/components/dialogs/approval_dialog.rb', line 127

def render_choices
  CHOICES.each_with_index.map do |choice, i|
    selected = i == @selected_index
    prefix = selected ? "#{RubyRich::AnsiCode.color(:cyan, true)}#{RubyRich::AnsiCode.reset}" : " "
    label = selected ? colored(choice[:label], choice[:color]) : muted(choice[:label])
    "#{prefix} [#{label}]"
  end.join("  ")
end

#render_contentObject



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/clacky/rich_ui/components/dialogs/approval_dialog.rb', line 104

def render_content
  risk = @risk
  lines = []
  lines << ""
  lines << "  #{colored("Tool:",  :body)}  #{colored(@tool_name, :accent)}  #{category_badge}"
  lines << "  #{colored("Risk:",  :body)}  #{colored(risk[:label], risk[:color])} #{colored(risk[:bar], risk[:color])}"
  lines << "  #{colored("Info:",  :body)}  #{colored(@message, :body)}"

  unless @params.empty?
    lines << ""
    @params.each do |key, value|
      val = value.to_s
      val = "#{val[0..50]}..." if val.length > 54
      lines << "  #{muted("#{key}:")}  #{colored(val, :body)}"
    end
  end

  lines << ""
  lines << render_choices
  lines << ""
  lines.join("\n")
end

#render_to_bufferObject



76
77
78
79
80
# File 'lib/clacky/rich_ui/components/dialogs/approval_dialog.rb', line 76

def render_to_buffer
  @panel.content = render_content
  @layout.calculate_dimensions(@width, @height)
  @layout.render_to_buffer
end

#waitObject



61
62
63
64
# File 'lib/clacky/rich_ui/components/dialogs/approval_dialog.rb', line 61

def wait
  @mutex.synchronize { @condition.wait(@mutex) until @finished }
  @result
end