diff --git a/README.md b/README.md index b555f66..2c0bb83 100644 --- a/README.md +++ b/README.md @@ -186,13 +186,18 @@ herdr server reload-config | `Ctrl+B Ctrl+A` | copy all annotations as Markdown, then archive them | | `Ctrl+B Ctrl+V` | paste all annotations into the focused agent's prompt without sending, then archive them | | `Ctrl+B Ctrl+S` | send all annotations as the focused agent's next message, then archive them | -| `Ctrl+B M` | manage · `e` edit the selected comment · `y` copy one · `c` copy all · `Shift+C` copy and archive · `Tab` archives (`y` copy · `u` restore · `d d` delete) | +| `Ctrl+B M` | manage · `Enter` jump to the pane it came from · `e` edit the selected comment · `y` copy one · `c` copy all · `Shift+C` copy and archive · `Tab` archives (`y` copy · `u` restore · `d d` delete) | In the manager's active view, `e` opens the selected comment in the editor. `Ctrl+S` saves it; `Esc` cancels. Editing leaves the selected text, capture metadata, and original ordering unchanged. If another pane saves that comment first, saving your stale draft is rejected and the draft stays open. To edit the latest version, cancel with `Esc`, then press `r` in the manager to reload. +`Enter` focuses the Herdr pane the selected annotation was captured from and closes the manager. +Agent panes are focused directly; for any other pane, Herdr focuses the tab that holds it. If the +pane has closed, or the annotation has no pane recorded, the manager says so and stays open. Herdr +cannot scroll a pane to the annotated text, so the jump stops at the pane. + Annotations are listed and handed off oldest first, in the order you wrote them, so the agent reads your feedback in the same order you went through the text. The Markdown uses Plannotator's feedback format: one numbered `## Annotation N` per note, headed with the workspace and tab it came diff --git a/docs/lite-testing.md b/docs/lite-testing.md index c3a647d..ea3a252 100644 --- a/docs/lite-testing.md +++ b/docs/lite-testing.md @@ -69,6 +69,8 @@ Deliberate re-records so far: - The manager's active view offers `e` to edit the selected comment. `Ctrl+S` saves without changing the annotation's selected text or provenance; `Esc` cancels. If another manager saves a different comment first, a stale save is rejected without closing the draft or overwriting the newer comment. +- The active view's footer adds `↵ jump`: `Enter` focuses the pane an annotation came from. No other + screen line changes. ## Everything else diff --git a/herdr-annotate.version b/herdr-annotate.version index ee1372d..7179039 100644 --- a/herdr-annotate.version +++ b/herdr-annotate.version @@ -1 +1 @@ -0.2.2 +0.2.3 diff --git a/herdr-plugin.toml b/herdr-plugin.toml index 0bd8c06..d7c30be 100644 --- a/herdr-plugin.toml +++ b/herdr-plugin.toml @@ -1,6 +1,6 @@ id = "annotate" name = "Annotate" -version = "0.9.3" +version = "0.9.4" min_herdr_version = "0.8.0" description = "Comment on terminal selections and copy annotations as agent context." platforms = ["linux", "macos", "windows"] diff --git a/links/herdr-plugin.toml b/links/herdr-plugin.toml index 59963b7..adf075a 100644 --- a/links/herdr-plugin.toml +++ b/links/herdr-plugin.toml @@ -15,7 +15,7 @@ id = "annotate-links" name = "Annotate Links" -version = "0.9.3" +version = "0.9.4" min_herdr_version = "0.8.0" description = "Open Ctrl-clicked file:// Markdown links in Annotate (needs the Annotate Full plugin)." platforms = ["linux", "macos", "windows"] diff --git a/lite/herdr-plugin.toml b/lite/herdr-plugin.toml index 7f11d87..fd386ba 100644 --- a/lite/herdr-plugin.toml +++ b/lite/herdr-plugin.toml @@ -8,7 +8,7 @@ id = "annotate" name = "Annotate" -version = "0.9.3" +version = "0.9.4" min_herdr_version = "0.8.0" description = "Comment on terminal selections and copy annotations as agent context." platforms = ["linux", "macos", "windows"] diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 2833e39..2f885c7 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -505,7 +505,7 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "herdr-annotate" -version = "0.2.2" +version = "0.2.3" dependencies = [ "chrono", "ratatui", diff --git a/rust/Cargo.toml b/rust/Cargo.toml index af4e3ac..d8c3643 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "herdr-annotate" -version = "0.2.2" +version = "0.2.3" edition = "2024" rust-version = "1.96" license = "MIT" diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 04b4eab..d38c1c1 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -13,6 +13,7 @@ pub mod manager; pub mod manager_copy; pub mod pane_clipboard; pub mod paths; +pub mod source_jump; pub mod store; pub mod types; pub mod width; diff --git a/rust/src/manager.rs b/rust/src/manager.rs index ebaec10..8958eac 100644 --- a/rust/src/manager.rs +++ b/rust/src/manager.rs @@ -19,9 +19,11 @@ use crate::archive_workflow::{ use crate::clipboard::write_clipboard; use crate::editor::{EditorApp, now_iso_for_manager}; use crate::format::{sanitize_terminal_text, wrap_text}; +use crate::herdr::run_herdr_output; use crate::manager_copy::{ManagerCopyOutcome, copy_annotations}; use crate::pane_clipboard::{emit_to_terminal, write_pane_clipboard}; use crate::paths::state_dir; +use crate::source_jump::{JumpOutcome, jump_to_source}; use crate::store::{ append_archived_set, capture_order_annotations, load_annotations, load_archived_sets, merge_annotations, newest_first_archived_sets, remove_annotations_by_id, @@ -56,9 +58,13 @@ pub struct ManagerApp { status: String, confirmation: Confirmation, editing: Option, + herdr: HerdrCall, quit: bool, } +/// Run one Herdr CLI command, returning stdout or stderr; replaced by a fake in tests. +type HerdrCall = fn(&[String]) -> Result; + impl ManagerApp { /// Load both stores and start in the active-annotation view. pub fn load(dir: PathBuf) -> Self { @@ -72,6 +78,7 @@ impl ManagerApp { status: String::new(), confirmation: Confirmation::None, editing: None, + herdr: run_herdr_output, quit: false, }; app.reload_active(); @@ -427,7 +434,7 @@ impl ManagerApp { } match self.view { ManagerView::Active => { - "j/k · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q" + "j/k · ↵ jump · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q" .to_owned() } ManagerView::Archives => { @@ -493,6 +500,7 @@ impl ManagerApp { } KeyCode::Char('C') => self.copy_and_archive(), KeyCode::Char('e') => self.edit_selected_annotation(), + KeyCode::Enter => self.jump_to_selected_source(), KeyCode::Char('y') => { let items = self .annotations @@ -587,6 +595,21 @@ impl ManagerApp { self.editing = Some(EditorApp::edit(annotation)); } + /// Focus the Herdr pane the selected annotation came from, closing the manager on success. + fn jump_to_selected_source(&mut self) { + let Some(context) = self + .annotations + .get(self.active_selected) + .map(|item| item.context.clone()) + else { + return; + }; + match jump_to_source(&context, self.herdr) { + JumpOutcome::Focused => self.quit = true, + JumpOutcome::Unavailable(message) => self.status = message, + } + } + fn handle_edit_key(&mut self, key: KeyEvent) { let Some(editor) = self.editing.as_mut() else { return; @@ -882,6 +905,7 @@ mod tests { assert!(frame.iter().any(|row| row.contains("oldest first"))); assert!(frame.iter().any(|row| row.contains("comment one"))); assert!(frame.iter().any(|row| row.contains("e edit"))); + assert!(frame.iter().any(|row| row.contains("↵ jump"))); assert!(frame.iter().any(|row| row.contains("C copy+archive"))); let _ = fs::remove_dir_all(dir); } @@ -1178,6 +1202,124 @@ mod tests { let _ = fs::remove_dir_all(dir); } + static HERDR_CALLS: AtomicUsize = AtomicUsize::new(0); + + fn herdr_must_not_run(_: &[String]) -> Result { + HERDR_CALLS.fetch_add(1, Ordering::Relaxed); + Err("unexpected herdr call".to_owned()) + } + + fn herdr_without_the_pane(args: &[String]) -> Result { + match args.first().map(String::as_str) { + Some("agent") => Err( + r#"{"error":{"code":"agent_not_found","message":"agent target w1:p9 not found"}}"# + .to_owned(), + ), + Some("pane") => Err( + r#"{"error":{"code":"pane_not_found","message":"pane w1:p9 not found"}}"# + .to_owned(), + ), + _ => Err(format!("unexpected herdr call: {args:?}")), + } + } + + fn herdr_focuses(args: &[String]) -> Result { + if args == ["agent", "focus", "w1:p2"] { + Ok("{}".to_owned()) + } else { + Err(format!("unexpected herdr call: {args:?}")) + } + } + + fn annotation_from_pane(id: &str, pane: &str) -> Annotation { + let mut item = annotation(id); + item.context.focused_pane_id = Some(pane.to_owned()); + item + } + + #[test] + fn jump_without_a_source_pane_id_reports_it_and_stays_open() { + let dir = directory(); + let original = annotation("one"); + append_annotation(&dir, &original).expect("annotation"); + let mut app = ManagerApp::load(dir.clone()); + app.herdr = herdr_must_not_run; + + let calls = HERDR_CALLS.load(Ordering::Relaxed); + + app.handle_key(KeyEvent::from(KeyCode::Enter)); + + assert!(!app.quit); + assert_eq!(HERDR_CALLS.load(Ordering::Relaxed), calls); + let frame = rows(&app); + assert!( + frame + .iter() + .any(|row| row.contains("This annotation has no source pane to jump to.")) + ); + assert!(frame.iter().any(|row| row.contains("Annotations (1)"))); + assert_eq!(load_annotations(&dir).expect("unchanged"), [original]); + let _ = fs::remove_dir_all(dir); + } + + #[test] + fn jump_to_a_closed_source_pane_reports_it_and_stays_open() { + let dir = directory(); + let original = annotation_from_pane("one", "w1:p9"); + append_annotation(&dir, &original).expect("annotation"); + let mut app = ManagerApp::load(dir.clone()); + app.herdr = herdr_without_the_pane; + + app.handle_key(KeyEvent::from(KeyCode::Enter)); + + assert!(!app.quit); + let frame = rows(&app); + assert!( + frame + .iter() + .any(|row| row.contains("The source pane no longer exists.")) + ); + assert_eq!(load_annotations(&dir).expect("unchanged"), [original]); + let _ = fs::remove_dir_all(dir); + } + + #[test] + fn jump_to_the_source_pane_closes_the_manager() { + let dir = directory(); + append_annotation(&dir, &annotation_from_pane("one", "w1:p2")).expect("annotation"); + let mut app = ManagerApp::load(dir.clone()); + app.herdr = herdr_focuses; + + app.handle_key(KeyEvent::from(KeyCode::Enter)); + + assert!(app.quit); + let _ = fs::remove_dir_all(dir); + } + + #[test] + fn jump_is_ignored_without_an_active_selection() { + let dir = directory(); + let mut app = ManagerApp::load(dir.clone()); + app.herdr = herdr_must_not_run; + app.handle_key(KeyEvent::from(KeyCode::Enter)); + assert!(!app.quit); + append_archived_set( + &dir, + &ArchivedAnnotationSet { + version: 1, + id: "archive-one".to_owned(), + archived_at: "2026-08-26T23:32:00Z".to_owned(), + annotations: vec![annotation_from_pane("one", "w1:p2")], + }, + ) + .expect("archive"); + app.handle_key(KeyEvent::from(KeyCode::Tab)); + app.handle_key(KeyEvent::from(KeyCode::Enter)); + assert!(!app.quit); + assert!(rows(&app).iter().any(|row| row.contains("Archives (1)"))); + let _ = fs::remove_dir_all(dir); + } + #[test] fn archive_frame_and_delete_confirmation_render_headlessly() { let dir = directory(); diff --git a/rust/src/source_jump.rs b/rust/src/source_jump.rs new file mode 100644 index 0000000..1080b6f --- /dev/null +++ b/rust/src/source_jump.rs @@ -0,0 +1,186 @@ +//! Focus the Herdr pane an annotation was captured from. +//! +//! Herdr's CLI focuses a pane by id only through `herdr agent focus`, which accepts agent panes. +//! For any other pane the plugin focuses the pane's tab instead, which puts the pane on screen. +//! Herdr cannot address a position in a pane's scrollback, so the jump stops at the pane. + +use serde_json::Value; + +use crate::agent_delivery::parse_herdr_error; +use crate::types::InvocationContext; + +/// What a jump did in Herdr. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum JumpOutcome { + /// The source pane, or the tab that holds it, is now focused. + Focused, + /// Nothing was focused; the message says why. + Unavailable(String), +} + +/// Focus the pane recorded in `context`, calling Herdr through `herdr`. +/// +/// `herdr` takes the argument list and returns stdout, or stderr on failure. An annotation +/// without a pane id makes no Herdr call at all. +pub fn jump_to_source( + context: &InvocationContext, + mut herdr: impl FnMut(&[String]) -> Result, +) -> JumpOutcome { + let Some(pane) = context + .focused_pane_id + .as_deref() + .filter(|id| !id.trim().is_empty() && !id.starts_with('-')) + else { + return JumpOutcome::Unavailable( + "This annotation has no source pane to jump to.".to_owned(), + ); + }; + if herdr(&args(&["agent", "focus", pane])).is_ok() { + return JumpOutcome::Focused; + } + let record = match herdr(&args(&["pane", "get", pane])) { + Ok(stdout) => stdout, + Err(stderr) => return JumpOutcome::Unavailable(failure(&stderr)), + }; + let tab = serde_json::from_str::(&record) + .ok() + .and_then(|value| { + value + .pointer("/result/pane/tab_id") + .and_then(Value::as_str) + .map(str::to_owned) + }) + .filter(|id| !id.is_empty() && !id.starts_with('-')); + let Some(tab) = tab else { + return JumpOutcome::Unavailable("Herdr did not report the source pane's tab.".to_owned()); + }; + match herdr(&args(&["tab", "focus", &tab])) { + Ok(_) => JumpOutcome::Focused, + Err(stderr) => JumpOutcome::Unavailable(failure(&stderr)), + } +} + +fn args(values: &[&str]) -> Vec { + values.iter().map(|value| (*value).to_owned()).collect() +} + +/// Say in plain words why Herdr could not focus the source, without its JSON. +fn failure(stderr: &str) -> String { + let error = parse_herdr_error(stderr); + let message = error.message.trim_end_matches('.'); + match error.code.as_deref() { + Some("pane_not_found" | "tab_not_found") => "The source pane no longer exists.".to_owned(), + Some(code) => format!("Herdr refused ({code}): {message}."), + None if message.is_empty() => "Herdr failed without saying why.".to_owned(), + None => format!("Herdr failed: {message}."), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn context(pane: Option<&str>) -> InvocationContext { + InvocationContext { + focused_pane_id: pane.map(str::to_owned), + ..InvocationContext::default() + } + } + + fn not_found(code: &str, target: &str) -> String { + format!(r#"{{"error":{{"code":"{code}","message":"{target} not found"}},"id":"cli"}}"#) + } + + #[test] + fn annotation_without_a_pane_id_makes_no_herdr_call() { + for pane in [None, Some(""), Some(" "), Some("--help")] { + let mut calls = Vec::new(); + let outcome = jump_to_source(&context(pane), |args: &[String]| { + calls.push(args.to_vec()); + Ok(String::new()) + }); + assert_eq!( + outcome, + JumpOutcome::Unavailable( + "This annotation has no source pane to jump to.".to_owned() + ) + ); + assert_eq!(calls, Vec::>::new()); + } + } + + #[test] + fn agent_pane_is_focused_directly() { + let mut calls = Vec::new(); + let outcome = jump_to_source(&context(Some("w1:p2")), |args: &[String]| { + calls.push(args.join(" ")); + Ok("{}".to_owned()) + }); + assert_eq!(outcome, JumpOutcome::Focused); + assert_eq!(calls, ["agent focus w1:p2"]); + } + + #[test] + fn missing_pane_reports_it_and_focuses_nothing() { + let mut calls = Vec::new(); + let outcome = jump_to_source(&context(Some("w1:p9")), |args: &[String]| { + calls.push(args.join(" ")); + match args.first().map(String::as_str) { + Some("agent") => Err(not_found("agent_not_found", "agent target w1:p9")), + _ => Err(not_found("pane_not_found", "pane w1:p9")), + } + }); + assert_eq!( + outcome, + JumpOutcome::Unavailable("The source pane no longer exists.".to_owned()) + ); + assert_eq!(calls, ["agent focus w1:p9", "pane get w1:p9"]); + } + + #[test] + fn other_pane_focuses_its_current_tab() { + let mut calls = Vec::new(); + let outcome = jump_to_source(&context(Some("w1:p3")), |args: &[String]| { + calls.push(args.join(" ")); + match args.first().map(String::as_str) { + Some("agent") => Err(not_found("agent_not_found", "agent target w1:p3")), + Some("pane") => Ok( + r#"{"id":"cli:pane:get","result":{"pane":{"pane_id":"w1:p3","tab_id":"w1:t2","workspace_id":"w1"},"type":"pane_info"}}"# + .to_owned(), + ), + _ => Ok("{}".to_owned()), + } + }); + assert_eq!(outcome, JumpOutcome::Focused); + assert_eq!( + calls, + ["agent focus w1:p3", "pane get w1:p3", "tab focus w1:t2"] + ); + } + + #[test] + fn herdr_failures_are_reported_without_json() { + let outcome = jump_to_source(&context(Some("w1:p2")), |_: &[String]| { + Err( + r#"{"id":"cli:pane:get","error":{"code":"server_not_running","message":"no herdr server is running."}}"# + .to_owned(), + ) + }); + assert_eq!( + outcome, + JumpOutcome::Unavailable( + "Herdr refused (server_not_running): no herdr server is running.".to_owned() + ) + ); + let outcome = jump_to_source(&context(Some("w1:p2")), |args: &[String]| { + match args.first().map(String::as_str) { + Some("pane") => Ok("not json".to_owned()), + _ => Err(String::new()), + } + }); + assert_eq!( + outcome, + JumpOutcome::Unavailable("Herdr did not report the source pane's tab.".to_owned()) + ); + } +} diff --git a/scripts/lite-goldens/screen.manager.all-views.json b/scripts/lite-goldens/screen.manager.all-views.json index 5b7aebd..ab7e0d4 100644 --- a/scripts/lite-goldens/screen.manager.all-views.json +++ b/scripts/lite-goldens/screen.manager.all-views.json @@ -42,7 +42,7 @@ " │ ", " │ api 한· / server · 8/9/2026, 10:11:13 AM ", " │ ", - " j/k · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " + " j/k · ↵ jump · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " ], "screen.manager.all-views.screen.active-arrow-up": [ " Annotations (3) oldest first ", @@ -72,7 +72,7 @@ " │ ", " │ api 한· / server · 8/8/2026, 12:00:01 AM ", " │ ", - " j/k · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " + " j/k · ↵ jump · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " ], "screen.manager.all-views.screen.active-clear": [ " Annotations (0) oldest first ", @@ -132,7 +132,7 @@ " │ ", " │ api 한· / server · 8/9/2026, 10:11:13 AM ", " │ ", - " j/k · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " + " j/k · ↵ jump · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " ], "screen.manager.all-views.screen.active-clear-confirm": [ " Annotations (2) oldest first ", @@ -252,7 +252,7 @@ " │ ", " │ api 한· / server · 8/9/2026, 10:11:13 AM ", " │ ", - " j/k · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " + " j/k · ↵ jump · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " ], "screen.manager.all-views.screen.active-k": [ " Annotations (3) oldest first ", @@ -282,7 +282,7 @@ " │ ", " │ api 한· / server · 8/8/2026, 12:00:01 AM ", " │ ", - " j/k · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " + " j/k · ↵ jump · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " ], "screen.manager.all-views.screen.active-quit": [ " ", @@ -402,7 +402,7 @@ " │ ", " │ ", " │ ", - " j/k · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " + " j/k · ↵ jump · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " ], "screen.manager.all-views.screen.archives-C-ignored": [ " Archives (2) newest first ", @@ -822,7 +822,7 @@ " │ ", " │ api 한· / server · 8/9/2026, 10:11:13 AM ", " │ ", - " j/k · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " + " j/k · ↵ jump · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " ], "screen.manager.all-views.screen.initial": [ " Annotations (3) oldest first ", @@ -852,7 +852,7 @@ " │ ", " │ api 한· / server · 8/8/2026, 12:00:01 AM ", " │ ", - " j/k · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " + " j/k · ↵ jump · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " ], "screen.manager.all-views.state": [ "[", diff --git a/scripts/lite-goldens/screen.manager.control-c-active.json b/scripts/lite-goldens/screen.manager.control-c-active.json index 3da9b6e..629a6ed 100644 --- a/scripts/lite-goldens/screen.manager.control-c-active.json +++ b/scripts/lite-goldens/screen.manager.control-c-active.json @@ -69,6 +69,6 @@ " │ ", " │ api 한· / server · 8/8/2026, 12:00:01 AM ", " │ ", - " j/k · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " + " j/k · ↵ jump · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " ] } diff --git a/scripts/lite-goldens/screen.manager.control-c-archives.json b/scripts/lite-goldens/screen.manager.control-c-archives.json index a7979e0..110b0f2 100644 --- a/scripts/lite-goldens/screen.manager.control-c-archives.json +++ b/scripts/lite-goldens/screen.manager.control-c-archives.json @@ -69,7 +69,7 @@ " │ ", " │ api 한· / server · 8/8/2026, 12:00:01 AM ", " │ ", - " j/k · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " + " j/k · ↵ jump · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " ], "screen.manager.control-c-archives.screen.to-archives": [ " Archives (2) newest first ", diff --git a/scripts/lite-goldens/screen.manager.empty-actions.json b/scripts/lite-goldens/screen.manager.empty-actions.json index 17c6986..aa1d36c 100644 --- a/scripts/lite-goldens/screen.manager.empty-actions.json +++ b/scripts/lite-goldens/screen.manager.empty-actions.json @@ -222,7 +222,7 @@ " │ ", " │ ", " │ ", - " j/k · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " + " j/k · ↵ jump · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " ], "screen.manager.empty-actions.screen.quit": [ " ", diff --git a/scripts/lite-goldens/screen.manager.escape-active.json b/scripts/lite-goldens/screen.manager.escape-active.json index 4b26c2a..70be3fa 100644 --- a/scripts/lite-goldens/screen.manager.escape-active.json +++ b/scripts/lite-goldens/screen.manager.escape-active.json @@ -69,6 +69,6 @@ " │ ", " │ api 한· / server · 8/8/2026, 12:00:01 AM ", " │ ", - " j/k · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " + " j/k · ↵ jump · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " ] } diff --git a/scripts/lite-goldens/screen.manager.escape-exit.json b/scripts/lite-goldens/screen.manager.escape-exit.json index 8f75224..5a7c477 100644 --- a/scripts/lite-goldens/screen.manager.escape-exit.json +++ b/scripts/lite-goldens/screen.manager.escape-exit.json @@ -69,7 +69,7 @@ " │ ", " │ api 한· / server · 8/8/2026, 12:00:01 AM ", " │ ", - " j/k · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " + " j/k · ↵ jump · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " ], "screen.manager.escape-exit.screen.to-archives": [ " Archives (2) newest first ", diff --git a/scripts/lite-goldens/screen.manager.q-archives.json b/scripts/lite-goldens/screen.manager.q-archives.json index d286ac4..198c887 100644 --- a/scripts/lite-goldens/screen.manager.q-archives.json +++ b/scripts/lite-goldens/screen.manager.q-archives.json @@ -69,7 +69,7 @@ " │ ", " │ api 한· / server · 8/8/2026, 12:00:01 AM ", " │ ", - " j/k · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " + " j/k · ↵ jump · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " ], "screen.manager.q-archives.screen.to-archives": [ " Archives (2) newest first ", diff --git a/scripts/lite-goldens/screen.manager.sighup.json b/scripts/lite-goldens/screen.manager.sighup.json index 5901831..fbcb823 100644 --- a/scripts/lite-goldens/screen.manager.sighup.json +++ b/scripts/lite-goldens/screen.manager.sighup.json @@ -39,7 +39,7 @@ " │ ", " │ api 한· / server · 8/8/2026, 12:00:01 AM ", " │ ", - " j/k · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " + " j/k · ↵ jump · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " ], "screen.manager.sighup.screen.sighup": [ " ", diff --git a/scripts/lite-goldens/store.manager.active-c-success.json b/scripts/lite-goldens/store.manager.active-c-success.json index e5c77e6..aeec3f4 100644 --- a/scripts/lite-goldens/store.manager.active-c-success.json +++ b/scripts/lite-goldens/store.manager.active-c-success.json @@ -93,7 +93,7 @@ " │ ", " │ api 한· / server · 8/8/2026, 12:00:01 AM ", " │ ", - " j/k · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " + " j/k · ↵ jump · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " ], "store.manager.active-c-success.state": [ "[", diff --git a/scripts/lite-goldens/store.manager.active-y-success.json b/scripts/lite-goldens/store.manager.active-y-success.json index d3d28c8..0d2c503 100644 --- a/scripts/lite-goldens/store.manager.active-y-success.json +++ b/scripts/lite-goldens/store.manager.active-y-success.json @@ -85,7 +85,7 @@ " │ ", " │ api 한· / server · 8/8/2026, 12:00:01 AM ", " │ ", - " j/k · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " + " j/k · ↵ jump · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " ], "store.manager.active-y-success.state": [ "[", diff --git a/scripts/lite-goldens/store.manager.archives-y-success.json b/scripts/lite-goldens/store.manager.archives-y-success.json index 1a358e4..2c4ba3e 100644 --- a/scripts/lite-goldens/store.manager.archives-y-success.json +++ b/scripts/lite-goldens/store.manager.archives-y-success.json @@ -84,7 +84,7 @@ " │ ", " │ api 한· / server · 8/8/2026, 12:00:01 AM ", " │ ", - " j/k · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " + " j/k · ↵ jump · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " ], "store.manager.archives-y-success.screen.to-archives": [ " Archives (2) newest first ", diff --git a/scripts/lite-goldens/store.manager.copy-archive.json b/scripts/lite-goldens/store.manager.copy-archive.json index 3f672c8..e8709e1 100644 --- a/scripts/lite-goldens/store.manager.copy-archive.json +++ b/scripts/lite-goldens/store.manager.copy-archive.json @@ -93,7 +93,7 @@ " │ ", " │ api 한· / server · 8/8/2026, 12:00:01 AM ", " │ ", - " j/k · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " + " j/k · ↵ jump · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " ], "store.manager.copy-archive.state": [ "[", diff --git a/scripts/lite-goldens/store.manager.edit-save.json b/scripts/lite-goldens/store.manager.edit-save.json index 68c22d3..229d63d 100644 --- a/scripts/lite-goldens/store.manager.edit-save.json +++ b/scripts/lite-goldens/store.manager.edit-save.json @@ -99,7 +99,7 @@ " │ ", " │ api 한· / server · 8/8/2026, 12:00:01 AM ", " │ ", - " j/k · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " + " j/k · ↵ jump · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " ], "store.manager.edit-save.screen.quit": [ " ", diff --git a/scripts/lite-goldens/store.manager.osc52-remote-copy-all.json b/scripts/lite-goldens/store.manager.osc52-remote-copy-all.json index 97ce4b8..633e10d 100644 --- a/scripts/lite-goldens/store.manager.osc52-remote-copy-all.json +++ b/scripts/lite-goldens/store.manager.osc52-remote-copy-all.json @@ -93,7 +93,7 @@ " │ ", " │ api 한· / server · 8/8/2026, 12:00:01 AM ", " │ ", - " j/k · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " + " j/k · ↵ jump · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " ], "store.manager.osc52-remote-copy-all.state": [ "[", diff --git a/scripts/lite-goldens/store.manager.osc52-remote-copy-archive.json b/scripts/lite-goldens/store.manager.osc52-remote-copy-archive.json index e9ff851..38c5c79 100644 --- a/scripts/lite-goldens/store.manager.osc52-remote-copy-archive.json +++ b/scripts/lite-goldens/store.manager.osc52-remote-copy-archive.json @@ -93,7 +93,7 @@ " │ ", " │ api 한· / server · 8/8/2026, 12:00:01 AM ", " │ ", - " j/k · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " + " j/k · ↵ jump · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " ], "store.manager.osc52-remote-copy-archive.state": [ "[", diff --git a/scripts/lite-goldens/store.manager.osc52-remote-copy.json b/scripts/lite-goldens/store.manager.osc52-remote-copy.json index 6dcd19b..16f459a 100644 --- a/scripts/lite-goldens/store.manager.osc52-remote-copy.json +++ b/scripts/lite-goldens/store.manager.osc52-remote-copy.json @@ -85,7 +85,7 @@ " │ ", " │ api 한· / server · 8/8/2026, 12:00:01 AM ", " │ ", - " j/k · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " + " j/k · ↵ jump · e edit · y copy · c all · C copy+archive · d delete · D clear · Tab archives · q " ], "store.manager.osc52-remote-copy.state": [ "[", diff --git a/scripts/test-windows-full-manifest.py b/scripts/test-windows-full-manifest.py index 5e4f096..88806d8 100644 --- a/scripts/test-windows-full-manifest.py +++ b/scripts/test-windows-full-manifest.py @@ -185,8 +185,8 @@ def check_distributed(path: Path, version_path: Path, native_version_path: Path) check_native_lite(path, manifest) if version_path.read_text(encoding="utf-8").strip() != "0.9.4": fail(version_path, "plannotator-tui.version is not 0.9.4") - if native_version_path.read_text(encoding="utf-8").strip() != "0.2.2": - fail(native_version_path, "herdr-annotate.version is not 0.2.2") + if native_version_path.read_text(encoding="utf-8").strip() != "0.2.3": + fail(native_version_path, "herdr-annotate.version is not 0.2.3") build_entries = builds(path, manifest) if len(build_entries) != 3: diff --git a/windows-full/herdr-plugin.toml b/windows-full/herdr-plugin.toml index b60e496..5fd8bb1 100644 --- a/windows-full/herdr-plugin.toml +++ b/windows-full/herdr-plugin.toml @@ -21,7 +21,7 @@ id = "annotate" name = "Annotate" -version = "0.9.3" +version = "0.9.4" min_herdr_version = "0.9.0" description = "Comment on terminal selections and copy annotations as agent context." platforms = ["windows"]