openclacky 0.5.6 → 0.6.0

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +43 -0
  3. data/docs/ui2-architecture.md +124 -0
  4. data/lib/clacky/agent.rb +245 -340
  5. data/lib/clacky/agent_config.rb +1 -7
  6. data/lib/clacky/cli.rb +156 -397
  7. data/lib/clacky/client.rb +68 -36
  8. data/lib/clacky/gitignore_parser.rb +26 -12
  9. data/lib/clacky/model_pricing.rb +6 -2
  10. data/lib/clacky/session_manager.rb +6 -2
  11. data/lib/clacky/tools/glob.rb +65 -9
  12. data/lib/clacky/tools/grep.rb +4 -120
  13. data/lib/clacky/tools/run_project.rb +5 -0
  14. data/lib/clacky/tools/safe_shell.rb +49 -13
  15. data/lib/clacky/tools/shell.rb +1 -49
  16. data/lib/clacky/tools/web_fetch.rb +2 -2
  17. data/lib/clacky/tools/web_search.rb +38 -26
  18. data/lib/clacky/ui2/README.md +214 -0
  19. data/lib/clacky/ui2/components/base_component.rb +163 -0
  20. data/lib/clacky/ui2/components/common_component.rb +89 -0
  21. data/lib/clacky/ui2/components/inline_input.rb +187 -0
  22. data/lib/clacky/ui2/components/input_area.rb +1029 -0
  23. data/lib/clacky/ui2/components/message_component.rb +76 -0
  24. data/lib/clacky/ui2/components/output_area.rb +112 -0
  25. data/lib/clacky/ui2/components/todo_area.rb +137 -0
  26. data/lib/clacky/ui2/components/tool_component.rb +106 -0
  27. data/lib/clacky/ui2/components/welcome_banner.rb +93 -0
  28. data/lib/clacky/ui2/layout_manager.rb +331 -0
  29. data/lib/clacky/ui2/line_editor.rb +201 -0
  30. data/lib/clacky/ui2/screen_buffer.rb +238 -0
  31. data/lib/clacky/ui2/theme_manager.rb +68 -0
  32. data/lib/clacky/ui2/themes/base_theme.rb +99 -0
  33. data/lib/clacky/ui2/themes/hacker_theme.rb +56 -0
  34. data/lib/clacky/ui2/themes/minimal_theme.rb +50 -0
  35. data/lib/clacky/ui2/ui_controller.rb +720 -0
  36. data/lib/clacky/ui2/view_renderer.rb +160 -0
  37. data/lib/clacky/ui2.rb +37 -0
  38. data/lib/clacky/utils/file_ignore_helper.rb +126 -0
  39. data/lib/clacky/version.rb +1 -1
  40. data/lib/clacky.rb +1 -6
  41. metadata +38 -6
  42. data/lib/clacky/ui/banner.rb +0 -155
  43. data/lib/clacky/ui/enhanced_prompt.rb +0 -786
  44. data/lib/clacky/ui/formatter.rb +0 -209
  45. data/lib/clacky/ui/statusbar.rb +0 -96
