Class: Clacky::UI2::Components::CommandSuggestions

Inherits:
Object
  • Object
show all
Defined in:
lib/clacky/ui2/components/command_suggestions.rb

Overview

CommandSuggestions displays a dropdown menu of available commands Supports keyboard navigation and filtering

Constant Summary collapse

SYSTEM_COMMANDS =

System commands available by default

[
  { command: "/clear", description: "Clear chat history and restart session" },
  { command: "/config", description: "Open configuration (models, API keys, settings)" },
  { command: "/model", description: "Quickly switch the current model" },
  { command: "/goal", description: "Set a standing goal for autonomous multi-turn work" },
  { command: "/undo", description: "Undo the last task and restore previous state" },
  { command: "/help", description: "Show help information" },
  { command: "/exit", description: "Exit the chat session" },
  { command: "/quit", description: "Quit the application" }
].freeze
GOAL_SUBCOMMANDS =
[
  { command: "/goal status", description: "Show current goal and turn budget" },
  { command: "/goal pause", description: "Pause the goal loop (keeps progress)" },
  { command: "/goal resume", description: "Resume a paused goal" },
  { command: "/goal clear", description: "Clear the goal entirely" },
  { command: "/goal --turns", description: "Set goal with custom turn budget (default 20)", argument_hint: "N <goal text>" }
].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeCommandSuggestions

Returns a new instance of CommandSuggestions.



34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/clacky/ui2/components/command_suggestions.rb', line 34

def initialize
  @pastel = Pastel.new
  @commands = []
  @filtered_commands = []
  @selected_index = 0
  @visible = false
  @filter_text = ""
  @skill_commands = []
  
  # Initialize with system commands
  update_commands
end

Instance Attribute Details

#selected_indexObject (readonly)

Returns the value of attribute selected_index.



12
13
14
# File 'lib/clacky/ui2/components/command_suggestions.rb', line 12

def selected_index
  @selected_index
end

#visibleObject (readonly)

Returns the value of attribute visible.



12
13
14
# File 'lib/clacky/ui2/components/command_suggestions.rb', line 12

def visible
  @visible
end

Instance Method Details

#clear_from_screen(row:, col:) ⇒ Object

Clear the rendered dropdown from screen

Parameters:

  • row (Integer)

    Starting row position

  • col (Integer)

    Starting column position



188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/clacky/ui2/components/command_suggestions.rb', line 188

def clear_from_screen(row:, col:)
  return unless @visible

  height = required_height
  output = []
  
  height.times do |i|
    output << position_cursor(row + i, col) + clear_line
  end
  
  print output.join
  flush
end

#has_suggestions?Boolean

Check if there are any suggestions to show

Returns:

  • (Boolean)


133
134
135
# File 'lib/clacky/ui2/components/command_suggestions.rb', line 133

def has_suggestions?
  @visible && !@filtered_commands.empty?
end

#hideObject

Hide the suggestions dropdown



83
84
85
86
87
88
# File 'lib/clacky/ui2/components/command_suggestions.rb', line 83

def hide
  @visible = false
  @filter_text = ""
  @filtered_commands = []
  @selected_index = 0
end

#load_skill_commands(skill_loader, agent_profile = nil) ⇒ Object

Load skill commands from skill loader, filtered by agent profile whitelist

Parameters:



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/clacky/ui2/components/command_suggestions.rb', line 55

def load_skill_commands(skill_loader, agent_profile = nil)
  return unless skill_loader

  skills = skill_loader.user_invocable_skills
  skills = skills.select { |s| s.allowed_for_agent?(agent_profile.name) } if agent_profile

  @skill_commands = skills.map do |skill|
    {
      command: skill.slash_command,
      description: skill.description || "No description available",
      type: :skill,
      argument_hint: skill.argument_hint
    }
  end

  update_commands
end

#render(row:, col:, width: 60) ⇒ String

Render the suggestions dropdown

Parameters:

  • row (Integer)

    Starting row position

  • col (Integer)

    Starting column position

  • width (Integer) (defaults to: 60)

    Maximum width for the dropdown

Returns:

  • (String)

    Rendered output



