kward 0.71.0 → 0.73.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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +30 -0
- data/CHANGELOG.md +93 -0
- data/Gemfile.lock +2 -2
- data/README.md +4 -0
- data/doc/agent-tools.md +15 -6
- data/doc/authentication.md +22 -1
- data/doc/code-search.md +42 -2
- data/doc/configuration.md +106 -3
- data/doc/context-budgeting.md +136 -0
- data/doc/context-tools.md +16 -3
- data/doc/editor.md +415 -0
- data/doc/extensibility.md +16 -7
- data/doc/files.md +100 -0
- data/doc/getting-started.md +25 -18
- data/doc/git.md +123 -0
- data/doc/memory.md +24 -4
- data/doc/personas.md +34 -5
- data/doc/plugins.md +72 -1
- data/doc/releasing.md +37 -9
- data/doc/rpc.md +75 -5
- data/doc/session-management.md +35 -1
- data/doc/shell.md +332 -0
- data/doc/tabs.md +122 -0
- data/doc/troubleshooting.md +77 -1
- data/doc/usage.md +79 -7
- data/doc/web-search.md +12 -4
- data/doc/workspace-tools.md +51 -12
- data/examples/plugins/space_invaders.rb +377 -0
- data/lib/kward/agent.rb +1 -1
- data/lib/kward/ansi.rb +62 -23
- data/lib/kward/cli/commands.rb +33 -2
- data/lib/kward/cli/git.rb +150 -0
- data/lib/kward/cli/interactive_turn.rb +73 -9
- data/lib/kward/cli/plugins.rb +54 -4
- data/lib/kward/cli/prompt_interface.rb +32 -1
- data/lib/kward/cli/rendering.rb +4 -1
- data/lib/kward/cli/runtime_helpers.rb +268 -4
- data/lib/kward/cli/sessions.rb +2 -2
- data/lib/kward/cli/settings.rb +217 -9
- data/lib/kward/cli/slash_commands.rb +628 -2
- data/lib/kward/cli/tabs.rb +725 -0
- data/lib/kward/cli/tool_summaries.rb +6 -0
- data/lib/kward/cli.rb +150 -26
- data/lib/kward/clipboard.rb +2 -3
- data/lib/kward/compactor.rb +7 -19
- data/lib/kward/config_files.rb +145 -1
- data/lib/kward/context_budget_meter.rb +44 -0
- data/lib/kward/conversation.rb +12 -4
- data/lib/kward/editor_mode.rb +25 -0
- data/lib/kward/ekwsh.rb +559 -0
- data/lib/kward/image_attachments.rb +3 -1
- data/lib/kward/interactive_pty_runner.rb +151 -0
- data/lib/kward/local_command_runner.rb +155 -0
- data/lib/kward/local_pty_command_runner.rb +171 -0
- data/lib/kward/model/context_usage.rb +2 -2
- data/lib/kward/model/payloads.rb +2 -5
- data/lib/kward/plugin_registry.rb +61 -0
- data/lib/kward/project_files.rb +52 -0
- data/lib/kward/prompt_history.rb +84 -0
- data/lib/kward/prompt_interface/composer_controller.rb +69 -1
- data/lib/kward/prompt_interface/composer_renderer.rb +109 -13
- data/lib/kward/prompt_interface/composer_state.rb +96 -27
- data/lib/kward/prompt_interface/editor/auto_close_pairs.rb +123 -0
- data/lib/kward/prompt_interface/editor/auto_indent.rb +510 -0
- data/lib/kward/prompt_interface/editor/buffer.rb +109 -0
- data/lib/kward/prompt_interface/editor/controller.rb +1218 -0
- data/lib/kward/prompt_interface/editor/endwise.rb +321 -0
- data/lib/kward/prompt_interface/editor/file_marker.rb +40 -0
- data/lib/kward/prompt_interface/editor/indent_navigation.rb +61 -0
- data/lib/kward/prompt_interface/editor/kill_ring.rb +78 -0
- data/lib/kward/prompt_interface/editor/modes/emacs.rb +259 -0
- data/lib/kward/prompt_interface/editor/modes/modern.rb +354 -0
- data/lib/kward/prompt_interface/editor/modes/vibe.rb +1812 -0
- data/lib/kward/prompt_interface/editor/modes/vibe_insert_readline.rb +166 -0
- data/lib/kward/prompt_interface/editor/renderer.rb +244 -0
- data/lib/kward/prompt_interface/editor/search.rb +76 -0
- data/lib/kward/prompt_interface/editor/selections.rb +120 -0
- data/lib/kward/prompt_interface/editor/state.rb +1271 -0
- data/lib/kward/prompt_interface/editor/status_text.rb +23 -0
- data/lib/kward/prompt_interface/editor/syntax_highlighter.rb +422 -0
- data/lib/kward/prompt_interface/editor/undo_history.rb +46 -0
- data/lib/kward/prompt_interface/editor/vibe_state.rb +44 -0
- data/lib/kward/prompt_interface/file_overlay.rb +211 -0
- data/lib/kward/prompt_interface/git_prompt.rb +288 -0
- data/lib/kward/prompt_interface/interactive/controller.rb +186 -0
- data/lib/kward/prompt_interface/interactive/renderer.rb +71 -0
- data/lib/kward/prompt_interface/interactive/state.rb +62 -0
- data/lib/kward/prompt_interface/key_handler.rb +451 -57
- data/lib/kward/prompt_interface/overlay_renderer.rb +21 -2
- data/lib/kward/prompt_interface/project_browser.rb +524 -0
- data/lib/kward/prompt_interface/question_prompt.rb +99 -56
- data/lib/kward/prompt_interface/runtime_state.rb +43 -0
- data/lib/kward/prompt_interface/screen.rb +19 -3
- data/lib/kward/prompt_interface/selection_prompt.rb +10 -19
- data/lib/kward/prompt_interface/slash_overlay.rb +2 -0
- data/lib/kward/prompt_interface/stream_state.rb +7 -0
- data/lib/kward/prompt_interface/transcript_buffer.rb +6 -0
- data/lib/kward/prompt_interface.rb +366 -222
- data/lib/kward/prompts/commands.rb +9 -0
- data/lib/kward/prompts.rb +2 -0
- data/lib/kward/rpc/memory_methods.rb +83 -0
- data/lib/kward/rpc/server.rb +169 -83
- data/lib/kward/rpc/session_manager.rb +45 -121
- data/lib/kward/rpc/session_tree_rows.rb +9 -115
- data/lib/kward/rpc/tool_event_normalizer.rb +1 -1
- data/lib/kward/rpc/tool_metadata.rb +11 -0
- data/lib/kward/rpc/transcript_normalizer.rb +4 -39
- data/lib/kward/scratchpad_runner.rb +56 -0
- data/lib/kward/session_diff.rb +20 -3
- data/lib/kward/session_naming.rb +11 -0
- data/lib/kward/session_store.rb +44 -0
- data/lib/kward/session_tree_nodes.rb +136 -0
- data/lib/kward/session_tree_renderer.rb +9 -131
- data/lib/kward/tab_store.rb +47 -0
- data/lib/kward/terminal_keys.rb +84 -0
- data/lib/kward/terminal_sequences.rb +42 -0
- data/lib/kward/text_boundary.rb +25 -0
- data/lib/kward/tools/context_budget_stats.rb +54 -0
- data/lib/kward/tools/context_for_task.rb +204 -0
- data/lib/kward/tools/read_file.rb +8 -4
- data/lib/kward/tools/registry.rb +62 -16
- data/lib/kward/tools/tool_call.rb +10 -0
- data/lib/kward/version.rb +1 -1
- data/lib/kward/workers/git_guard.rb +93 -0
- data/lib/kward/workers/job.rb +99 -0
- data/lib/kward/workers/live_view.rb +49 -0
- data/lib/kward/workers/manager.rb +288 -0
- data/lib/kward/workers/queue_runner.rb +166 -0
- data/lib/kward/workers/queue_store.rb +112 -0
- data/lib/kward/workers/store.rb +72 -0
- data/lib/kward/workers/tool_policy.rb +23 -0
- data/lib/kward/workers/worker.rb +82 -0
- data/lib/kward/workers/write_lock.rb +38 -0
- data/lib/kward/workers.rb +10 -0
- data/lib/kward/workspace.rb +125 -87
- data/templates/default/fulldoc/html/css/kward.css +140 -36
- data/templates/default/fulldoc/html/images/kward_screen_1.png +0 -0
- data/templates/default/fulldoc/html/setup.rb +1 -0
- data/templates/default/kward_navigation.rb +12 -1
- data/templates/default/layout/html/layout.erb +23 -34
- data/templates/default/layout/html/setup.rb +6 -0
- metadata +67 -1
|
@@ -65,6 +65,7 @@ module Kward
|
|
|
65
65
|
def finish_question_prompt(saved_state)
|
|
66
66
|
@mutex.synchronize do
|
|
67
67
|
@question_state = nil
|
|
68
|
+
@question_prompt_active = false
|
|
68
69
|
@select_state = saved_state[:select_state]
|
|
69
70
|
@prompt_label = saved_state[:prompt_label]
|
|
70
71
|
self.composer_input = saved_state[:input]
|
|
@@ -81,20 +82,17 @@ module Kward
|
|
|
81
82
|
|
|
82
83
|
def handle_question_key(key)
|
|
83
84
|
return if handle_question_bracketed_paste_key(key)
|
|
85
|
+
return if handle_question_shift_enter_key(key)
|
|
84
86
|
|
|
85
87
|
csi_result = handle_question_csi_u_key(key)
|
|
86
88
|
return csi_result unless csi_result == false
|
|
87
89
|
|
|
88
|
-
if key
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
return handle_question_key(token)
|
|
93
|
-
end
|
|
94
|
-
end
|
|
90
|
+
return true if handle_bundled_key(key) { |token| handle_question_key(token) }
|
|
91
|
+
|
|
92
|
+
binding_result = handle_question_composer_key_binding(key)
|
|
93
|
+
return binding_result unless binding_result == false
|
|
95
94
|
|
|
96
|
-
|
|
97
|
-
case key_name
|
|
95
|
+
case key_name_for(key)
|
|
98
96
|
when :return, :enter
|
|
99
97
|
current_question_answer
|
|
100
98
|
when :backspace
|
|
@@ -102,13 +100,13 @@ module Kward
|
|
|
102
100
|
when :delete
|
|
103
101
|
question_delete_at_cursor
|
|
104
102
|
when :left
|
|
105
|
-
|
|
103
|
+
move_cursor_left
|
|
106
104
|
when :right
|
|
107
|
-
|
|
105
|
+
move_cursor_right
|
|
108
106
|
when :home
|
|
109
|
-
|
|
107
|
+
move_to_start_of_line
|
|
110
108
|
when :end
|
|
111
|
-
|
|
109
|
+
move_to_end_of_line
|
|
112
110
|
when :up
|
|
113
111
|
question_previous_choice
|
|
114
112
|
when :down
|
|
@@ -132,21 +130,45 @@ module Kward
|
|
|
132
130
|
return false unless sequence
|
|
133
131
|
|
|
134
132
|
code = sequence[:code]
|
|
133
|
+
modifier = sequence[:modifier]
|
|
135
134
|
queue_pending_keys(sequence[:remaining]) if sequence[:remaining] && !sequence[:remaining].empty?
|
|
136
135
|
|
|
137
136
|
case code
|
|
138
137
|
when 13
|
|
139
|
-
|
|
138
|
+
if modifier == 2
|
|
139
|
+
question_insert_string("\n")
|
|
140
|
+
nil
|
|
141
|
+
else
|
|
142
|
+
current_question_answer
|
|
143
|
+
end
|
|
140
144
|
when 27
|
|
141
145
|
SELECT_CANCEL
|
|
142
146
|
when 8, 127
|
|
143
|
-
question_delete_before_cursor
|
|
147
|
+
alt_modifier?(modifier) ? question_delete_word_before_cursor : question_delete_before_cursor
|
|
144
148
|
nil
|
|
145
149
|
else
|
|
146
|
-
|
|
150
|
+
modified_result = handle_question_modified_csi_u_key(code, modifier)
|
|
151
|
+
return modified_result unless modified_result == false
|
|
152
|
+
|
|
153
|
+
question_insert_csi_u_text(sequence)
|
|
147
154
|
end
|
|
148
155
|
end
|
|
149
156
|
|
|
157
|
+
def handle_question_modified_csi_u_key(code, modifier)
|
|
158
|
+
before = composer_input.dup
|
|
159
|
+
result = handle_modified_csi_u_key(code, modifier)
|
|
160
|
+
question_select_custom_choice if result != false && composer_input != before
|
|
161
|
+
result
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def question_insert_csi_u_text(sequence)
|
|
165
|
+
text = csi_u_printable_text(sequence)
|
|
166
|
+
return true if text.nil? && csi_u_text_field?(sequence)
|
|
167
|
+
return false unless text
|
|
168
|
+
|
|
169
|
+
question_insert_string(text)
|
|
170
|
+
end
|
|
171
|
+
|
|
150
172
|
def handle_question_escape_sequence
|
|
151
173
|
pending_sequence = read_pending_escape_sequence
|
|
152
174
|
return SELECT_CANCEL if pending_sequence.empty?
|
|
@@ -156,27 +178,24 @@ module Kward
|
|
|
156
178
|
queue_pending_keys(full_sequence[sequence.length..]) if full_sequence.length > sequence.length
|
|
157
179
|
return SELECT_CANCEL if sequence == "\e"
|
|
158
180
|
|
|
159
|
-
|
|
160
|
-
|
|
181
|
+
binding_result = handle_question_composer_key_binding(sequence)
|
|
182
|
+
return binding_result unless binding_result == false
|
|
183
|
+
|
|
184
|
+
case key_name_for(sequence)
|
|
161
185
|
when :up
|
|
162
186
|
question_previous_choice
|
|
163
187
|
when :down
|
|
164
188
|
question_next_choice
|
|
165
189
|
when :left
|
|
166
|
-
|
|
190
|
+
move_cursor_left
|
|
167
191
|
when :right
|
|
168
|
-
|
|
192
|
+
move_cursor_right
|
|
169
193
|
end
|
|
170
194
|
true
|
|
171
195
|
end
|
|
172
196
|
|
|
173
197
|
def handle_question_bracketed_paste_key(key)
|
|
174
|
-
|
|
175
|
-
return false unless paste
|
|
176
|
-
|
|
177
|
-
question_insert_string(normalize_paste(paste[:content]))
|
|
178
|
-
queue_pending_keys(paste[:remaining]) if paste[:remaining] && !paste[:remaining].empty?
|
|
179
|
-
true
|
|
198
|
+
handle_bracketed_paste(key) { |content| question_insert_string(content) }
|
|
180
199
|
end
|
|
181
200
|
|
|
182
201
|
def current_question_answer
|
|
@@ -244,34 +263,78 @@ module Kward
|
|
|
244
263
|
question_insert_string(key)
|
|
245
264
|
end
|
|
246
265
|
|
|
266
|
+
def handle_question_shift_enter_key(key)
|
|
267
|
+
sequence = shift_enter_sequence_for(key)
|
|
268
|
+
return false unless sequence
|
|
269
|
+
|
|
270
|
+
question_insert_string("\n")
|
|
271
|
+
queue_pending_keys(key[sequence.length..]) if key.length > sequence.length
|
|
272
|
+
true
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
def handle_question_composer_key_binding(key)
|
|
276
|
+
before = composer_input.dup
|
|
277
|
+
result = handle_composer_key_binding(key)
|
|
278
|
+
question_select_custom_choice if result != false && composer_input != before
|
|
279
|
+
result
|
|
280
|
+
end
|
|
281
|
+
|
|
247
282
|
def question_insert_string(string)
|
|
248
283
|
return if string.empty?
|
|
249
284
|
|
|
250
|
-
|
|
251
|
-
|
|
285
|
+
insert_string(string)
|
|
286
|
+
question_select_custom_choice
|
|
252
287
|
end
|
|
253
288
|
|
|
254
289
|
def question_delete_before_cursor
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
290
|
+
before = composer_input.dup
|
|
291
|
+
delete_before_cursor
|
|
292
|
+
question_select_custom_choice if composer_input != before && !composer_input.empty?
|
|
258
293
|
end
|
|
259
294
|
|
|
260
295
|
def question_delete_at_cursor
|
|
261
|
-
|
|
296
|
+
before = composer_input.dup
|
|
297
|
+
delete_at_cursor
|
|
298
|
+
question_select_custom_choice if composer_input != before && !composer_input.empty?
|
|
299
|
+
end
|
|
262
300
|
|
|
263
|
-
|
|
301
|
+
def question_delete_word_before_cursor
|
|
302
|
+
before = composer_input.dup
|
|
303
|
+
delete_word_before_cursor
|
|
304
|
+
question_select_custom_choice if composer_input != before && !composer_input.empty?
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
def question_select_custom_choice
|
|
308
|
+
@question_state[:selection_index] = question_choices.length - 1 if @question_state
|
|
264
309
|
end
|
|
265
310
|
|
|
266
311
|
def question_composer_layout(width, height = screen_height)
|
|
267
312
|
content_width = [width - 4, 1].max
|
|
268
313
|
overlay_rows = active_overlay_rows(width, height: height)
|
|
269
|
-
|
|
270
|
-
return [rows, question_custom_cursor_row, question_custom_cursor_col(width)] if selected_question_choice&.fetch(:custom, false)
|
|
314
|
+
return question_custom_composer_layout(width, height, overlay_rows, content_width) if selected_question_choice&.fetch(:custom, false)
|
|
271
315
|
|
|
316
|
+
rows = overlay_rows + [top_border(width), box_content_row("", content_width)]
|
|
317
|
+
rows.concat(question_bottom_border_rows(width))
|
|
272
318
|
[rows, overlay_rows.length + 1, 2]
|
|
273
319
|
end
|
|
274
320
|
|
|
321
|
+
def question_custom_composer_layout(width, height, overlay_rows, content_width)
|
|
322
|
+
input_layout_rows, input_cursor_row, input_cursor_col = input_layout(content_width)
|
|
323
|
+
max_input_rows = max_visible_input_rows(0, overlay_rows.length, 0, height: height)
|
|
324
|
+
visible_start = [[input_cursor_row - max_input_rows + 1, 0].max, [input_layout_rows.length - max_input_rows, 0].max].min
|
|
325
|
+
visible_rows = input_layout_rows[visible_start, max_input_rows] || [""]
|
|
326
|
+
rows = overlay_rows + [top_border(width)]
|
|
327
|
+
rows.concat(visible_rows.map { |row| box_content_row(row, content_width) })
|
|
328
|
+
rows.concat(question_bottom_border_rows(width))
|
|
329
|
+
cursor_row = overlay_rows.length + 1 + input_cursor_row - visible_start
|
|
330
|
+
cursor_col = 2 + [input_cursor_col, content_width - 1].min
|
|
331
|
+
[rows, cursor_row, cursor_col]
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
def question_bottom_border_rows(width)
|
|
335
|
+
@tabs.empty? ? [bottom_border(width)] : tab_border_rows(width)
|
|
336
|
+
end
|
|
337
|
+
|
|
275
338
|
def question_overlay_rows(width)
|
|
276
339
|
title = "Question #{@question_state[:index]}/#{@question_state[:total]} · #{@question_state[:header]}"
|
|
277
340
|
lines = [
|
|
@@ -286,35 +349,15 @@ module Kward
|
|
|
286
349
|
overlay_card_rows(title, lines, width)
|
|
287
350
|
end
|
|
288
351
|
|
|
289
|
-
def question_custom_cursor_row
|
|
290
|
-
4 + question_choices.index { |choice| choice[:custom] }.to_i
|
|
291
|
-
end
|
|
292
|
-
|
|
293
|
-
def question_custom_cursor_col(width)
|
|
294
|
-
card_width = overlay_card_width(width)
|
|
295
|
-
left_padding = overlay_left_padding(width, card_width)
|
|
296
|
-
custom_prefix = selected_question_choice&.fetch(:custom, false) || !composer_input.empty? ? "Type something: " : "Type something."
|
|
297
|
-
visible_before_cursor = display_question_input(composer_input[0...composer_cursor])
|
|
298
|
-
[[left_padding + 2 + 2 + custom_prefix.length + visible_before_cursor.length, width - 1].min, 0].max
|
|
299
|
-
end
|
|
300
|
-
|
|
301
352
|
def choice_text(choice, selected: false)
|
|
302
353
|
if choice[:custom]
|
|
303
|
-
|
|
304
|
-
"Type something: #{display_question_input(composer_input)}"
|
|
305
|
-
else
|
|
306
|
-
"Type something."
|
|
307
|
-
end
|
|
354
|
+
selected ? "Type a custom answer below." : "Type something."
|
|
308
355
|
else
|
|
309
356
|
description = choice[:description].empty? ? "" : " — #{choice[:description]}"
|
|
310
357
|
"#{choice[:label]}#{description}"
|
|
311
358
|
end
|
|
312
359
|
end
|
|
313
360
|
|
|
314
|
-
def display_question_input(value)
|
|
315
|
-
value.to_s.gsub(/\s+/, " ")
|
|
316
|
-
end
|
|
317
|
-
|
|
318
361
|
end
|
|
319
362
|
end
|
|
320
363
|
end
|
|
@@ -50,6 +50,21 @@ module Kward
|
|
|
50
50
|
true
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
+
def cached_composer_status_text
|
|
54
|
+
return nil unless @composer_status
|
|
55
|
+
|
|
56
|
+
now = monotonic_now
|
|
57
|
+
elapsed = now - @last_composer_status_refresh.to_f
|
|
58
|
+
if @cached_composer_status_text.nil? || elapsed >= COMPOSER_STATUS_REFRESH_INTERVAL
|
|
59
|
+
text = @composer_status.call.to_s
|
|
60
|
+
@cached_composer_status_text = text.empty? ? nil : status_composer_text(text)
|
|
61
|
+
@last_composer_status_refresh = now
|
|
62
|
+
end
|
|
63
|
+
@cached_composer_status_text
|
|
64
|
+
rescue StandardError
|
|
65
|
+
@cached_composer_status_text = nil
|
|
66
|
+
end
|
|
67
|
+
|
|
53
68
|
def monotonic_now
|
|
54
69
|
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
55
70
|
end
|
|
@@ -59,6 +74,34 @@ module Kward
|
|
|
59
74
|
ANSI.colorize(text, *styles, enabled: @color_enabled)
|
|
60
75
|
end
|
|
61
76
|
|
|
77
|
+
def normalize_tab_keybindings(value)
|
|
78
|
+
text = value.to_s.downcase
|
|
79
|
+
return "ctrl" if text == "ctrl"
|
|
80
|
+
return "alt" if text == "alt"
|
|
81
|
+
|
|
82
|
+
RbConfig::CONFIG["host_os"].to_s.downcase.include?("darwin") ? "ctrl" : "alt"
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def normalize_editor_mode(value)
|
|
86
|
+
EditorMode.normalize(value)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def normalize_editor_line_numbers(value)
|
|
90
|
+
EditorMode.normalize_line_numbers(value)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def tab_action_result?(result)
|
|
94
|
+
result.is_a?(Hash) && result[:tab_action]
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def reasoning_action_result?(result)
|
|
98
|
+
result.is_a?(Hash) && result[:reasoning_action]
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def prompt_action_result?(result)
|
|
102
|
+
tab_action_result?(result) || reasoning_action_result?(result)
|
|
103
|
+
end
|
|
104
|
+
|
|
62
105
|
end
|
|
63
106
|
end
|
|
64
107
|
end
|
|
@@ -56,6 +56,8 @@ module Kward
|
|
|
56
56
|
def render_cursor_visibility_locked
|
|
57
57
|
visible = !(@question_state && !selected_question_choice&.fetch(:custom, false))
|
|
58
58
|
visible = select_editing_active? if @select_state
|
|
59
|
+
visible = git_composing? if @git_state
|
|
60
|
+
visible = project_browser_search_active? if project_browser_visible?
|
|
59
61
|
set_cursor_visible_locked(visible)
|
|
60
62
|
end
|
|
61
63
|
|
|
@@ -66,6 +68,20 @@ module Kward
|
|
|
66
68
|
@cursor_visible = visible
|
|
67
69
|
end
|
|
68
70
|
|
|
71
|
+
def set_editor_bar_cursor_locked
|
|
72
|
+
return if @editor_bar_cursor_active
|
|
73
|
+
|
|
74
|
+
@output_io.print(CURSOR_SHAPE_BAR)
|
|
75
|
+
@editor_bar_cursor_active = true
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def restore_editor_cursor_shape_locked
|
|
79
|
+
return unless @editor_bar_cursor_active
|
|
80
|
+
|
|
81
|
+
@output_io.print(CURSOR_SHAPE_DEFAULT)
|
|
82
|
+
@editor_bar_cursor_active = false
|
|
83
|
+
end
|
|
84
|
+
|
|
69
85
|
def reserve_composer_region_locked(width: screen_width, height: screen_height)
|
|
70
86
|
rows, = composer_layout(width, height)
|
|
71
87
|
ensure_scroll_region_locked(rows.length, width: width, height: height)
|
|
@@ -79,7 +95,7 @@ module Kward
|
|
|
79
95
|
old_top = [height - old_reserved_rows + 1, 1].max
|
|
80
96
|
@reserved_rows = new_reserved_rows
|
|
81
97
|
new_top = composer_top_row(height)
|
|
82
|
-
@output_io.print(
|
|
98
|
+
@output_io.print(TerminalSequences.scroll_region(1, transcript_bottom_row(height)))
|
|
83
99
|
clear_screen_rows_locked(old_top, new_top - 1) if new_top > old_top
|
|
84
100
|
@last_composer_rows = []
|
|
85
101
|
redraw_transcript_locked(width: width, height: height) if redraw_transcript && new_reserved_rows < old_reserved_rows
|
|
@@ -103,7 +119,7 @@ module Kward
|
|
|
103
119
|
end
|
|
104
120
|
|
|
105
121
|
def restore_scroll_region_locked
|
|
106
|
-
@output_io.print(
|
|
122
|
+
@output_io.print(TerminalSequences.restore_scroll_region)
|
|
107
123
|
@reserved_rows = 0
|
|
108
124
|
end
|
|
109
125
|
|
|
@@ -172,7 +188,7 @@ module Kward
|
|
|
172
188
|
end
|
|
173
189
|
|
|
174
190
|
def move_to_screen(row, col)
|
|
175
|
-
@output_io.print(
|
|
191
|
+
@output_io.print(TerminalSequences.move_to(row, col))
|
|
176
192
|
end
|
|
177
193
|
|
|
178
194
|
def screen_size
|
|
@@ -10,13 +10,7 @@ module Kward
|
|
|
10
10
|
return select_current_choice if key.nil?
|
|
11
11
|
return if handle_select_bracketed_paste_key(key)
|
|
12
12
|
|
|
13
|
-
if key
|
|
14
|
-
token = next_key_token(key)
|
|
15
|
-
if token.length < key.length
|
|
16
|
-
queue_pending_keys(key[token.length..])
|
|
17
|
-
return handle_select_key(token)
|
|
18
|
-
end
|
|
19
|
-
end
|
|
13
|
+
return true if handle_bundled_key(key) { |token| handle_select_key(token) }
|
|
20
14
|
|
|
21
15
|
return handle_select_confirmation_key(key) if select_confirmation_active?
|
|
22
16
|
|
|
@@ -101,7 +95,7 @@ module Kward
|
|
|
101
95
|
modified_result = handle_select_modified_csi_u_key(code, modifier)
|
|
102
96
|
return modified_result unless modified_result == false
|
|
103
97
|
|
|
104
|
-
handle_select_printable_csi_u_key(
|
|
98
|
+
handle_select_printable_csi_u_key(sequence)
|
|
105
99
|
end
|
|
106
100
|
end
|
|
107
101
|
|
|
@@ -119,12 +113,12 @@ module Kward
|
|
|
119
113
|
end
|
|
120
114
|
end
|
|
121
115
|
|
|
122
|
-
def handle_select_printable_csi_u_key(
|
|
123
|
-
|
|
124
|
-
return
|
|
116
|
+
def handle_select_printable_csi_u_key(sequence)
|
|
117
|
+
text = csi_u_printable_text(sequence)
|
|
118
|
+
return true if text.nil? && csi_u_text_field?(sequence)
|
|
119
|
+
return false unless text
|
|
125
120
|
|
|
126
|
-
|
|
127
|
-
select_typed_key(key)
|
|
121
|
+
select_typed_key(text)
|
|
128
122
|
end
|
|
129
123
|
|
|
130
124
|
def handle_select_escape_sequence
|
|
@@ -153,12 +147,9 @@ module Kward
|
|
|
153
147
|
end
|
|
154
148
|
|
|
155
149
|
def handle_select_bracketed_paste_key(key)
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
select_insert_string(normalize_paste(paste[:content])) if select_editing_active?
|
|
160
|
-
queue_pending_keys(paste[:remaining]) if paste[:remaining] && !paste[:remaining].empty?
|
|
161
|
-
true
|
|
150
|
+
handle_bracketed_paste(key) do |content|
|
|
151
|
+
select_insert_string(content) if select_editing_active?
|
|
152
|
+
end
|
|
162
153
|
end
|
|
163
154
|
|
|
164
155
|
def select_current_choice
|