@@ -0,0 +1,187 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../line_editor"
4
+
5
+ module Clacky
6
+ module UI2
7
+ module Components
8
+ # InlineInput provides inline input for confirmations and simple prompts
9
+ # Renders at the end of output area, not at fixed bottom position
10
+ class InlineInput
11
+ include LineEditor
12
+
13
+ attr_reader :prompt, :default_value
14
+
15
+ def initialize(prompt: "", default: nil)
16
+ initialize_line_editor
17
+ @prompt = prompt
18
+ @default_value = default
19
+ @active = false
20
+ @result_queue = nil
21
+ @paste_counter = 0
22
+ @paste_placeholders = {}
23
+ end
24
+
25
+ # Activate inline input and wait for user input
26
+ # @return [String] User input
27
+ def collect
28
+ @active = true
29
+ @result_queue = Queue.new
30
+ # Don't set default as initial text - start empty
31
+ @result_queue.pop
32
+ end
33
+
34
+ # Check if active
35
+ def active?
36
+ @active
37
+ end
38
+
39
+ # Handle keyboard input
40
+ # @param key [Symbol, String] Key input
41
+ # @return [Hash] Result with action
42
+ def handle_key(key)
43
+ return { action: nil } unless @active
44
+
45
+ case key
46
+ when Hash
47
+ if key[:type] == :rapid_input
48
+ # Handle multi-line paste with placeholder
49
+ pasted_text = key[:text]
50
+ pasted_lines = pasted_text.split(/\r\n|\r|\n/)
51
+
52
+ if pasted_lines.size > 1
53
+ # Multi-line paste - use placeholder
54
+ @paste_counter += 1
55
+ placeholder = "[##{@paste_counter} Paste Text]"
56
+ @paste_placeholders[placeholder] = pasted_text
57
+ insert_text(placeholder)
58
+ else
59
+ # Single line - insert directly
60
+ insert_text(pasted_text)
61
+ end
62
+ end
63
+ { action: :update }
64
+ when :enter
65
+ handle_enter
66
+ when :backspace
67
+ backspace
68
+ { action: :update }
69
+ when :delete
70
+ delete_char
71
+ { action: :update }
72
+ when :left_arrow, :ctrl_b
73
+ cursor_left
74
+ { action: :update }
75
+ when :right_arrow, :ctrl_f
76
+ cursor_right
77
+ { action: :update }
78
+ when :home, :ctrl_a
79
+ cursor_home
80
+ { action: :update }
81
+ when :end, :ctrl_e
82
+ cursor_end
83
+ { action: :update }
84
+ when :ctrl_k
85
+ kill_to_end
86
+ { action: :update }
87
+ when :ctrl_u
88
+ kill_to_start
89
+ { action: :update }
90
+ when :ctrl_w
91
+ kill_word
92
+ { action: :update }
93
+ when :shift_tab
94
+ handle_shift_tab
95
+ when :ctrl_c
96
+ handle_cancel
97
+ when :escape
98
+ handle_cancel
99
+ else
100
+ if key.is_a?(String) && key.length >= 1 && key.ord >= 32
101
+ insert_char(key)
102
+ { action: :update }
103
+ else
104
+ { action: nil }
105
+ end
106
+ end
107
+ end
108
+
109
+ # Render inline input with prompt and cursor
110
+ # @return [String] Rendered line (may wrap to multiple lines)
111
+ def render
112
+ line = render_line_with_cursor
113
+ full_text = "#{@prompt}#{line}"
114
+
115
+ # Calculate terminal width and check if wrapping is needed
116
+ width = TTY::Screen.width
117
+ visible_text = strip_ansi_codes(full_text)
118
+ display_width = calculate_display_width(visible_text)
119
+
120
+ # If no wrapping needed, return as is
121
+ return full_text if display_width <= width
122
+
123
+ # Otherwise, wrap the input (prompt on first line, continuation indented)
124
+ prompt_width = calculate_display_width(strip_ansi_codes(@prompt))
125
+ available_width = width - prompt_width
126
+
127
+ # For simplicity, just return full text and let terminal handle wrapping
128
+ # InlineInput is typically short, so natural wrapping should be fine
129
+ full_text
130
+ end
131
+
132
+ # Get cursor column position
133
+ # @return [Integer] Column position
134
+ def cursor_col
135
+ cursor_column(@prompt)
136
+ end
137
+
138
+ # Deactivate inline input
139
+ def deactivate
140
+ @active = false
141
+ @result_queue = nil
142
+ end
143
+
144
+ private
145
+
146
+ def handle_enter
147
+ result = expand_placeholders(current_line)
148
+ # If empty and has default, use default
149
+ result = @default_value.to_s if result.empty? && @default_value
150
+
151
+ queue = @result_queue
152
+ deactivate
153
+ queue&.push(result)
154
+
155
+ { action: :submit, result: result }
156
+ end
157
+
158
+ def expand_placeholders(text)
159
+ super(text, @paste_placeholders)
160
+ end
161
+
162
+ def handle_cancel
163
+ queue = @result_queue
164
+ deactivate
165
+ queue&.push(nil)
166
+
167
+ { action: :cancel }
168
+ end
169
+
170
+ def handle_shift_tab
171
+ # Auto-confirm as yes (or use default if it's true)
172
+ result = if @default_value == true || @default_value.to_s.downcase == "yes"
173
+ @default_value.to_s
174
+ else
175
+ "yes"
176
+ end
177
+
178
+ queue = @result_queue
179
+ deactivate
180
+ queue&.push(result)
181
+
182
+ { action: :toggle_mode }
183
+ end
184
+ end
185
+ end
186
+ end
187
+ end