anveesa 0.3.3 → 0.3.5
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.
- package/Cargo.lock +1 -1
- package/Cargo.toml +1 -1
- package/package.json +1 -1
- package/src/lib.rs +90 -16
package/Cargo.lock
CHANGED
package/Cargo.toml
CHANGED
package/package.json
CHANGED
package/src/lib.rs
CHANGED
|
@@ -143,15 +143,22 @@ async fn run_interactive(options: AskOptions) -> Result<()> {
|
|
|
143
143
|
|
|
144
144
|
loop {
|
|
145
145
|
print_input_separator(is_tty, width);
|
|
146
|
-
let line
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
146
|
+
let (line, ctrl_v_image) =
|
|
147
|
+
match read_prompt_line(&label, width, &mut paste_count, images_available) {
|
|
148
|
+
Ok(PromptRead::Line(line, img)) => (line, img),
|
|
149
|
+
Ok(PromptRead::Interrupted) => continue,
|
|
150
|
+
Ok(PromptRead::Eof) => {
|
|
151
|
+
println!();
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
Err(error) => return Err(error).context("failed to read interactive prompt"),
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
// Ctrl+V image takes precedence over a previously pending image.
|
|
158
|
+
if let Some(img) = ctrl_v_image {
|
|
159
|
+
last_image_fp = Some(image_fingerprint(&img));
|
|
160
|
+
pending_image = Some(img);
|
|
161
|
+
}
|
|
155
162
|
|
|
156
163
|
let prompt = line.trim().to_string();
|
|
157
164
|
if prompt.is_empty() {
|
|
@@ -1313,7 +1320,7 @@ fn print_session_header(
|
|
|
1313
1320
|
}
|
|
1314
1321
|
|
|
1315
1322
|
enum PromptRead {
|
|
1316
|
-
Line(String),
|
|
1323
|
+
Line(String, Option<ImageAttachment>),
|
|
1317
1324
|
Interrupted,
|
|
1318
1325
|
Eof,
|
|
1319
1326
|
}
|
|
@@ -1389,6 +1396,24 @@ impl PromptBuffer {
|
|
|
1389
1396
|
self.segments.pop();
|
|
1390
1397
|
}
|
|
1391
1398
|
}
|
|
1399
|
+
|
|
1400
|
+
/// Ctrl+U / Cmd+Delete — erase the entire line.
|
|
1401
|
+
fn clear_all(&mut self) {
|
|
1402
|
+
self.full.clear();
|
|
1403
|
+
self.display.clear();
|
|
1404
|
+
self.segments.clear();
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
/// Ctrl+W / Option+Delete — erase the last word (whitespace-delimited).
|
|
1408
|
+
fn pop_word(&mut self) {
|
|
1409
|
+
// Trim trailing whitespace first, then remove up to the previous whitespace boundary.
|
|
1410
|
+
while self.full.ends_with(' ') {
|
|
1411
|
+
self.pop_last();
|
|
1412
|
+
}
|
|
1413
|
+
while !self.full.is_empty() && !self.full.ends_with(' ') {
|
|
1414
|
+
self.pop_last();
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1392
1417
|
}
|
|
1393
1418
|
|
|
1394
1419
|
#[cfg(unix)]
|
|
@@ -1448,13 +1473,28 @@ impl RawPromptMode {
|
|
|
1448
1473
|
}
|
|
1449
1474
|
}
|
|
1450
1475
|
|
|
1451
|
-
fn read_prompt_line(
|
|
1476
|
+
fn read_prompt_line(
|
|
1477
|
+
label: &str,
|
|
1478
|
+
width: usize,
|
|
1479
|
+
paste_count: &mut usize,
|
|
1480
|
+
images_available: bool,
|
|
1481
|
+
) -> Result<PromptRead> {
|
|
1452
1482
|
let _raw_mode = RawPromptMode::enter()?;
|
|
1453
1483
|
let mut input = io::stdin().lock();
|
|
1454
1484
|
let mut buffer = PromptBuffer::default();
|
|
1455
1485
|
let mut display_rows = 1usize;
|
|
1486
|
+
let mut ctrl_v_image: Option<ImageAttachment> = None;
|
|
1487
|
+
|
|
1488
|
+
// Compose the visible prompt label, optionally prefixed with an image indicator.
|
|
1489
|
+
let effective_label = |img: &Option<ImageAttachment>| -> String {
|
|
1490
|
+
if img.is_some() {
|
|
1491
|
+
format!("\x1b[2m[📎]\x1b[0m {label}")
|
|
1492
|
+
} else {
|
|
1493
|
+
label.to_string()
|
|
1494
|
+
}
|
|
1495
|
+
};
|
|
1456
1496
|
|
|
1457
|
-
print!("{
|
|
1497
|
+
print!("{}", effective_label(&ctrl_v_image));
|
|
1458
1498
|
io::stdout().flush().context("failed to write prompt")?;
|
|
1459
1499
|
|
|
1460
1500
|
loop {
|
|
@@ -1466,29 +1506,63 @@ fn read_prompt_line(label: &str, width: usize, paste_count: &mut usize) -> Resul
|
|
|
1466
1506
|
match byte[0] {
|
|
1467
1507
|
b'\r' | b'\n' => {
|
|
1468
1508
|
println!();
|
|
1469
|
-
return Ok(PromptRead::Line(buffer.full));
|
|
1509
|
+
return Ok(PromptRead::Line(buffer.full, ctrl_v_image));
|
|
1470
1510
|
}
|
|
1471
1511
|
3 => {
|
|
1512
|
+
// Ctrl+C — discard any pasted image too
|
|
1513
|
+
ctrl_v_image = None;
|
|
1472
1514
|
println!("^C");
|
|
1473
1515
|
return Ok(PromptRead::Interrupted);
|
|
1474
1516
|
}
|
|
1475
1517
|
4 if buffer.is_empty() => return Ok(PromptRead::Eof),
|
|
1476
1518
|
8 | 127 => {
|
|
1519
|
+
// Backspace
|
|
1477
1520
|
buffer.pop_last();
|
|
1478
|
-
|
|
1521
|
+
let lbl = effective_label(&ctrl_v_image);
|
|
1522
|
+
display_rows = redraw_prompt_line(&lbl, &buffer.display, display_rows, width)?;
|
|
1523
|
+
}
|
|
1524
|
+
21 => {
|
|
1525
|
+
// Ctrl+U / Cmd+Delete — erase entire line
|
|
1526
|
+
buffer.clear_all();
|
|
1527
|
+
let lbl = effective_label(&ctrl_v_image);
|
|
1528
|
+
display_rows = redraw_prompt_line(&lbl, &buffer.display, display_rows, width)?;
|
|
1529
|
+
}
|
|
1530
|
+
22 if images_available => {
|
|
1531
|
+
// Ctrl+V — paste image from clipboard
|
|
1532
|
+
match grab_clipboard_image() {
|
|
1533
|
+
Some(img) => {
|
|
1534
|
+
ctrl_v_image = Some(img);
|
|
1535
|
+
let lbl = effective_label(&ctrl_v_image);
|
|
1536
|
+
display_rows =
|
|
1537
|
+
redraw_prompt_line(&lbl, &buffer.display, display_rows, width)?;
|
|
1538
|
+
}
|
|
1539
|
+
None => {
|
|
1540
|
+
// No image in clipboard — ring the bell
|
|
1541
|
+
print!("\x07");
|
|
1542
|
+
let _ = io::stdout().flush();
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
23 => {
|
|
1547
|
+
// Ctrl+W / Option+Delete — erase last word
|
|
1548
|
+
buffer.pop_word();
|
|
1549
|
+
let lbl = effective_label(&ctrl_v_image);
|
|
1550
|
+
display_rows = redraw_prompt_line(&lbl, &buffer.display, display_rows, width)?;
|
|
1479
1551
|
}
|
|
1480
1552
|
0x1b => {
|
|
1481
1553
|
let sequence = read_escape_sequence(&mut input)?;
|
|
1482
1554
|
if sequence == b"[200~" {
|
|
1483
1555
|
let paste = normalize_pasted_text(read_bracketed_paste(&mut input)?);
|
|
1484
1556
|
push_paste(&mut buffer, paste, paste_count);
|
|
1485
|
-
|
|
1557
|
+
let lbl = effective_label(&ctrl_v_image);
|
|
1558
|
+
display_rows = redraw_prompt_line(&lbl, &buffer.display, display_rows, width)?;
|
|
1486
1559
|
}
|
|
1487
1560
|
}
|
|
1488
1561
|
byte if byte >= 0x20 && byte != 0x7f => {
|
|
1489
1562
|
if let Some(ch) = read_utf8_char(byte, &mut input)? {
|
|
1490
1563
|
buffer.push_text(ch.encode_utf8(&mut [0; 4]));
|
|
1491
|
-
|
|
1564
|
+
let lbl = effective_label(&ctrl_v_image);
|
|
1565
|
+
display_rows = redraw_prompt_line(&lbl, &buffer.display, display_rows, width)?;
|
|
1492
1566
|
}
|
|
1493
1567
|
}
|
|
1494
1568
|
_ => {}
|