152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/clacky/ui2/components/command_suggestions.rb', line 152

def render(row:, col:, width: 60)
  return "" unless @visible
  return "" if @filtered_commands.empty?

  output = []
  max_items = 5  # Maximum visible items

  # Sliding window: keep selected item visible
  start_idx = [@selected_index - max_items + 1, 0].max
  start_idx = [start_idx, [@filtered_commands.size - max_items, 0].max].min
  visible_commands = @filtered_commands[start_idx, max_items] || []

  # Header
  header = @pastel.dim("┌─ Commands ") + @pastel.dim("" * (width - 13)) + @pastel.dim("")
  output << position_cursor(row, col) + header

  # Items
  visible_commands.each_with_index do |cmd, idx|
    is_selected = (start_idx + idx == @selected_index)
    line = render_command_item(cmd, is_selected, width)
    output << position_cursor(row + 1 + idx, col) + line
  end

  # Footer with navigation hint
  footer_row = row + 1 + visible_commands.size
  total = @filtered_commands.size
  hint = total > max_items ? " (#{total - max_items} more...)" : ""
  footer = @pastel.dim("") + @pastel.dim("" * (width - 2)) + @pastel.dim("")
  output << position_cursor(footer_row, col) + footer

  output.join
end

#required_heightInteger

Calculate required height for rendering

Returns:

  • (Integer)

    Number of lines needed



139
140
141
142
143
144
145
# File 'lib/clacky/ui2/components/command_suggestions.rb', line 139

def required_height
  return 0 unless @visible
  return 0 if @filtered_commands.empty?

  # Header + commands + footer
  1 + [@filtered_commands.size, 5].min + 1  # Max 5 visible items
end

#select_nextObject

Move selection down



105
106
107
108
# File 'lib/clacky/ui2/components/command_suggestions.rb', line 105

def select_next
  return if @filtered_commands.empty?
  @selected_index = (@selected_index + 1) % @filtered_commands.size
end

#select_previousObject

Move selection up



99
100
101
102
# File 'lib/clacky/ui2/components/command_suggestions.rb', line 99

def select_previous
  return if @filtered_commands.empty?
  @selected_index = (@selected_index - 1) % @filtered_commands.size
end

#selected_argument_hintString?

Get the argument hint for the currently selected command

Returns:

  • (String, nil)

    Argument hint string or nil if none



126
127
128
129
# File 'lib/clacky/ui2/components/command_suggestions.rb', line 126

def selected_argument_hint
  cmd = selected_command
  cmd ? cmd[:argument_hint] : nil
end

#selected_commandHash?

Get the currently selected command

Returns:

  • (Hash, nil)

    Selected command hash or nil if none selected



112
113
114
115
# File 'lib/clacky/ui2/components/command_suggestions.rb', line 112

def selected_command
  return nil if @filtered_commands.empty?
  @filtered_commands[@selected_index]
end

#selected_command_textString?

Get the currently selected command text

Returns:

  • (String, nil)

    Selected command text or nil if none selected



119
120
121
122
# File 'lib/clacky/ui2/components/command_suggestions.rb', line 119

def selected_command_text
  cmd = selected_command
  cmd ? cmd[:command] : nil
end

#show(filter_text = "") ⇒ Object

Show the suggestions dropdown

Parameters:

  • filter_text (String) (defaults to: "")

    Initial filter text (everything after the /)



75
76
77
78
79
80
# File 'lib/clacky/ui2/components/command_suggestions.rb', line 75

def show(filter_text = "")
  @filter_text = filter_text
  @visible = true
  update_filtered_commands
  @selected_index = 0
end

#themeObject

Get current theme from ThemeManager



48
49
50
# File 'lib/clacky/ui2/components/command_suggestions.rb', line 48

def theme
  UI2::ThemeManager.current_theme
end

#update_filter(text) ⇒ Object

Update filter text and refresh filtered commands

Parameters:

  • text (String)

    Filter text (everything after the /)



92
93
94
95
96
# File 'lib/clacky/ui2/components/command_suggestions.rb', line 92

def update_filter(text)
  @filter_text = text
  update_filtered_commands
  @selected_index = 0  # Reset selection when filter changes
end