Add soft word wrap for input area and improve navigation
This commit is contained in:
parent
46bf99b0ab
commit
475e900e18
83
tui/input.py
83
tui/input.py
@ -7,20 +7,65 @@ import os
|
|||||||
from .agent import submit, log
|
from .agent import submit, log
|
||||||
|
|
||||||
|
|
||||||
|
def _build_visual(buffer, max_chars):
|
||||||
|
# Build list of (logical_line_idx, start_col) for each visual line.
|
||||||
|
visual = []
|
||||||
|
for i, line in enumerate(buffer):
|
||||||
|
if not line:
|
||||||
|
visual.append((i, 0))
|
||||||
|
else:
|
||||||
|
for start in range(0, max(len(line), 1), max_chars):
|
||||||
|
visual.append((i, start))
|
||||||
|
return visual
|
||||||
|
|
||||||
|
|
||||||
|
def _find_visual(visual, logical_line, col):
|
||||||
|
# Find visual line index for given logical line and column.
|
||||||
|
best = 0
|
||||||
|
for idx, (li, start) in enumerate(visual):
|
||||||
|
if li == logical_line:
|
||||||
|
best = idx
|
||||||
|
if start <= col:
|
||||||
|
break
|
||||||
|
return best
|
||||||
|
|
||||||
|
|
||||||
def handle_key(app, stdscr, key):
|
def handle_key(app, stdscr, key):
|
||||||
|
max_chars = app.w - 6 # usable width in input box
|
||||||
|
visual = _build_visual(app.input_buffer, max_chars)
|
||||||
|
cur_visual = _find_visual(visual, app.input_line, app.input_col)
|
||||||
|
|
||||||
|
processing = app.processing
|
||||||
|
|
||||||
|
# -- Always allowed (even during processing) --
|
||||||
|
if key == 3: # Ctrl+C → exit
|
||||||
|
app.running = False
|
||||||
|
elif key == curses.KEY_PPAGE:
|
||||||
|
app.scroll = max(0, app.scroll - (app.h - 10) // 2)
|
||||||
|
elif key == curses.KEY_NPAGE:
|
||||||
|
app.scroll += (app.h - 10) // 2
|
||||||
|
elif key == curses.KEY_RESIZE:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# -- Blocked during processing --
|
||||||
|
elif processing:
|
||||||
|
pass # ignore all other keys while processing
|
||||||
|
|
||||||
# -- Ctrl shortcuts --
|
# -- Ctrl shortcuts --
|
||||||
if key == 4: # Ctrl+D → submit query ke LLM
|
elif key == 4: # Ctrl+D → submit query ke LLM
|
||||||
submit(app, stdscr)
|
submit(app, stdscr)
|
||||||
elif key == 23: # Ctrl+W → popup ganti workspace
|
elif key == 23: # Ctrl+W → popup ganti workspace
|
||||||
workspace_popup(app, stdscr)
|
workspace_popup(app, stdscr)
|
||||||
elif key == 3: # Ctrl+C → exit
|
|
||||||
app.running = False
|
|
||||||
elif key == 12: # Ctrl+L → clear chat log
|
elif key == 12: # Ctrl+L → clear chat log
|
||||||
app.log.clear()
|
app.log.clear()
|
||||||
|
|
||||||
# -- Enter: buat baris baru di input buffer --
|
# -- Enter: split logical line at cursor position --
|
||||||
elif key in (curses.KEY_ENTER, 10, 13):
|
elif key in (curses.KEY_ENTER, 10, 13):
|
||||||
app.input_buffer.insert(app.input_line + 1, "")
|
line = app.input_buffer[app.input_line]
|
||||||
|
left = line[:app.input_col]
|
||||||
|
right = line[app.input_col:]
|
||||||
|
app.input_buffer[app.input_line] = left
|
||||||
|
app.input_buffer.insert(app.input_line + 1, right)
|
||||||
app.input_line += 1
|
app.input_line += 1
|
||||||
app.input_col = 0
|
app.input_col = 0
|
||||||
|
|
||||||
@ -40,17 +85,15 @@ def handle_key(app, stdscr, key):
|
|||||||
|
|
||||||
# -- Navigation arrows --
|
# -- Navigation arrows --
|
||||||
elif key == curses.KEY_UP:
|
elif key == curses.KEY_UP:
|
||||||
if app.input_line > 0:
|
if cur_visual > 0:
|
||||||
app.input_line -= 1
|
prev_li, prev_start = visual[cur_visual - 1]
|
||||||
app.input_col = min(
|
app.input_line = prev_li
|
||||||
app.input_col, len(app.input_buffer[app.input_line])
|
app.input_col = min(app.input_col, len(app.input_buffer[prev_li]))
|
||||||
)
|
|
||||||
elif key == curses.KEY_DOWN:
|
elif key == curses.KEY_DOWN:
|
||||||
if app.input_line < len(app.input_buffer) - 1:
|
if cur_visual < len(visual) - 1:
|
||||||
app.input_line += 1
|
next_li, next_start = visual[cur_visual + 1]
|
||||||
app.input_col = min(
|
app.input_line = next_li
|
||||||
app.input_col, len(app.input_buffer[app.input_line])
|
app.input_col = min(app.input_col, len(app.input_buffer[next_li]))
|
||||||
)
|
|
||||||
elif key == curses.KEY_LEFT:
|
elif key == curses.KEY_LEFT:
|
||||||
if app.input_col > 0:
|
if app.input_col > 0:
|
||||||
app.input_col -= 1
|
app.input_col -= 1
|
||||||
@ -68,16 +111,6 @@ def handle_key(app, stdscr, key):
|
|||||||
elif key == curses.KEY_END:
|
elif key == curses.KEY_END:
|
||||||
app.input_col = len(app.input_buffer[app.input_line])
|
app.input_col = len(app.input_buffer[app.input_line])
|
||||||
|
|
||||||
# -- Page Up / Down: scroll chat area --
|
|
||||||
elif key == curses.KEY_PPAGE:
|
|
||||||
app.scroll = max(0, app.scroll - (app.h - 10))
|
|
||||||
elif key == curses.KEY_NPAGE:
|
|
||||||
app.scroll += app.h - 10
|
|
||||||
|
|
||||||
# -- Resize terminal: tidak perlu action, next loop akan baca ukuran baru --
|
|
||||||
elif key == curses.KEY_RESIZE:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# -- Tab: insert 4 spasi --
|
# -- Tab: insert 4 spasi --
|
||||||
elif key == 9:
|
elif key == 9:
|
||||||
line = app.input_buffer[app.input_line]
|
line = app.input_buffer[app.input_line]
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user