update: tuiの文字入力のCtrlブロックを追加
This commit is contained in:
parent
4b9b4f1450
commit
d9f55185f0
2
TODO.md
2
TODO.md
|
|
@ -14,6 +14,8 @@
|
|||
- [ ] フルスクリーン化によるオーバーホール → [tickets/tui-fullscreen-overhaul.md](tickets/tui-fullscreen-overhaul.md)
|
||||
- [ ] Run 中の入力キューイング → [tickets/tui-input-queue.md](tickets/tui-input-queue.md)
|
||||
- [ ] ユーザーマニフェストのモデル設定 wizard → [tickets/tui-user-model-setup.md](tickets/tui-user-model-setup.md)
|
||||
- [ ] auto-kick 由来ターンが描画されない → [tickets/tui-pod-event-render.md](tickets/tui-pod-event-render.md)
|
||||
- [ ] 子 Pod の PodEvent::TurnEnded が親に届かない → [tickets/pod-event-delivery.md](tickets/pod-event-delivery.md)
|
||||
- [ ] サブミット入力
|
||||
- [ ] FileRef リゾルバ → [tickets/submit-file-ref-resolver.md](tickets/submit-file-ref-resolver.md)
|
||||
- [ ] Manifest: Tool Output / File Upload 上限の分離とデフォルト緩和 → [tickets/manifest-output-upload-limits.md](tickets/manifest-output-upload-limits.md)
|
||||
|
|
|
|||
|
|
@ -619,20 +619,14 @@ impl App {
|
|||
pub fn delete_char_after(&mut self) {
|
||||
self.input.delete_after();
|
||||
}
|
||||
pub fn delete_word_before(&mut self) {
|
||||
self.input.delete_word_before();
|
||||
}
|
||||
pub fn move_cursor_left(&mut self) {
|
||||
self.input.move_left();
|
||||
}
|
||||
pub fn move_cursor_right(&mut self) {
|
||||
self.input.move_right();
|
||||
}
|
||||
pub fn move_cursor_word_left(&mut self) {
|
||||
self.input.move_word_left();
|
||||
}
|
||||
pub fn move_cursor_word_right(&mut self) {
|
||||
self.input.move_word_right();
|
||||
pub fn move_cursor_start(&mut self) {
|
||||
self.input.move_start();
|
||||
}
|
||||
pub fn move_cursor_home(&mut self) {
|
||||
self.input.move_home();
|
||||
|
|
|
|||
|
|
@ -345,6 +345,10 @@ impl InputBuffer {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn move_start(&mut self) {
|
||||
self.cursor = 0;
|
||||
}
|
||||
|
||||
pub fn move_home(&mut self) {
|
||||
while self.cursor > 0 {
|
||||
if matches!(self.atoms[self.cursor - 1], Atom::Char('\n')) {
|
||||
|
|
@ -921,6 +925,14 @@ mod word_motion_tests {
|
|||
assert_eq!(cursor(&buf), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn move_start_lands_at_beginning_of_buffer() {
|
||||
let mut buf = buf_from("foo\nbar");
|
||||
assert_eq!(cursor(&buf), 7);
|
||||
buf.move_start();
|
||||
assert_eq!(cursor(&buf), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn forward_from_start_lands_after_first_word() {
|
||||
let mut buf = buf_from("foo bar baz");
|
||||
|
|
|
|||
|
|
@ -376,16 +376,65 @@ fn handle_key(app: &mut App, key: KeyEvent) -> Option<Method> {
|
|||
let shift = key.modifiers.contains(KeyModifiers::SHIFT);
|
||||
let alt = key.modifiers.contains(KeyModifiers::ALT);
|
||||
|
||||
// Scroll / navigation (history view).
|
||||
match key.code {
|
||||
// Modifier-key bindings.
|
||||
if let Some(method) = match key.code {
|
||||
KeyCode::Up if shift => {
|
||||
app.scroll.scroll_up(1);
|
||||
return None;
|
||||
Some(None)
|
||||
}
|
||||
KeyCode::Down if shift => {
|
||||
app.scroll.scroll_down(1);
|
||||
Some(None)
|
||||
}
|
||||
KeyCode::Home if ctrl => {
|
||||
app.scroll.to_top();
|
||||
Some(None)
|
||||
}
|
||||
KeyCode::End if ctrl => {
|
||||
app.scroll.to_bottom();
|
||||
Some(None)
|
||||
}
|
||||
KeyCode::Char('[') if ctrl => {
|
||||
app.scroll.jump_prev_turn();
|
||||
Some(None)
|
||||
}
|
||||
KeyCode::Char(']') if ctrl => {
|
||||
app.scroll.jump_next_turn();
|
||||
Some(None)
|
||||
}
|
||||
KeyCode::Char('o') if ctrl => {
|
||||
app.mode = app.mode.cycle();
|
||||
Some(None)
|
||||
}
|
||||
KeyCode::Char('a') if ctrl => {
|
||||
app.move_cursor_start();
|
||||
Some(app.refresh_completion())
|
||||
}
|
||||
KeyCode::Char('c') if ctrl => Some(handle_pause_or_quit(app)),
|
||||
KeyCode::Char('x') if ctrl => Some(if app.running {
|
||||
Some(Method::Cancel)
|
||||
} else {
|
||||
app.push_error("Nothing to cancel (Pod is not running).");
|
||||
None
|
||||
}),
|
||||
KeyCode::Char('d') if ctrl => Some(handle_shutdown(app)),
|
||||
KeyCode::Enter if alt => {
|
||||
app.insert_newline();
|
||||
Some(app.refresh_completion())
|
||||
}
|
||||
_ => None,
|
||||
} {
|
||||
return method;
|
||||
}
|
||||
|
||||
// Unbound Ctrl+Char keys are ignored before the text-input path so
|
||||
// holding Ctrl while typing never inserts control characters.
|
||||
if ctrl && matches!(key.code, KeyCode::Char(_)) {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Scroll / navigation (history view).
|
||||
match key.code {
|
||||
KeyCode::PageUp => {
|
||||
app.scroll.page_up();
|
||||
return None;
|
||||
|
|
@ -394,26 +443,6 @@ fn handle_key(app: &mut App, key: KeyEvent) -> Option<Method> {
|
|||
app.scroll.page_down();
|
||||
return None;
|
||||
}
|
||||
KeyCode::Home if ctrl => {
|
||||
app.scroll.to_top();
|
||||
return None;
|
||||
}
|
||||
KeyCode::End if ctrl => {
|
||||
app.scroll.to_bottom();
|
||||
return None;
|
||||
}
|
||||
KeyCode::Char('[') if ctrl => {
|
||||
app.scroll.jump_prev_turn();
|
||||
return None;
|
||||
}
|
||||
KeyCode::Char(']') if ctrl => {
|
||||
app.scroll.jump_next_turn();
|
||||
return None;
|
||||
}
|
||||
KeyCode::Char('o') if ctrl => {
|
||||
app.mode = app.mode.cycle();
|
||||
return None;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
|
|
@ -463,31 +492,13 @@ fn handle_key(app: &mut App, key: KeyEvent) -> Option<Method> {
|
|||
}
|
||||
|
||||
match key.code {
|
||||
KeyCode::Char('c') if ctrl => handle_pause_or_quit(app),
|
||||
KeyCode::Char('x') if ctrl => {
|
||||
if app.running {
|
||||
Some(Method::Cancel)
|
||||
} else {
|
||||
app.push_error("Nothing to cancel (Pod is not running).");
|
||||
None
|
||||
}
|
||||
}
|
||||
KeyCode::Char('d') if ctrl => handle_shutdown(app),
|
||||
KeyCode::Esc => {
|
||||
// Close the popup if it's still showing (covers the
|
||||
// request-in-flight case where `is_active()` was false).
|
||||
app.cancel_completion();
|
||||
None
|
||||
}
|
||||
KeyCode::Enter if alt => {
|
||||
app.insert_newline();
|
||||
app.refresh_completion()
|
||||
}
|
||||
KeyCode::Enter => app.submit_input(),
|
||||
KeyCode::Backspace if ctrl => {
|
||||
app.delete_word_before();
|
||||
app.refresh_completion()
|
||||
}
|
||||
KeyCode::Backspace => {
|
||||
app.delete_char_before();
|
||||
app.refresh_completion()
|
||||
|
|
@ -496,18 +507,10 @@ fn handle_key(app: &mut App, key: KeyEvent) -> Option<Method> {
|
|||
app.delete_char_after();
|
||||
app.refresh_completion()
|
||||
}
|
||||
KeyCode::Left if ctrl => {
|
||||
app.move_cursor_word_left();
|
||||
app.refresh_completion()
|
||||
}
|
||||
KeyCode::Left => {
|
||||
app.move_cursor_left();
|
||||
app.refresh_completion()
|
||||
}
|
||||
KeyCode::Right if ctrl => {
|
||||
app.move_cursor_word_right();
|
||||
app.refresh_completion()
|
||||
}
|
||||
KeyCode::Right => {
|
||||
app.move_cursor_right();
|
||||
app.refresh_completion()
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@
|
|||
|
||||
| キー | 動作 |
|
||||
|---|---|
|
||||
| 文字キー | カーソル位置に挿入 |
|
||||
| 文字キー | カーソル位置に挿入(未割り当ての `Ctrl`+文字キーは無視) |
|
||||
| `Ctrl-A` | 入力欄全体の先頭へ |
|
||||
| `Backspace` | カーソル直前を削除(ペーストプレースホルダは 1 回で全削除) |
|
||||
| `Delete` | カーソル直後を削除(同上) |
|
||||
| `Left` / `Right` | カーソル移動 |
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user