aboutsummaryrefslogtreecommitdiff
path: root/crates/dinggerd/src/hooks.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/dinggerd/src/hooks.rs')
-rw-r--r--crates/dinggerd/src/hooks.rs48
1 files changed, 48 insertions, 0 deletions
diff --git a/crates/dinggerd/src/hooks.rs b/crates/dinggerd/src/hooks.rs
index 0074544..072735f 100644
--- a/crates/dinggerd/src/hooks.rs
+++ b/crates/dinggerd/src/hooks.rs
@@ -27,6 +27,9 @@
//! `{"method":"agents.reload_outdated"}`, and the watcher reloads each agent
//! on an old extension once it is idle and off screen.
//!
+//! It also carries the `draft` in the agent's input box ("" for none), for
+//! marking a draft you left behind; never logged.
+//!
//! Each line is answered with `{"ok":true}`.
//!
//! `{"method":"poke"}` only wakes the watcher; `dingger focus` and tmux's
@@ -69,6 +72,8 @@ struct PaneReports {
/// The version of the extension that reported, and its control socket.
extension: Option<String>,
control: Option<String>,
+ /// The draft in the agent's input box, as one line ("" for none).
+ draft: Option<String>,
/// When the last report arrived.
at: Option<Instant>,
}
@@ -165,6 +170,8 @@ struct HookParams {
extension: Option<String>,
#[serde(default)]
control: Option<String>,
+ #[serde(default)]
+ draft: Option<String>,
}
fn handle_stream(stream: UnixStream) {
@@ -231,6 +238,7 @@ fn handle_stream(stream: UnixStream) {
importance,
extension: params.extension,
control: params.control,
+ draft: params.draft,
};
record(params.pane_id, state, reported);
wake();
@@ -244,6 +252,22 @@ struct Reported {
importance: Option<Importance>,
extension: Option<String>,
control: Option<String>,
+ draft: Option<String>,
+}
+
+/// The longest draft kept: enough to find an agent by it.
+const DRAFT_CHARS: usize = 200;
+
+/// A draft as one line, at most [`DRAFT_CHARS`] long; "" for none.
+fn draft_line(draft: &str) -> String {
+ draft
+ .split(|c: char| c.is_whitespace() || c.is_control())
+ .filter(|word| !word.is_empty())
+ .collect::<Vec<_>>()
+ .join(" ")
+ .chars()
+ .take(DRAFT_CHARS)
+ .collect()
}
/// A value fit for a tmux option: not empty, no control characters.
@@ -271,6 +295,9 @@ fn record(pane_id: String, state: Option<AgentState>, reported: Reported) {
if let Some(control) = option_value(reported.control) {
pane.control = Some(control);
}
+ if let Some(draft) = reported.draft {
+ pane.draft = Some(draft_line(&draft));
+ }
}
/// Start listening; reports accumulate for the watcher to consume.
@@ -328,6 +355,12 @@ pub fn extension(pane_id: &str) -> (Option<String>, Option<String>) {
reports().get(pane_id).map(|pane| (pane.extension.clone(), pane.control.clone())).unwrap_or_default()
}
+/// The draft in the pane's input box ("" for none), if its extension
+/// reported one.
+pub fn draft(pane_id: &str) -> Option<String> {
+ reports().get(pane_id).and_then(|pane| pane.draft.clone())
+}
+
/// Ask the extension behind `control` to reload; its answer: `Ok(())` when
/// it does, else why not (busy, a draft, or no answer).
pub fn request_reload(control: &str) -> Result<(), String> {
@@ -408,6 +441,21 @@ mod tests {
}
#[test]
+ fn a_draft_is_kept_as_one_short_line_until_reported_again() {
+ let draft = |text: &str| Reported { draft: Some(text.into()), ..Default::default() };
+ record("%t5".into(), Some(AgentState::Idle), Reported::default());
+ assert_eq!(super::draft("%t5"), None, "an extension that does not report drafts");
+ record("%t5".into(), None, draft(" fix the\n\tsidecar;\x1b[1m "));
+ assert_eq!(super::draft("%t5").as_deref(), Some("fix the sidecar; [1m"));
+ record("%t5".into(), Some(AgentState::Working), Reported::default());
+ assert_eq!(super::draft("%t5").as_deref(), Some("fix the sidecar; [1m"), "kept by a report without one");
+ record("%t5".into(), None, draft(&"x".repeat(500)));
+ assert_eq!(super::draft("%t5").map(|d| d.chars().count()), Some(DRAFT_CHARS));
+ record("%t5".into(), None, draft(""));
+ assert_eq!(super::draft("%t5").as_deref(), Some(""), "cleared");
+ }
+
+ #[test]
fn a_reload_answer_says_whether_it_reloads() {
let dir = std::env::temp_dir().join(format!("dingger-reload-{}", std::process::id()));
std::fs::create_dir_all(&dir).unwrap();