diff --git a/Cargo.lock b/Cargo.lock index c3b197f..0c3d5a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -339,12 +339,6 @@ dependencies = [ "capnp", ] -[[package]] -name = "cassowary" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" - [[package]] name = "castaway" version = "0.2.4" @@ -447,20 +441,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" -[[package]] -name = "compact_str" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b79c4069c6cad78e2e0cdfcbd26275770669fb39fd308a752dc110e83b9af32" -dependencies = [ - "castaway", - "cfg-if", - "itoa", - "rustversion", - "ryu", - "static_assertions", -] - [[package]] name = "compact_str" version = "0.9.0" @@ -1350,7 +1330,7 @@ version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfe4fbac503b8d1f88e6676011885f34b7174f46e59956bba534ba83abded4df" dependencies = [ - "unicode-width 0.2.0", + "unicode-width", ] [[package]] @@ -1446,8 +1426,6 @@ version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ - "allocator-api2", - "equivalent", "foldhash 0.1.5", ] @@ -1810,15 +1788,6 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" -[[package]] -name = "itertools" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.14.0" @@ -2007,15 +1976,6 @@ dependencies = [ "tracing-subscriber", ] -[[package]] -name = "lru" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" -dependencies = [ - "hashbrown 0.15.5", -] - [[package]] name = "lru" version = "0.16.3" @@ -2648,13 +2608,14 @@ dependencies = [ "glob", "json5", "libc", - "ratatui 0.30.0", + "ratatui", "reqwest", "serde", "serde_json", "tiktoken-rs", "tokio", "tui-markdown", + "unicode-width", "walkdir", ] @@ -2701,7 +2662,7 @@ dependencies = [ "paste", "peg", "poc-agent", - "ratatui 0.29.0", + "ratatui", "rayon", "redb", "regex", @@ -3055,27 +3016,6 @@ dependencies = [ "rand 0.9.2", ] -[[package]] -name = "ratatui" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b" -dependencies = [ - "bitflags 2.11.0", - "cassowary", - "compact_str 0.8.1", - "crossterm 0.28.1", - "indoc", - "instability", - "itertools 0.13.0", - "lru 0.12.5", - "paste", - "strum 0.26.3", - "unicode-segmentation", - "unicode-truncate 1.1.0", - "unicode-width 0.2.0", -] - [[package]] name = "ratatui" version = "0.30.0" @@ -3097,17 +3037,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ef8dea09a92caaf73bff7adb70b76162e5937524058a7e5bff37869cbbec293" dependencies = [ "bitflags 2.11.0", - "compact_str 0.9.0", + "compact_str", "hashbrown 0.16.1", "indoc", - "itertools 0.14.0", + "itertools", "kasuari", - "lru 0.16.3", - "strum 0.27.2", + "lru", + "strum", "thiserror 2.0.18", "unicode-segmentation", - "unicode-truncate 2.0.1", - "unicode-width 0.2.0", + "unicode-truncate", + "unicode-width", ] [[package]] @@ -3152,13 +3092,13 @@ dependencies = [ "hashbrown 0.16.1", "indoc", "instability", - "itertools 0.14.0", + "itertools", "line-clipping", "ratatui-core", - "strum 0.27.2", + "strum", "time", "unicode-segmentation", - "unicode-width 0.2.0", + "unicode-width", ] [[package]] @@ -3751,35 +3691,13 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" -[[package]] -name = "strum" -version = "0.26.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" -dependencies = [ - "strum_macros 0.26.4", -] - [[package]] name = "strum" version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" dependencies = [ - "strum_macros 0.27.2", -] - -[[package]] -name = "strum_macros" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.117", + "strum_macros", ] [[package]] @@ -4366,7 +4284,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e766339aabad4528c3fccddf4acf03bc2b7ae6642def41e43c7af1a11f183122" dependencies = [ "ansi-to-tui", - "itertools 0.14.0", + "itertools", "pretty_assertions", "pulldown-cmark", "ratatui-core", @@ -4414,39 +4332,22 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" -[[package]] -name = "unicode-truncate" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" -dependencies = [ - "itertools 0.13.0", - "unicode-segmentation", - "unicode-width 0.1.14", -] - [[package]] name = "unicode-truncate" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16b380a1238663e5f8a691f9039c73e1cdae598a30e9855f541d29b08b53e9a5" dependencies = [ - "itertools 0.14.0", + "itertools", "unicode-segmentation", - "unicode-width 0.2.0", + "unicode-width", ] [[package]] name = "unicode-width" -version = "0.1.14" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" - -[[package]] -name = "unicode-width" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" [[package]] name = "unicode-xid" diff --git a/poc-agent/Cargo.toml b/poc-agent/Cargo.toml index 12f6a22..8b4a97b 100644 --- a/poc-agent/Cargo.toml +++ b/poc-agent/Cargo.toml @@ -32,3 +32,4 @@ figment = { version = "0.10", features = ["env"] } json5 = "0.4" clap = { version = "4", features = ["derive"] } tui-markdown = "0.3" +unicode-width = "0.2.2" diff --git a/poc-agent/src/tui.rs b/poc-agent/src/tui.rs index 13861a5..4d69034 100644 --- a/poc-agent/src/tui.rs +++ b/poc-agent/src/tui.rs @@ -9,6 +9,7 @@ // Uses ratatui + crossterm. The App struct holds all TUI state and // handles rendering. Input is processed from crossterm key events. +use unicode_width::UnicodeWidthStr; use crossterm::{ event::{EnableMouseCapture, DisableMouseCapture, KeyCode, KeyEvent, KeyModifiers, MouseEvent, MouseEventKind, MouseButton}, terminal::{self, EnterAlternateScreen, LeaveAlternateScreen}, @@ -16,6 +17,7 @@ use crossterm::{ }; use ratatui::{ backend::CrosstermBackend, + buffer::Buffer, layout::{Constraint, Direction, Layout, Rect}, style::{Color, Modifier, Style}, text::{Line, Span}, @@ -816,21 +818,36 @@ impl App { let input_para = Paragraph::new(input_lines).wrap(Wrap { trim: false }); frame.render_widget(input_para, input_area); - // Cursor position: walk through text up to cursor, tracking visual row/col - let cursor_text = format!("{}{}", prompt, &self.input[..self.cursor]); - let w = input_area.width as usize; - let cursor_lines: Vec<&str> = cursor_text.split('\n').collect(); - let n = cursor_lines.len(); - let mut visual_row = 0u16; - for line in &cursor_lines[..n - 1] { - visual_row += wrapped_height(line, w) as u16; + // Cursor position: scan the rendered buffer to find where the cursor should be. + // This matches ratatui's actual word wrapping instead of trying to simulate it. + let buffer = frame.buffer_mut(); + let mut char_count = 0usize; + let mut cursor_x = input_area.x; + let mut cursor_y = input_area.y; + + // Walk through the rendered buffer, counting characters until we reach the cursor position + for y in input_area.y..input_area.y + input_area.height { + for x in input_area.x..input_area.x + input_area.width { + if let Some(cell) = buffer.cell((x, y)) { + let symbol = cell.symbol(); + // Count visible characters (skip zero-width and empty) + if !symbol.is_empty() { + let width = symbol.width(); + if char_count + width > self.cursor { + // Found the cursor position + cursor_x = x; + cursor_y = y; + break; + } + char_count += width; + } + } + } + if cursor_x != input_area.x || cursor_y != input_area.y { + break; // Found it + } } - // Use unicode display width to match ratatui's wrapping - let last_width = ratatui::text::Line::raw(cursor_lines[n - 1]).width(); - let col = if w > 0 { last_width % w } else { last_width }; - visual_row += if w > 0 { (last_width / w) as u16 } else { 0 }; - let cursor_x = col as u16 + input_area.x; - let cursor_y = visual_row + input_area.y; + if cursor_y < input_area.y + input_area.height { frame.set_cursor_position((cursor_x, cursor_y)); } diff --git a/poc-memory/Cargo.toml b/poc-memory/Cargo.toml index 13411af..29e11f6 100644 --- a/poc-memory/Cargo.toml +++ b/poc-memory/Cargo.toml @@ -25,7 +25,7 @@ poc-agent = { path = "../poc-agent" } tokio = { version = "1", features = ["rt-multi-thread"] } redb = "2" log = "0.4" -ratatui = "0.29" +ratatui = "0.30" skillratings = "0.28" crossterm = { version = "0.28", features = ["event-stream"] }