diff --git a/crates/buzz-cli/README.md b/crates/buzz-cli/README.md index c279008a4e..104e566c98 100644 --- a/crates/buzz-cli/README.md +++ b/crates/buzz-cli/README.md @@ -32,6 +32,7 @@ export BUZZ_RELAY_URL="https://relay.example.com" buzz messages send --channel --content "Hello" buzz messages send --channel --content "Reply" --reply-to --broadcast buzz messages send --channel --content - < message.md # read body from stdin +buzz messages send --channel --content "Decision: ship" --from-thread # desktop "Send to channel" buzz messages get --channel --limit 20 buzz messages thread --channel --event buzz messages thread --link 'buzz://message?channel=&id=&thread=' diff --git a/crates/buzz-cli/src/commands/messages.rs b/crates/buzz-cli/src/commands/messages.rs index a453d57c25..6babbcc61f 100644 --- a/crates/buzz-cli/src/commands/messages.rs +++ b/crates/buzz-cli/src/commands/messages.rs @@ -4,6 +4,7 @@ use uuid::Uuid; use crate::client::{normalize_events, normalize_write_response, BuzzClient}; use crate::commands::event_output::{format_events, thread_markers}; +use crate::commands::sent_from_thread; use crate::error::CliError; use crate::validate::{ infer_language, parse_event_id, parse_uuid, read_or_stdin, truncate_diff, @@ -579,6 +580,46 @@ pub struct SendMessageParams { pub broadcast: bool, pub files: Vec, pub mentions: Vec, + pub from_thread: Option, +} + +/// Verify the thread root exists in `channel` and build the provenance tag +/// desktop's "Send to channel" attaches. +async fn resolve_sent_from_thread_tag( + client: &BuzzClient, + channel: Uuid, + root_event_id: &str, +) -> Result { + let root_hex = parse_event_id(root_event_id)?.to_hex(); + let root = fetch_event(client, &root_hex).await?; + sent_from_thread_tag_for_root(channel, &root_hex, &root) +} + +fn sent_from_thread_tag_for_root( + channel: Uuid, + root_hex: &str, + root: &serde_json::Value, +) -> Result { + if channel_id_from_event(root_hex, root)? != channel { + return Err(CliError::Usage(format!( + "--from-thread event {root_hex} is not in channel {channel}" + ))); + } + if root.get("kind").and_then(|k| k.as_u64()) != Some(9) { + return Err(CliError::Usage(format!( + "--from-thread event {root_hex} is not an ordinary channel message (kind 9)" + ))); + } + if let Some(real_root) = root.get("tags").and_then(find_root_from_tags) { + return Err(CliError::Usage(format!( + "--from-thread takes a thread root, but {root_hex} is a reply; use {real_root}" + ))); + } + let content = root.get("content").and_then(|c| c.as_str()).unwrap_or(""); + Ok(sent_from_thread::build_tag( + root_hex, + sent_from_thread::summarize_thread_root(content).as_deref(), + )) } pub async fn cmd_send_message( @@ -595,6 +636,15 @@ pub async fn cmd_send_message( validate_hex64(r)?; } let channel_uuid = parse_uuid(&p.channel_id)?; + if p.from_thread.is_some() && !matches!(p.kind, None | Some(9)) { + return Err(CliError::Usage( + "--from-thread only applies to kind 9 messages".into(), + )); + } + let sent_from_thread_tag = match p.from_thread.as_deref() { + Some(root) => Some(resolve_sent_from_thread_tag(client, channel_uuid, root).await?), + None => None, + }; let explicit_mentions = normalize_explicit_mentions(&p.mentions)?; let stripped = strip_code_regions(&p.content); @@ -697,7 +747,7 @@ pub async fn cmd_send_message( } else { Vec::new() }; - buzz_sdk::build_message( + let builder = buzz_sdk::build_message( channel_uuid, &final_content, thread_ref.as_ref(), @@ -706,7 +756,11 @@ pub async fn cmd_send_message( &media_tags, &emoji_tags, ) - .map_err(|e| CliError::Other(format!("build_message failed: {e}")))? + .map_err(|e| CliError::Other(format!("build_message failed: {e}")))?; + match sent_from_thread_tag { + Some(tag) => builder.tag(tag), + None => builder, + } } Some(k) => { return Err(CliError::Usage(format!( @@ -918,6 +972,7 @@ pub async fn dispatch( broadcast, files, mentions, + from_thread, } => { cmd_send_message( client, @@ -929,6 +984,7 @@ pub async fn dispatch( broadcast, files, mentions, + from_thread, }, ) .await @@ -1079,8 +1135,8 @@ mod tests { channel_id_from_event, cmd_get_thread, cmd_send_message, event_mention_pubkeys, find_root_from_tags, match_profiles_by_name, merge_message_mentions, missing_members, normalize_explicit_mentions, parse_member_pubkeys, resolve_names_to_pubkeys, - resolve_thread_target, thread_ref_from_event, thread_ref_from_parent_tags, BuzzClient, - CliError, Uuid, + resolve_thread_target, sent_from_thread_tag_for_root, thread_ref_from_event, + thread_ref_from_parent_tags, BuzzClient, CliError, Uuid, }; use buzz_sdk::mentions::{ extract_at_mentions_with_known, extract_at_names, match_names_to_profiles, MentionProfile, @@ -1142,6 +1198,32 @@ mod tests { ); } + #[test] + fn from_thread_accepts_only_top_level_kind_9_roots_in_the_channel() { + let channel = "123e4567-e89b-12d3-a456-426614174000"; + let other = "123e4567-e89b-12d3-a456-426614174001"; + let channel_uuid = Uuid::parse_str(channel).unwrap(); + let root = |kind: u64, h: &str| json!({"kind": kind, "content": "hi", "tags": [["h", h]]}); + + let tag = sent_from_thread_tag_for_root(channel_uuid, ID_A, &root(9, channel)).unwrap(); + assert_eq!(tag.as_slice()[1], ID_A); + + for kind in [40002, 40008, 45001, 45003] { + let err = sent_from_thread_tag_for_root(channel_uuid, ID_A, &root(kind, channel)) + .unwrap_err(); + assert!(matches!(err, CliError::Usage(_)), "kind {kind}"); + assert!(err.to_string().contains("kind 9"), "kind {kind}"); + } + let no_kind = json!({"content": "hi", "tags": [["h", channel]]}); + assert!(sent_from_thread_tag_for_root(channel_uuid, ID_A, &no_kind).is_err()); + assert!(sent_from_thread_tag_for_root(channel_uuid, ID_A, &root(9, other)).is_err()); + let reply = json!({"kind": 9, "tags": [["h", channel], ["e", ID_B, "", "reply"]]}); + assert!(sent_from_thread_tag_for_root(channel_uuid, ID_A, &reply) + .unwrap_err() + .to_string() + .contains(ID_B)); + } + #[test] fn selected_event_requires_a_valid_channel_tag() { let missing = json!({"tags": []}); @@ -1682,6 +1764,7 @@ mod tests { broadcast: false, files: vec![], mentions: vec![], + from_thread: None, } } @@ -1853,4 +1936,147 @@ mod tests { "palette-error fallback must produce no emoji tags, got: {emoji_tags:?}" ); } + + /// Fake relay that answers `/query` by `ids` filter from `stored` and + /// records every `/events` body. + async fn fake_thread_relay( + stored: Vec, + ) -> (String, StdArc>>) { + type S = ( + Vec, + StdArc>>, + ); + let published = StdArc::new(std::sync::Mutex::new(Vec::new())); + let app = AxumRouter::new() + .route( + "/query", + axum_post( + |AxumState((stored, _)): AxumState, body: AxumBytes| async move { + let filter: serde_json::Value = serde_json::from_slice(&body).unwrap(); + let filter = filter.get(0).unwrap_or(&filter); + let found: Vec<_> = stored + .iter() + .filter(|e| { + filter["ids"] + .as_array() + .is_some_and(|ids| ids.contains(&e["id"])) + }) + .collect(); + ( + AxumStatusCode::OK, + [("content-type", "application/json")], + serde_json::to_string(&found).unwrap(), + ) + }, + ), + ) + .route( + "/events", + axum_post( + |AxumState((_, published)): AxumState, body: AxumBytes| async move { + published + .lock() + .unwrap() + .push(String::from_utf8_lossy(&body).to_string()); + ( + AxumStatusCode::OK, + [("content-type", "application/json")], + r#"{"event_id":"fake0000","accepted":true}"#, + ) + }, + ), + ) + .with_state((stored, published.clone())); + let listener = TokioTcpListener::bind("127.0.0.1:0").await.unwrap(); + let addr: StdSocketAddr = listener.local_addr().unwrap(); + tokio::spawn(async move { axum::serve(listener, app).await.unwrap() }); + (format!("http://{addr}"), published) + } + + fn stored_message( + id: &str, + channel: &str, + content: &str, + extra: &[serde_json::Value], + ) -> serde_json::Value { + let mut tags = vec![serde_json::json!(["h", channel])]; + tags.extend(extra.iter().cloned()); + serde_json::json!({ "id": id, "kind": 9, "content": content, "tags": tags }) + } + + #[tokio::test] + async fn send_from_thread_publishes_a_top_level_tagged_message() { + let root = "a".repeat(64); + let reply = "b".repeat(64); + let other_channel = "223e4567-e89b-12d3-a456-426614174000"; + let (url, published) = fake_thread_relay(vec![ + stored_message( + &root, + SEND_TEST_CHANNEL, + "**Plan**: see [doc](https://x.io) 😀", + &[], + ), + stored_message( + &reply, + SEND_TEST_CHANNEL, + "reply", + &[serde_json::json!(["e", root, "", "reply"])], + ), + stored_message(&"c".repeat(64), other_channel, "elsewhere", &[]), + ]) + .await; + let client = BuzzClient::new(url, Keys::generate(), None, None).unwrap(); + + let send = |from_thread: &str, kind: Option| { + let mut params = send_params("decision: ship it"); + params.from_thread = Some(from_thread.to_string()); + params.kind = kind; + cmd_send_message(&client, params) + }; + + for (target, kind) in [ + (reply.as_str(), None), + ("c".repeat(64).as_str(), None), + ("d".repeat(64).as_str(), None), + ("not-hex", None), + (root.as_str(), Some(45001)), + ] { + let err = send(target, kind).await.unwrap_err(); + let expected = match target { + t if t == "d".repeat(64) => matches!(err, CliError::NotFound(_)), + _ => matches!(err, CliError::Usage(_)), + }; + assert!(expected, "{target} {kind:?}: {err}"); + if target == reply { + assert!(err.to_string().contains(&root), "{err}"); + } + } + assert!( + published.lock().unwrap().is_empty(), + "failed checks must not publish" + ); + + send(&root.to_uppercase(), None).await.unwrap(); + let bodies = published.lock().unwrap(); + assert_eq!(bodies.len(), 1, "one send is one publish"); + let event: serde_json::Value = serde_json::from_str(&bodies[0]).unwrap(); + assert_eq!(event["kind"], 9); + assert_eq!(event["content"], "decision: ship it"); + let tags = event["tags"].as_array().unwrap(); + assert!(tags.contains(&serde_json::json!(["h", SEND_TEST_CHANNEL]))); + assert!( + tags.contains(&serde_json::json!([ + "buzz:sent-from-thread", + root, + "Plan : see doc 😀" + ])), + "{tags:?}" + ); + assert!( + !tags + .iter() + .any(|t| matches!(t[0].as_str(), Some("e" | "broadcast"))), + "must be top-level: {tags:?}" + ); + } } diff --git a/crates/buzz-cli/src/commands/mod.rs b/crates/buzz-cli/src/commands/mod.rs index cd9f94a21c..3634f12ea9 100644 --- a/crates/buzz-cli/src/commands/mod.rs +++ b/crates/buzz-cli/src/commands/mod.rs @@ -24,6 +24,7 @@ pub mod reactions; pub(crate) mod read_state; mod repo_default_branch; pub mod repos; +pub(crate) mod sent_from_thread; pub mod social; pub mod upload; pub mod users; diff --git a/crates/buzz-cli/src/commands/sent_from_thread.rs b/crates/buzz-cli/src/commands/sent_from_thread.rs new file mode 100644 index 0000000000..e30928be88 --- /dev/null +++ b/crates/buzz-cli/src/commands/sent_from_thread.rs @@ -0,0 +1,255 @@ +//! `buzz:sent-from-thread` provenance tag, mirroring desktop's +//! `features/messages/lib/sentFromThread.ts` and the validation in +//! `desktop/src-tauri/src/events/message_tags.rs`. + +use nostr::{Tag, TagKind}; + +const TAG_NAME: &str = "buzz:sent-from-thread"; +const EXCERPT_MAX_CHARS: usize = 64; +const MIN_WORD_BREAK_UTF16: usize = 32; + +/// JavaScript `\s` / `String.prototype.trim` whitespace. +fn is_js_space(c: char) -> bool { + c.is_whitespace() || c == '\u{feff}' +} + +fn starts_with_at(chars: &[char], at: usize, pat: &str) -> bool { + pat.chars() + .enumerate() + .all(|(i, p)| chars.get(at + i) == Some(&p)) +} + +fn find_from(chars: &[char], from: usize, pat: &str) -> Option { + (from..chars.len()).find(|&i| starts_with_at(chars, i, pat)) +} + +/// Match `||…||` (no `||` inside) at `at`; returns the end index. +fn spoiler_end(chars: &[char], at: usize) -> Option { + if !starts_with_at(chars, at, "||") { + return None; + } + find_from(chars, at + 2, "||").map(|close| close + 2) +} + +/// Match `[label](target)` at `at`; returns `(end, label_end)` where the label +/// is `at + 1..label_end`. `min_label` is the minimum label length. +fn link_end(chars: &[char], at: usize, min_label: usize) -> Option<(usize, usize)> { + if chars.get(at) != Some(&'[') { + return None; + } + let close = (at + 1..chars.len()).find(|&i| chars[i] == ']')?; + if close - (at + 1) < min_label || chars.get(close + 1) != Some(&'(') { + return None; + } + let paren = (close + 2..chars.len()).find(|&i| chars[i] == ')')?; + Some((paren + 1, close)) +} + +/// Match `?` at `at`; returns the end index. +fn url_end(chars: &[char], at: usize) -> Option { + let start = if chars.get(at) == Some(&'<') { + at + 1 + } else { + at + }; + let after_scheme = if starts_with_at(chars, start, "https://") { + start + 8 + } else if starts_with_at(chars, start, "http://") { + start + 7 + } else { + return None; + }; + let end = (after_scheme..chars.len()) + .find(|&i| is_js_space(chars[i])) + .unwrap_or(chars.len()); + (end > after_scheme).then_some(end) +} + +/// Left-to-right `String.prototype.replace` with a global pattern: `matcher` +/// returns the match end and replacement for a match starting at the index. +fn replace_all( + chars: &[char], + matcher: impl Fn(&[char], usize) -> Option<(usize, Vec)>, +) -> Vec { + let mut out = Vec::with_capacity(chars.len()); + let mut i = 0; + while i < chars.len() { + match matcher(chars, i) { + Some((end, replacement)) => { + out.extend(replacement); + i = end; + } + None => { + out.push(chars[i]); + i += 1; + } + } + } + out +} + +fn strip_markdown(content: &str) -> String { + let controls_removed: Vec = content + .chars() + .map(|c| { + let cp = c as u32; + if cp <= 0x1f || (0x7f..=0x9f).contains(&cp) { + ' ' + } else { + c + } + }) + .collect(); + let spoilers = replace_all(&controls_removed, |chars, at| { + spoiler_end(chars, at).map(|end| (end, vec![' '])) + }); + let images = replace_all(&spoilers, |chars, at| { + if chars[at] != '!' { + return None; + } + link_end(chars, at + 1, 0).map(|(end, _)| (end, vec![' '])) + }); + let links = replace_all(&images, |chars, at| { + link_end(chars, at, 1).map(|(end, label_end)| (end, chars[at + 1..label_end].to_vec())) + }); + let urls = replace_all(&links, |chars, at| { + url_end(chars, at).map(|end| (end, vec![' '])) + }); + urls.into_iter() + .map(|c| if "`*_~>#|".contains(c) { ' ' } else { c }) + .collect() +} + +fn collapse_whitespace(text: &str) -> String { + text.split(is_js_space) + .filter(|part| !part.is_empty()) + .collect::>() + .join(" ") +} + +/// Summarize a thread root for the tag excerpt exactly as desktop's +/// `summarizeThreadRoot` does. Returns `None` when nothing readable remains. +pub(crate) fn summarize_thread_root(content: &str) -> Option { + let normalized = collapse_whitespace(&strip_markdown(content)); + if normalized.is_empty() { + return None; + } + let chars: Vec = normalized.chars().collect(); + if chars.len() <= EXCERPT_MAX_CHARS { + return Some(normalized); + } + let clipped: String = chars[..EXCERPT_MAX_CHARS - 1].iter().collect(); + let units: Vec = clipped.encode_utf16().collect(); + let excerpt = match units.iter().rposition(|&u| u == u16::from(b' ')) { + Some(space) if space > MIN_WORD_BREAK_UTF16 => String::from_utf16_lossy(&units[..space]), + _ => clipped, + }; + Some(format!("{}…", excerpt.trim_end_matches(is_js_space))) +} + +/// Build the `["buzz:sent-from-thread", , ]` tag. +pub(crate) fn build_tag(root_event_id: &str, excerpt: Option<&str>) -> Tag { + let mut parts = vec![root_event_id.to_string()]; + parts.extend(excerpt.map(str::to_string)); + Tag::custom(TagKind::custom(TAG_NAME), parts) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn s(content: &str) -> Option { + summarize_thread_root(content) + } + + #[test] + fn strips_markdown_like_desktop() { + assert_eq!(s(" hello\n\tworld ").as_deref(), Some("hello world")); + assert_eq!( + s("**bold** _it_ `code` ~~x~~ > q # h").as_deref(), + Some("bold it code x q h") + ); + assert_eq!( + s("see [the doc](https://x.io/a) now").as_deref(), + Some("see the doc now") + ); + assert_eq!(s("a ![alt](https://x.io/i.png) b").as_deref(), Some("a b")); + assert_eq!(s("a ||secret | x|| b").as_deref(), Some("a b")); + assert_eq!(s("a ||unclosed b").as_deref(), Some("a unclosed b")); + assert_eq!( + s("go and http://y.io z").as_deref(), + Some("go and z") + ); + assert_eq!(s("[](x) [a]b").as_deref(), Some("[](x) [a]b")); + assert_eq!(s("a\u{0}b\u{7f}c\u{85}d").as_deref(), Some("a b c d")); + assert_eq!(s("a\u{feff}b\u{a0}c").as_deref(), Some("a b c")); + assert_eq!(s("*** ||x|| ![i](u)"), None); + assert_eq!(s(""), None); + } + + #[test] + fn clips_at_the_desktop_boundaries() { + let exact = "a".repeat(64); + assert_eq!(s(&exact).as_deref(), Some(exact.as_str())); + + let unbroken = "a".repeat(65); + assert_eq!(s(&unbroken), Some(format!("{}…", "a".repeat(63)))); + + let words = format!("{} {}", "a".repeat(40), "b".repeat(40)); + assert_eq!(s(&words), Some(format!("{}…", "a".repeat(40)))); + + let early_space = format!("{} {}", "a".repeat(32), "b".repeat(40)); + assert_eq!( + s(&early_space), + Some(format!("{} {}…", "a".repeat(32), "b".repeat(30))) + ); + + let late_space = format!("{} {}", "a".repeat(33), "b".repeat(40)); + assert_eq!(s(&late_space), Some(format!("{}…", "a".repeat(33)))); + } + + #[test] + fn clips_by_code_point_and_breaks_by_utf16_unit() { + let wide = "é".repeat(65); + assert_eq!(s(&wide), Some(format!("{}…", "é".repeat(63)))); + let emoji = "😀".repeat(65); + assert_eq!(s(&emoji), Some(format!("{}…", "😀".repeat(63)))); + let kept = "😀".repeat(64); + assert_eq!(s(&kept).as_deref(), Some(kept.as_str())); + + // 16 emoji take 32 UTF-16 units, so the space sits at unit 32: kept whole. + let at_32 = format!("{} {}", "😀".repeat(16), "b".repeat(60)); + assert_eq!( + s(&at_32), + Some(format!("{} {}…", "😀".repeat(16), "b".repeat(46))) + ); + // 17 emoji put the space at unit 34 (> 32), though only 17 code points in: break there. + let at_34 = format!("{} {}", "😀".repeat(17), "b".repeat(60)); + assert_eq!(s(&at_34), Some(format!("{}…", "😀".repeat(17)))); + } + + #[test] + fn matches_desktop_summarize_thread_root_on_generated_vectors() { + let vectors: Vec = + serde_json::from_str(include_str!("sent_from_thread_vectors.json")).unwrap(); + assert!(vectors.len() > 600); + for vector in vectors { + let input = vector["input"].as_str().unwrap(); + assert_eq!( + s(input).as_deref(), + vector["expected"].as_str(), + "input: {input:?}" + ); + } + } + + #[test] + fn tag_has_desktop_shape() { + let id = "a".repeat(64); + assert_eq!(build_tag(&id, None).as_slice(), [TAG_NAME, id.as_str()]); + assert_eq!( + build_tag(&id, Some("hi")).as_slice(), + [TAG_NAME, id.as_str(), "hi"] + ); + } +} diff --git a/crates/buzz-cli/src/commands/sent_from_thread_vectors.json b/crates/buzz-cli/src/commands/sent_from_thread_vectors.json new file mode 100644 index 0000000000..b72c329fef --- /dev/null +++ b/crates/buzz-cli/src/commands/sent_from_thread_vectors.json @@ -0,0 +1,2558 @@ +[ + { + "input": "", + "expected": null + }, + { + "input": " ", + "expected": null + }, + { + "input": "hello", + "expected": "hello" + }, + { + "input": " hello\n\tworld ", + "expected": "hello world" + }, + { + "input": "**bold** _it_ `code` ~~x~~ > q # h", + "expected": "bold it code x q h" + }, + { + "input": "see [the doc](https://x.io/a) now", + "expected": "see the doc now" + }, + { + "input": "a ![alt](https://x.io/i.png) b", + "expected": "a b" + }, + { + "input": "a ||secret | x|| b", + "expected": "a b" + }, + { + "input": "a ||unclosed b", + "expected": "a unclosed b" + }, + { + "input": "||a|||b||", + "expected": "b" + }, + { + "input": "||||", + "expected": null + }, + { + "input": "|||", + "expected": null + }, + { + "input": "go and http://y.io z", + "expected": "go and z" + }, + { + "input": "http:// x", + "expected": "http:// x" + }, + { + "input": "https://", + "expected": "https://" + }, + { + "input": "[](x) [a]b", + "expected": "[](x) [a]b" + }, + { + "input": "[a[b](c)", + "expected": "a[b" + }, + { + "input": "![a](b", + "expected": "![a](b" + }, + { + "input": "![](x)", + "expected": null + }, + { + "input": "[a](b(c)d)", + "expected": "ad)" + }, + { + "input": "a\u0000bc…d", + "expected": "a b c d" + }, + { + "input": "ab c d", + "expected": "a b c d" + }, + { + "input": "*** ||x|| ![i](u)", + "expected": null + }, + { + "input": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "expected": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + { + "input": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "expected": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa…" + }, + { + "input": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "expected": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa…" + }, + { + "input": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "expected": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb…" + }, + { + "input": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "expected": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa…" + }, + { + "input": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "expected": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa…" + }, + { + "input": "ééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééé", + "expected": "ééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééé…" + }, + { + "input": "😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀", + "expected": "😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀" + }, + { + "input": "😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀", + "expected": "😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀…" + }, + { + "input": "漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢", + "expected": "漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢漢…" + }, + { + "input": "😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀 bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "expected": "😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀 bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb…" + }, + { + "input": "😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀 bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "expected": "😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀…" + }, + { + "input": "😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀 bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "expected": "😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀 bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb…" + }, + { + "input": "😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀a bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "expected": "😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀a bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb…" + }, + { + "input": "x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x ", + "expected": "x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x…" + }, + { + "input": "ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab  z", + "expected": "ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab…" + }, + { + "input": "](漢!__\n`#~)>\né||**>  http://x.io`)![[\u0000]]#", + "expected": "](漢! ) é ]]" + }, + { + "input": " ]([https://y.io/p!http://x.io~**", + "expected": "]([" + }, + { + "input": "#](😀 ` éword_[_||[]]…]a||", + "expected": "](😀 éword [" + }, + { + "input": "_a![![]**", + "expected": "a![![]" + }, + { + "input": "http://x.io😀 漢_a😀!http://x.io\né_ é_word…word||", + "expected": "漢 a😀 漢a" + }, + { + "input": "#]( ||#]]a](`#>!é ](a||https://y.io/p|_", + "expected": "](" + }, + { + "input": "http://x.io|\u0000_ é**_<](_)[||]( 😀!`https://y.io/p##](http://x.iohttp://x.io )a漢[", + "expected": "é <]( ) a漢[" + }, + { + "input": " >`", + "expected": null + }, + { + "input": ")**http://x.io\u0000](word)!**||é!||`](#![https://y.io/p`https://y.io/p__![ >a http://x.io<)_", + "expected": ") ](word )! ]( ![ a" + }, + { + "input": "é)<<é](\u0000\u0000漢]![b][…!a][\n![…`!>http://x.io_", + "expected": "é)<<é]( 漢] ![b][ !a][ ![ !" + }, + { + "input": "](>é漢😀word\u0000~a|![#`…https://y.io/p** **\u0000>![]( >_`\n##~** word||)http://x.iohttp://x.iob](漢", + "expected": "]( é漢😀word a" + }, + { + "input": "\nb||>http://x.io|é #<|漢]( é😀>]b!>>||\n_\u0000]http://x.io!_http://x.io_https://y.io/phttps://y.io/p``![||b  ", + "expected": "b ]" + }, + { + "input": "](a !#é!>`#!|_ >)_http://x.io\u0000>[**`ba ] ||| wordéé|word![||word\n ![  http://x.io\u0000b`_]a \n_#|**http://x.io**>a![](", + "expected": "[ ba ] word ![ b ]a" + }, + { + "input": "]]wordbwordhttps://y.io/p…#\nb**\u0000>)…](word", + "expected": "]]wordb word b ) ](word" + }, + { + "input": "|http://x.io#<~éb… a[[ [http://x.ioé>😀a aé", + "expected": "]( wordé <<](>\u0000](漢\n漢http://x.io😀a](]( **\né# 漢  # |> é]漢** ![\u0000", + "expected": "] word <<]( ](漢 漢 é 漢 é] 漢 ![" + }, + { + "input": " ~a!~![ \n))`**![漢é]<]![||…`]( _#http://x.io", + "expected": "a ! ![ ) ) ![漢é]<]![ ](" + }, + { + "input": "a](http://x.io!#\n_http://x.iohttp://x.io!…\u0000a… |>|)![…|``**)`…漢[]", + "expected": "a]( a )![ ) 漢[]" + }, + { + "input": "~https://y.io/p !|~b![é漢]wordhttps://y.io/p<)**https://y.io/p\u0000b_  |…~>]()[)] ", + "expected": "! b![é漢]word b ]()[)]" + }, + { + "input": "\u0000||~😀>", + "expected": "😀" + }, + { + "input": "…[||wordword >![`[|![~é…**|http://x.io**a!`>]", + "expected": "[ wordword ![ [ ![ é" + }, + { + "input": " `https://y.io/p_~aahttp://x.iohttp://x.iob😀** >![  https://y.io/p[ [`a**http://x.io||https://y.io/p_😀\n||ééhttp://x.io](![![é", + "expected": "aa ![ [ a éé" + }, + { + "input": "é…😀😀 https://y.io/pb\u0000", + "expected": "é 😀😀" + }, + { + "input": "![ba", + "expected": "![b a" + }, + { + "input": "_bé>\n_ http://x.ioéaéhttps://y.io/p `", + "expected": "bé" + }, + { + "input": "b) \né`_", + "expected": "b) é" + }, + { + "input": "~https://y.io/pa😀 a http://x.io**`aabwordé]~ http://x.io_||word~\n]( >\u0000![", + "expected": "a ]( ![" + }, + { + "input": "a![http://x.io", + "expected": "a![" + }, + { + "input": "]| ]…[😀![ ", + "expected": "] ] [😀![" + }, + { + "input": "http://x.iob_||😀![![~**é[|\n| **\nb", + "expected": "😀![![ é[ b" + }, + { + "input": "](…>\u0000](\n >… |**|<~_aé#é]", + "expected": "]( ]( < aé é]" + }, + { + "input": "![]( b> ~|\u0000漢[a \n\n https://y.io/p])~![|é\nword![https://y.io/pword** ab|]||!>!…é", + "expected": "![ é word![ ab ] ! ! é" + }, + { + "input": "~>>bhttps://y.io/p![漢wordé![… é漢`…](<[漢é…#]\n[|b~**!…漢word#…word😀|é", + "expected": "b é漢 ](<[漢é ] [ b ! 漢word word😀 é" + }, + { + "input": "😀é](https://y.io/p#漢><é>||![ [", + "expected": ") é [ 😀é]( [" + }, + { + "input": "https://y.io/p**a\u0000](a |#\u0000< 😀\u0000é`]\n <漢_|| word|| [ b\u0000<[ahttps://y.io/pword[word é…", + "expected": "](a < 😀 é ] <漢 [ b <[a é" + }, + { + "input": "![_word😀 ![>_wordhttp://x.io**||**)漢…![!…]![~<😀https://y.io/p>)\n <`", + "expected": "![ word😀 ![ word ![! ]![ <😀 <" + }, + { + "input": "`b漢<#😀word]\n<|…", + "expected": "b漢< 😀word] <" + }, + { + "input": "[](http://x.io[…>]<\n<  \u0000word😀\u0000![\u0000#_](word![…**word", + "expected": "[]( ]< < word😀 ![ ](word![ word" + }, + { + "input": " ](\u0000https://y.io/p#)ahttps://y.io/p||……~😀wordbbword ]> _a漢", + "expected": "]( 😀wordbbword ] a漢" + }, + { + "input": "#漢a||]…_~![](b😀é![\u0000\n||http://x.io<_ éhttps://y.io/p|word\u0000#](漢ahttps://y.io/p||a漢!||]漢b\u0000", + "expected": "漢a é ](漢a ]漢b" + }, + { + "input": "`|#…\n]https://y.io/p`](>漢~", + "expected": "]" + }, + { + "input": "**#[>\nbbb> ", + "expected": "b> a|a|)b…bhttp://x.io\na…_", + "expected": "ab b a a )b b a" + }, + { + "input": "]<]éa\nhttp://x.io) **[http://x.io\nhttp://x.io漢  b !||#![>", + "expected": "]<]éa [ b ! ![" + }, + { + "input": "a~\u0000😀 ]", + "expected": "a 😀 ]" + }, + { + "input": " … `word![![!", + "expected": "word![![!" + }, + { + "input": "~!~`](](… 😀 <_ )http://x.ioa`||>b`| [http://x.ioé#`~[http://x.io", + "expected": "[] ! ) [ [" + }, + { + "input": "a _~漢_|https://y.io/p<)\u0000>>>![>~#<||**\u0000\n!é~a|#[ word", + "expected": "a 漢 ![ < !é a [ word" + }, + { + "input": "…`)~http://x.io||a](word<||||`#", + "expected": ")" + }, + { + "input": "> https://y.io/p!](>`)aa", + "expected": "! 😀]😀<漢 😀 é]![ [ )aa" + }, + { + "input": "![![_]( ~漢http://x.io||", + "expected": "![![ ]( 漢" + }, + { + "input": "\u0000![漢😀](>[!a~)|word漢[||**", + "expected": "word漢[" + }, + { + "input": "~![😀…)…)[", + "expected": "![😀 ) )[" + }, + { + "input": "**#< [ **<]( a\u0000](!](# ~<[](~ ||b ", + "expected": "< [ < ]( a ](! ]( <[]( b" + }, + { + "input": "~]) [ _ ](||~)b~bé||😀[\u0000<éa<…word\n ![!_b", + "expected": "]) [ ]( 😀[ <éa< word ![! b" + }, + { + "input": "_|~a#!]", + "expected": "a !]" + }, + { + "input": "`…| ![![<\n!漢<](<#||__ || #!~…)<](a…>b_\n", + "expected": "<](a b" + }, + { + "input": "…\u0000http://x.io # |b )<…> `😀 ](`b<  漢a`…😀_[b…http://x.io) ||_😀😀 b!~", + "expected": "漢 😀😀 b!" + }, + { + "input": "[b", + "expected": "[b" + }, + { + "input": "`漢 https://y.io/p)word http://x.io|||![` <😀😀](\nwordhttps://y.io/p**", + "expected": "漢 ![ <😀😀]( word" + }, + { + "input": "wordhttp://x.ioaéé]a![http://x.io![](>\u0000…\n](", + "expected": "word ](" + }, + { + "input": "**[…é`!>漢||\u0000 aword😀 é< ![] ![", + "expected": "]( < ![] ![" + }, + { + "input": "\u0000#ahttps://y.io/p](word>)https://y.io/p!~>#https://y.io/pba](http://x.io😀a`[http://x.io**_https://y.io/p[https://y.io/p ~http://x.iohttps://y.io/p]https://y.io/p]( `", + "expected": "a word ) ](" + }, + { + "input": "|]|| ", + "expected": "]" + }, + { + "input": " #!![!**é\n#…a ]`漢|]_😀http://x.io>)`é`", + "expected": "<)漢 a ] 漢 ] 😀" + }, + { + "input": "#\n😀😀|[漢 é", + "expected": "😀😀 [漢 é" + }, + { + "input": "漢", + "expected": "漢" + }, + { + "input": "]~)![word😀\nhttps://y.io/p**||word\u0000 \n ||漢 😀#https://y.io/p ||||word><~b 漢[http://x.io", + "expected": "] )![word😀 漢 😀 word < b 漢[" + }, + { + "input": ">! http://x.io)~word <#word )](…word", + "expected": "! < word )]( word" + }, + { + "input": "é>a!>~  `~ 😀!|`<😀![|| |[>\nword[~bhttps://y.io/p||[https://y.io/p!https://y.io/pab)漢\n", + "expected": "é a! 😀! <😀![ [" + }, + { + "input": "…[_a<]|>\u0000é漢word", + "expected": "[ a<] é漢word" + }, + { + "input": "[  😀~!~~`~](_b😀…**http://x.io||…_>!>ba**😀https://y.io/p", + "expected": "[ 😀 ! ]( b😀 ! ba 😀" + }, + { + "input": "é| ****![~\u0000éaword](<…<[word… ]#\nab`!word_😀**](||#  \u0000) || 漢#  `", + "expected": "é ![ éaword](< <[word ] ab !word 😀 ]( 漢" + }, + { + "input": "_!word**<…\u0000…http://x.io漢](漢 `…#😀", + "expected": "!word < 😀" + }, + { + "input": "word|b~) a`>\u0000a>~", + "expected": "word b ) a a" + }, + { + "input": "<…](**… |**😀\n\u0000😀![漢[]\u0000](**", + "expected": "< ]( 😀 😀 [] ](" + }, + { + "input": "![https://y.io/p)a[b <#wordhttp://x.io|[>>>~#b! >", + "expected": "![ < word" + }, + { + "input": "a` ", + "expected": "a" + }, + { + "input": "word![_>bé …<#word", + "expected": "word![ bé < word" + }, + { + "input": "…_|<[ ) !wordb漢`#~ ])__漢…~[!` https://y.io/phttp://x.io||)漢b\u0000é😀https://y.io/pword>]>![", + "expected": "<[ ) !wordb漢 ]) 漢 [! é😀" + }, + { + "input": "…é)>!", + "expected": "é) !" + }, + { + "input": "](", + "expected": "](" + }, + { + "input": " b… word …!a", + "expected": "b word !a" + }, + { + "input": " ](||#||…`]( ]…https://y.io/p` !https://y.io/p>😀wordword😀http://x.io||#>`>~)😀", + "expected": "]( ]( ] !" + }, + { + "input": "**`[…\n )![ !http://x.io[ [https://y.io/p! https://y.io/p漢_word`😀# … `_!https://y.io/p>](https://y.io/p )漢", + "expected": "word[é ! 漢" + }, + { + "input": "a ]é_…![![# word~~ [漢< \u0000 ", + "expected": "a ]é ![![ word [漢<" + }, + { + "input": "\n😀![!😀[>>é b](]( word|||)**](||word_…\n漢word`漢  #\u0000|<😀😀< ", + "expected": "😀![!😀[ é b](]( word word 漢word 漢 <😀😀<" + }, + { + "input": "|a#http://x.io[https://y.io/p[ <|word# |漢https://y.io/p||\u0000b[)) 漢[ #|!", + "expected": "a < word 漢 ]😀 !" + }, + { + "input": "b\u0000#http://x.io[\n||> \u0000 https://y.io/pword`…!  é http://x.io](é||~…\u0000 漢||😀word ", + "expected": "b 漢 😀word" + }, + { + "input": "_)![`ahttp://x.io\u0000word>!b](…`b_ )~é\n[)]é\n~]([", + "expected": "é < a word é [)]é ]([" + }, + { + "input": "`http://x.iohttp://x.io\néa![![😀\n… \u0000)b~漢![ \u0000 ", + "expected": "éa![![ 😀 )b 漢![" + }, + { + "input": " \u0000\u0000!a**|éa漢…", + "expected": "!a é a漢" + }, + { + "input": "… b||😀 _ ]…>~……|) https://y.io/phttps://y.io/p||a! … 😀||**https://y.io/p", + "expected": "b a < _!😀|**|漢__\u0000éhttp://x.io漢_é|| ~])😀漢漢", + "expected": "b < !😀 漢 é ] )😀漢漢" + }, + { + "input": "http://x.io\n<\né>>é|aword!\n]http://x.io![worda]", + "expected": "< é é aword! ]" + }, + { + "input": "[…aé]…![\u0000a<||<漢", + "expected": "[ aé] ![ a< <漢" + }, + { + "input": "b~a", + "expected": "b a" + }, + { + "input": "`[é https://y.io/p](http://x.ioword~b`||]éahttps://y.io/p😀http://x.io ahttps://y.io/p[ !||é!\n[", + "expected": "[é é! [" + }, + { + "input": "]_#wordé", + "expected": "] wordé" + }, + { + "input": "|http://x.io|!||[…`\u0000)[https://y.io/phttp://x.io![ [http://x.io\n!https://y.io/p_https://y.io/p~word_\n|_https://y.io/p>漢)word]>", + "expected": ")[ [ !" + }, + { + "input": "word  ) ~https://y.io/pb** _b!<\u0000[\n _![[>https://y.io/phttps://y.io/p](\n", + "expected": "word ) b!< [ ![[" + }, + { + "input": "~éa#word![>https://y.io/p\u0000😀…😀ahttp://x.io]|", + "expected": "éa word![ 😀 😀a" + }, + { + "input": "<`é!~]!)#https://y.io/p<~](~…http://x.io>", + "expected": "< é! ]!)" + }, + { + "input": "~\u0000https://y.io/pa<](![****\nhttp://x.io||![_\u0000", + "expected": null + }, + { + "input": "漢éhttp://x.ioword _😀`[|_ a\u0000 >|| … |http://x.io||~\u0000**||~b word…b><…a|a b", + "expected": "漢é 😀 [ a b word b < a a b" + }, + { + "input": "😀[\u0000http://x.io![\u0000", + "expected": "😀[" + }, + { + "input": "![\n", + "expected": "![" + }, + { + "input": " ](<|\nb\u0000b ](ba>  !\n||`![]**||##[ ![!é\n_😀!\nhttps://y.io/p", + "expected": "](< b b ](ba ! [ ![!é 😀!" + }, + { + "input": ">\n…>https://y.io/pword)word é ![](https://y.io/p#\u0000**#|\u0000~||``** ![…", + "expected": "é ![]( ![" + }, + { + "input": "b 😀é !]( \nhttps://y.io/phttps://y.io/p\n# …[]( `)b漢", + "expected": "b 😀é !]( b漢" + }, + { + "input": "# ||![漢 )||> é …http://x.io[😀 http://x.io#\n …😀 ![**[", + "expected": "é 😀 ![ [" + }, + { + "input": "[b…漢a**\n", + "expected": "[b 漢a" + }, + { + "input": "http://x.ioé)http://x.iohttp://x.iob)#[b_…]", + "expected": ") ]" + }, + { + "input": " [https://y.io/p`\n…||[~[", + "expected": "[ [ [" + }, + { + "input": "> |||]<<||b漢](||~#…[word )é漢é漢\n…_**word!\n", + "expected": "b漢]( [word )é漢é漢 word !" + }, + { + "input": ")漢<](\u0000 …https://y.io/p)) ", + "expected": "] word)a <](" + }, + { + "input": "]](b]]wordé\u0000\u0000", + "expected": "]](b]]wordé" + }, + { + "input": "!_http://x.io…#http://x.io![a__ b](https://y.io/p_>http://x.io😀é ![\u0000漢", + "expected": "! b]( ![ 漢" + }, + { + "input": "wordhttps://y.io/p](word#![) ) ~漢…](\u0000 || ~||\n`_bhttp://x.io![~)_![😀", + "expected": "word ![😀" + }, + { + "input": ">_", + "expected": null + }, + { + "input": "\u0000 bwordhttp://x.io\u0000…![#漢`word<", + "expected": "bword ![ 漢 word<" + }, + { + "input": " http://x.io…<é ", + "expected": "<é" + }, + { + "input": "![\n>**![]\né!…|😀|~# ||漢", + "expected": "![ ![] é! 😀 漢" + }, + { + "input": "\n](>http://x.io… <", + "expected": "]( <" + }, + { + "input": "``é\né!…_é ", + "expected": "é é! é" + }, + { + "input": "!word!éé😀b )!#](word#!http://x.io😀![ < `a![ ||**`a![", + "expected": "!word!éé😀b )! ](word ! < a![ a![" + }, + { + "input": "![]!b!é##漢…https://y.io/p`!## é漢漢 ]( # \u0000[**https://y.io/p#漢a\u0000!#`http://x.io_](😀[**_https://y.io/p||…#", + "expected": "b ![]( bbb😀] 漢 ]( [ !" + }, + { + "input": "~\nword](", + "expected": "word](" + }, + { + "input": "#word](\u0000#!**||😀https://y.io/pbhttps://y.io/phttp://x.io!https://y.io/p<\n\u0000>😀a![![!![_[é", + "expected": "word]( ! 😀 😀a![![!![ [é" + }, + { + "input": "![[~éhttp://x.io`_😀`漢|http://x.io` **\n|a](漢]https://y.io/p<**  ]", + "expected": "![[ é a](漢] ]" + }, + { + "input": "]!b|\u0000漢http://x.io||https://y.io/p! …) \u0000  é**|~http://x.io#[😀](~…b", + "expected": "]!b 漢 ) é b" + }, + { + "input": "||漢]<  ~\n…] \u0000bword](#aé\nb]<\u0000bhttps://y.io/p", + "expected": "漢]< ] bword]( aé b]< b" + }, + { + "input": ")\u0000[…**\n**#word", + "expected": ") [ word" + }, + { + "input": "]()😀漢_||](_b]https://y.io/p< …\u0000bword \nhttps://y.io/p\u0000~]\né !…![__**😀b )[ [](||[漢", + "expected": "]()😀漢 [漢" + }, + { + "input": "|[~_ ![ 漢a…[word漢 https://y.io/p![ ]()|b ~é|\n~_#éé #😀 https://y.io/p[#>_[", + "expected": "[ b é éé 😀" + }, + { + "input": "**]**]a)|| bhttp://x.io__**~****… #[|| \n", + "expected": "] ]a)" + }, + { + "input": "]word]b><]", + "expected": "]word]b <]" + }, + { + "input": "word)\né😀http://x.io https://y.io/p ! [#é|é\u0000 ****http://x.io\u0000`https://y.io/p|| **b é**word`|![\n||\nhttp://x.io", + "expected": "word) é😀 ! [ é é" + }, + { + "input": "a||! a\n)_[a**#<", + "expected": "a ! a ) [a <" + }, + { + "input": "\u0000![漢!\u0000#!\n_** ](漢)!https://y.io/p~>](~ab]b…|| a漢…##", + "expected": "! a漢" + }, + { + "input": "http://x.io", + "expected": null + }, + { + "input": "ba !](_>]**a]_>漢漢éword|😀é", + "expected": "ba !]( ] ![ b é ) é a] 漢漢éword 😀é" + }, + { + "input": "](#漢|>![漢**)| **https://y.io/p_>|\u0000>\u0000ba|||![](`)`>…漢\u0000…", + "expected": "]( 漢 漢" + }, + { + "input": " ]é😀漢](a😀\n_||)a|word~]]!](\n_", + "expected": "]é😀漢](a😀 )a word ]]!](" + }, + { + "input": "word\n>![b`", + "expected": "word ![b" + }, + { + "input": "…](****<a![](**…https://y.io/phttp://x.io ](**b||a||] \u0000b](_", + "expected": "a ] b](" + }, + { + "input": "<_", + "expected": "<" + }, + { + "input": "…)`!word|| ![😀](|_)||] _**http://x.ioword **\n… ", + "expected": ") !word ]" + }, + { + "input": "\n>http://x.iohttps://y.io/p!)… ~ 😀`😀\n_)`a**", + "expected": "😀 😀 ) a" + }, + { + "input": "`b< ]…http://x.io~ ~😀", + "expected": "b< ] 😀" + }, + { + "input": "||é)** 😀`!…# 漢<\n[||漢\na|)~#`> #\n", + "expected": "漢 a )" + }, + { + "input": "漢\n https://y.io/p||<…b word\u0000`a_[b", + "expected": "b[[ a ! word漢 b" + }, + { + "input": "https://y.io/p", + "expected": null + }, + { + "input": "https://y.io/phttps://y.io/p![https://y.io/p)) ]", + "expected": "a![)a ]" + }, + { + "input": "a `http://x.iob<# https://y.io/pwordé]…~||", + "expected": "a" + }, + { + "input": "#é](![``http://x.io`]` #**aahttp://x.io] ", + "expected": "é](![ aa" + }, + { + "input": "![](…\n\u0000![ >漢b~>~**||\n\n<漢a word漢_)word#漢!😀wordhttp://x.iobé**http://x.io<word~\n)[<😀|![~", + "expected": "word[ << [<😀 ![" + }, + { + "input": "#|…||https://y.io/p_https://y.io/p!éb[)\u0000](é>#]~…", + "expected": "](é ]" + }, + { + "input": "](aword |a] 漢_]  #_|](漢 **漢éword", + "expected": "](aword a ] 漢 ] ](漢 漢éword" + }, + { + "input": "!http://x.io]漢", + "expected": "!" + }, + { + "input": "a b![é\u0000https://y.io/p|| b<_…**", + "expected": "a é b![é b<" + }, + { + "input": "\u0000 `> >é\u0000[_]<>||_)b``_](#_ https://y.io/pb![`\nb", + "expected": "é [ ]< )b ]( b" + }, + { + "input": "](word~word\u0000word](]漢\u0000\na \n\u0000漢 ]||` http://x.ioaa…https://y.io/p b>](![>b]…\u0000b#[word", + "expected": "](word word word](]漢 a 漢 ] b ](![ b] b [word" + }, + { + "input": "…``ba<\u0000<é)[_` `<\u0000]( 漢>😀! !b) <", + "expected": "a< <é) < <" + }, + { + "input": "漢|a||word… > )", + "expected": "漢 a word )" + }, + { + "input": "|| ``é_ #](a", + "expected": "é ](a" + }, + { + "input": " <…#_漢http://x.io!![#…a![ \n**)", + "expected": "< 漢 ![ a![ )" + }, + { + "input": "!é>![", + "expected": "!é ![" + }, + { + "input": "\nhttps://y.io/phttps://y.io/p](", + "expected": null + }, + { + "input": "a!…`[https://y.io/p ](https://y.io/p`…![||)…  [** \n\n#http://x.io]😀\u0000`bhttps://y.io/p[~😀", + "expected": ")< !< word ![ ) [ b" + }, + { + "input": "…\u0000é😀<漢…b~😀~#](漢]…[word\nhttps://y.io/p\u0000\u0000>", + "expected": "é😀<漢 b 😀 ](漢] [word" + }, + { + "input": "a`]漢[\u0000ba…😀](b!||漢[ …~b>…#|https://y.io/p ~bhttps://y.io/phttps://y.io/p 😀b😀_](~", + "expected": "a ]漢[ ba 😀]( b! 漢[ b b 😀b😀 ](" + }, + { + "input": "**😀 )]![é…", + "expected": "😀 )]![é" + }, + { + "input": ">漢[wordé…[`| ~http://x.io a[](] ~` ||a![[**b)_<…a**", + "expected": "漢wordé [ a[ < a" + }, + { + "input": "ééé#||!![ ` ](é`]**_https://y.io/p]||[!漢\né http://x.io", + "expected": "ééé [!漢 é" + }, + { + "input": "漢https://y.io/p**~> b😀…**… https://y.io/p`)https://y.io/p|", + "expected": "漢 b😀" + }, + { + "input": "https://y.io/p`<>漢 )|>**bhttp://x.io", + "expected": "< 漢 ) b" + }, + { + "input": "](bhttps://y.io/p!https://y.io/p b_\u0000](漢a!word|b", + "expected": "](b b ]( 漢a!word b" + }, + { + "input": "`[\u0000  ||a word ||~~](>\n<~`>]![http://x.io\u0000aé![é![]( http://x.io\u0000||~|]([>word]bhttps://y.io/p", + "expected": "[ ]( < ]![ aé![é![]( ]([ word]b" + }, + { + "input": "![||bhttp://x.io!_||_]||", + "expected": "![ ]" + }, + { + "input": "https://y.io/p](", + "expected": null + }, + { + "input": ">😀` é>漢` <_ http://x.io_", + "expected": "😀 é 漢 <" + }, + { + "input": "~)~#_\n\n漢!a) \u0000[|_~…]( https://y.io/p", + "expected": "b) [https://y.io/p**||>ahttps://y.io/pa…😀word |**||<", + "expected": "b) [ <" + }, + { + "input": "#a\u0000b\n|[<<__\u0000漢\u0000](||é`漢word~ \n 漢**é[|wordé 漢**https://y.io/p**", + "expected": "a b [<< 漢 ]( é 漢word 漢 é[ wordé 漢" + }, + { + "input": "~|…漢b \u0000", + "expected": "漢b" + }, + { + "input": "\u0000漢bé||…<~", + "expected": "漢bé <" + }, + { + "input": "[_é|](wordhttp://x.io\u0000 …|", + "expected": "[ é ](word" + }, + { + "input": ">_ https://y.io/p])#>|** 漢 >)`", + "expected": "漢 )" + }, + { + "input": "漢", + "expected": "漢" + }, + { + "input": "#!#b||_\u0000…漢**|\n\u0000b`awordword`>)||wordhttps://y.io/p< \n\na[)](漢\u0000![😀!b![😀", + "expected": "! b word a[)](漢 ![😀!b![😀" + }, + { + "input": "|| ! …[😀  _![ <", + "expected": "! [😀 ![ <" + }, + { + "input": "_]|  ba漢\n[ b||`", + "expected": "] ba漢 [ b" + }, + { + "input": " ![_!)!", + "expected": "![ !)!" + }, + { + "input": " \u0000|| |!b_|![![😀<…b<||", + "expected": " ](worda\u0000| ||| )~…](~~)]", + "expected": "!é ]" + }, + { + "input": "漢||)\n)_", + "expected": "漢 ) )" + }, + { + "input": "#**>漢b<\u0000)![`[http://x.iohttp://x.iohttps://y.io/p]()!", + "expected": "](" + }, + { + "input": "b _ \n||wordhttps://y.io/p)]~…é…||\n[_a…https://y.io/p||![b😀http://x.ioa](! !`|https://y.io/pword😀\n> ", + "expected": "b [ a !" + }, + { + "input": " \nb漢a |b) \nword…]>😀http://x.iob) )…b>)[", + "expected": "b漢a b) word ] 😀 ) b )[" + }, + { + "input": "` _<\u0000http://x.io\u0000||)éword #!漢`😀||~>#||](](!http://x.io\n)", + "expected": "< ](](! )" + }, + { + "input": "http://x.iohttp://x.iohttp://x.ioé<é~](é…[\u0000**b_漢`é`a||\n漢é`é! **[a](**https://y.io/p😀~http://x.io😀…", + "expected": "[ b 漢 é a 漢é é! [a ](" + }, + { + "input": "a](](😀`word![😀|](\na]\n)a<)word", + "expected": "a](](😀 word a<)word" + }, + { + "input": "~])#b漢 #b", + "expected": "]) b漢 b" + }, + { + "input": "é**\u0000_](#](<|||< ", + "expected": "é ]( ](< <" + }, + { + "input": "#é~https://y.io/phttps://y.io/p)http://x.io !\u0000word[  …\u0000 [é>http://x.io", + "expected": "😀] 😀! ] [ a <" + }, + { + "input": "\u0000|**>😀https://y.io/p |…**![\n\u0000_\u0000[漢😀`**]~`http://x.io)", + "expected": "😀 ![ [漢😀 ]" + }, + { + "input": "…**[](a~|~<é![!!\u0000#…#](http://x.io [**word~[**http://x.io\n", + "expected": "[](a <é![!! ]( [ word [" + }, + { + "input": ">http://x.io~https://y.io/p\u0000|]word**![)>**https://y.io/p))!", + "expected": "]word ![)" + }, + { + "input": " \u0000)||``![", + "expected": ") ![" + }, + { + "input": " word…![ _…漢b|~", + "expected": "word ![ 漢b" + }, + { + "input": ">!bbhttps://y.io/p> …wordhttp://x.ioé~😀a\u0000#<\u0000\u0000b… !https://y.io/p…||)[http://x.io||> 漢**)", + "expected": "漢 b ! 漢 )" + }, + { + "input": "\n![ …<] #`https://y.io/pword#]ahttps://y.io/p](", + "expected": "![ <]" + }, + { + "input": ">~ **||éb\u0000https://y.io/pé a~_ **https://y.io/p~_)a\u0000||)**https://y.io/p~", + "expected": ")" + }, + { + "input": "|\n…a||é 😀`[ _", + "expected": "a é 😀 [" + }, + { + "input": "…<**a ](漢   ]…", + "expected": "< a ](漢 ]" + }, + { + "input": "](||)~||_[\u0000", + "expected": "]( [" + }, + { + "input": "<||!é\u0000 … word😀](<> !#  ** \u0000_[b]` !", + "expected": "< !é word😀](< ! [b] !" + }, + { + "input": "http://x.io||\u0000_\u0000_>a\u0000a#`< 😀…_", + "expected": "a a < 😀" + }, + { + "input": "é](`", + "expected": "é](" + }, + { + "input": " <é![](b] >_)!漢 word`http://x.io|)#é< …)#é\u0000http://x.io!>https://y.io/p", + "expected": "<é https://y.io/p](https://y.io/p>](", + "expected": "< b ! 😀b ! 😀" + }, + { + "input": "#)… **[||`word![\n word ](]||_)# ](éaaé||…[word<)||||`😀", + "expected": ") [ ) ](éaaé 😀" + }, + { + "input": "\u0000[>#word\n|https://y.io/p<)|😀https://y.io/p[||<]漢#!>", + "expected": "[ word <]漢 !" + }, + { + "input": "<**)…||\u0000![]<~![b\u0000**ahttps://y.io/p||", + "expected": "< )" + }, + { + "input": "ba`word~~](_漢#|)||…![** http://x.io#\nbword#word |…_|\u0000!https://y.io/p~é", + "expected": "b a word ]( 漢 ) ![ bword word !" + }, + { + "input": "[](😀`<[||https://y.io/p)http://x.io😀#![| ] <\n**é>word漢 ", + "expected": "[](😀 <[ ] < é word漢" + }, + { + "input": ")||#😀`#word#||é", + "expected": ") é" + }, + { + "input": "~http://x.io|| !~[~ ` _漢|b", + "expected": "! [ 漢 b" + }, + { + "input": " \n!`![` _a |||| éa]😀`)<\u0000![` ", + "expected": "! ![ a éa]😀 )< ![" + }, + { + "input": " _😀http://x.io~é\nhttp://x.io[)!!漢b😀a ~word>](](  `word!😀](~😀\u0000** ](||word](", + "expected": "😀 word ](]( word!😀]( 😀 ]( word](" + }, + { + "input": " 😀](<", + "expected": "😀](<" + }, + { + "input": "😀\n |_)\u0000|~)\n\u0000_https://y.io/péa \u0000é… |漢 <||<", + "expected": ") [] é 漢 < <" + }, + { + "input": "b\u0000…! word#a ", + "expected": "b ! word a" + }, + { + "input": "é)]`| #\u0000 … |`\n", + "expected": "é)]" + }, + { + "input": "_word_", + "expected": "word" + }, + { + "input": "ahttp://x.io~)\u0000\u0000word漢https://y.io/pa#)**https://y.io/p>_!_http://x.io>~||](_\nahttp://x.io word![", + "expected": "a word漢 a word![" + }, + { + "input": "bhttps://y.io/p**https://y.io/p||\nb[_` ![https://y.io/pb", + "expected": "b b[ ![" + }, + { + "input": "😀\u0000[]https://y.io/p>\u0000<é…b", + "expected": "😀 [] <é b" + }, + { + "input": "word **`[\n)éhttp://x.io **](http://x.io| **`http://x.io\n [||]!b_ ]()![éword](#é`**||||https://y.io/p#)", + "expected": "word )é" + }, + { + "input": "](worda\n😀]> [) aa>|> 😀word\n!http://x.io![| ![| 漢[#…[||https://y.io/p", + "expected": "](worda 😀] [) aa 😀word ! ![ 漢[ [" + }, + { + "input": "word", + "expected": "word" + }, + { + "input": "http://x.io ])`_ _![](`||![~  ||", + "expected": "]) ![](" + }, + { + "input": "😀[** [ ****é|>>)bb漢[](**)![![`[\u0000word](![]é||http://x.io[ ", + "expected": "😀 [ é )bb漢[![![ [ word](![]é" + }, + { + "input": "](#…]|漢`](#é~漢 ~https://y.io/pb😀|]", + "expected": "]( ] 漢 ]( é 漢 ]" + }, + { + "input": "b#!! |>< https://y.io/phttps://y.io/p<](http://x.io  \u0000… 漢b…漢 ]éword 😀**……**>~", + "expected": "b !! < 漢b 漢 ]éword 😀" + }, + { + "input": "_]<]![[]**<||](漢b|]b<> 😀\n**_||a#)![\u0000![b漢", + "expected": "]<]![[] < a )![ ![b漢" + }, + { + "input": "b~…a<<_http://x.ioa<😀>#a😀https://y.io/p<>![", + "expected": "b a<<" + }, + { + "input": ") **https://y.io/pword<😀_\u0000` 😀 b 😀#)](……a!<) \u0000漢![word", + "expected": ") 😀 b 😀 )]( a!<) 漢![word" + }, + { + "input": "!>]||](>![ <#é ||😀a** 漢>< )http://x.io!", + "expected": "! ] ]![b!< 😀a 漢 < )" + }, + { + "input": " ||[**||https://y.io/pword]word…", + "expected": null + }, + { + "input": ")http://x.iohttps://y.io/p  …😀 https://y.io/p](https://y.io/p😀]](http://x.io>https://y.io/p\n](**😀**\u0000]( ||a_aawordhttp://x.io![😀)][\n>漢", + "expected": ") 😀 ]( 😀 ]( a aaword ][ 漢" + }, + { + "input": "]![`https://y.io/pword`)\n!\né||~éword| )http://x.io漢 #~…é\n_\n <**]\u0000\néé||<)b wordb", + "expected": "]![ ! é <)b wordb" + }, + { + "input": "_#éé)_…_ |||ahttp://x.io", + "expected": "éé) a" + }, + { + "input": " ", + "expected": null + }, + { + "input": "https://y.io/phttps://y.io/p**_\u0000#](é\n<>![ ]漢]https://y.io/pb#b[![", + "expected": "](é < ![ ]漢]" + }, + { + "input": "b>#**~_é![!é😀>\n<éa)~…![![![ é !||](#|>word", + "expected": "b é![! é😀 <éa) ![![![ é ! ]( word" + }, + { + "input": "]](] é**é\u0000_******#!**#]#**]](\n", + "expected": "]](] é é ! ] ]](" + }, + { + "input": "\u0000\n  _|http://x.io>_)]…))>])a~_é|**< 漢]#< >b~ <`\u0000#b**<", + "expected": ")) ])a é < 漢] < b < b <" + }, + { + "input": "||`))…**a…|https://y.io/p>[>[_", + "expected": ")) a" + }, + { + "input": " é [ ](https://y.io/pé|`<漢<…]https://y.io/p", + "expected": "é [ ]( <漢< ]" + }, + { + "input": "a漢]|<>😀 \nhttp://x.io~**||~|http://x.io**#<<", + "expected": "a漢] < 😀" + }, + { + "input": "![https://y.io/p[#|>b||>_|😀漢||…é\na…`https://y.io/p😀!😀 ](", + "expected": "word](é 漢 ]( []é 漢 😀!😀 ](" + }, + { + "input": "`! 漢<** ` !https://y.io/pa\u0000… `)bab é<~…**https://y.io/p](!<\n ", + "expected": "! 漢< ! )bab é<" + }, + { + "input": "#…😀é![]>漢_b]é\n\u0000**![é ~**https://y.io/p\u0000é||word__||||![_", + "expected": "😀é![] 漢 b]é ![é é ![" + }, + { + "input": "<", + "expected": "<" + }, + { + "input": ">**~|](![] http://x.io~|😀!a#~https://y.io/pa[b >](word~[😀>>|`]a~ \n_http://x.io>éhttp://x.io\n!", + "expected": "](![] ](word [😀 ]a !" + }, + { + "input": "!](!http://x.io<\n_~]#…||…`https://y.io/p`](<](**https://y.io/p|é\n~ #>![<)~![#![#!", + "expected": "!](! ] ![<) ![ ![ !" + }, + { + "input": "\n\n#>||||b)http://x.iobwordé<", + "expected": "b)" + }, + { + "input": ">||…a![|!http://x.io word) )……)https://y.io/p_ http://x.io****` \u0000![]|a|](漢#**#** [](**](", + "expected": "a![ ! word) ) ) ![] a ](漢 []( ](" + }, + { + "input": ">😀`!![>é漢b漢](http://x.io~||😀\u0000https://y.io/p\u0000b é[![\n #![!>[b](\n😀[b_\u0000_>`http://x.io\u0000**](😀\n`](|", + "expected": "漢 b é[![ ![! [b]( 😀[b ](😀 ](" + }, + { + "input": ">`**||****😀\u0000é](漢http://x.iob", + "expected": "! b![ a[😀 word 漢" + }, + { + "input": "~word![é\nhttp://x.io**https://y.io/p!http://x.io[](\n](!éaa!`é][**http://x.io](word~bé) ", + "expected": "word" + }, + { + "input": "b~a![~😀é😀||", + "expected": "![] ![a漢 b é b[ !word![ é😀" + }, + { + "input": "|a😀]http://x.io\u0000 !!漢 漢https://y.io/p\nhttp://x.io \n 漢", + "expected": "a😀] ! !漢 漢 漢" + }, + { + "input": "!é", + "expected": "!é" + }, + { + "input": "|a]|bhttps://y.io/p", + "expected": "a] b" + }, + { + "input": "~~|…é# é`#<[\n<#]b]https://y.io/p \n!ahttps://y.io/p😀~) bb", + "expected": "é é < [ < ] b] !a bb" + }, + { + "input": "![!**_<|]) ||[>)[ 漢#_b ", + "expected": "![! < ]) [ )[ 漢 b" + }, + { + "input": "https://y.io/p](éhttp://x.io\u0000||\u0000http://x.io~**😀", + "expected": "](" + }, + { + "input": "`b é |\na é!", + "expected": "b é a é!" + }, + { + "input": "漢`](https://y.io/p>]()\n! ||~ ", + "expected": "漢 ]( !" + }, + { + "input": "](~ >~#\nword", + "expected": "]( b ~a<||>a~|[~😀é||…aa` …", + "expected": ")漢 [ b a< aa" + }, + { + "input": "a)[", + "expected": "a )[" + }, + { + "input": "#https://y.io/p 😀 word#||!#<[[ _…`!!é`** http://x.iohttp://x.io<\u0000!_[漢a )`**`!", + "expected": "😀 word ! <[[ !!é ! [漢a ) !" + }, + { + "input": "!!http://x.io", + "expected": "!!" + }, + { + "input": "\u0000|||😀a\nhttp://x.io`… 😀_b!** é漢aé\u0000]a aa![", + "expected": "😀 a 😀 b! é漢aé ]a aa![" + }, + { + "input": "|| aword`! ](~#`b…||](****  #http://x.io](a`_]([ 😀word \u0000", + "expected": "]( 😀word" + }, + { + "input": "\nword  [http://x.io)])][http://x.iohttp://x.io](#]( <||aé\u0000||!>]||b \u0000\u0000😀é]", + "expected": "word [ < ! ] b 😀é]" + }, + { + "input": "http://x.io]([_]#漢https://y.io/p😀|\u0000#!b] ||||>)  !", + "expected": "漢 !b] ) !" + }, + { + "input": "http://x.io**wordhttps://y.io/pa![ ] a!**[** …](<_\néa😀b](>https://y.io/p…>…", + "expected": "] a! [ ](< éa😀b](" + }, + { + "input": " a _))**word||\n>\nbb`\n|| \u0000|| \u0000>]", + "expected": "b b ]" + }, + { + "input": " ![", + "expected": "![" + }, + { + "input": "`\n](|| 漢![#]](](~_漢![ 😀** [b\u0000b", + "expected": "]( 漢![ ]](]( 漢![ 😀 [b b" + }, + { + "input": "a<#a#http://x.io|| 😀a![]", + "expected": "a< a a漢! ! ] ] 😀a![]" + }, + { + "input": " 漢a`[|é ` ||http://x.io😀", + "expected": "漢a [ é" + }, + { + "input": "||)\n", + "expected": ")" + }, + { + "input": "<|", + "expected": "<" + }, + { + "input": "<[!_\u0000漢|]\u0000 \u0000!]() **漢[https://y.io/p)…#>)||", + "expected": "](![)!é!" + }, + { + "input": "![`…https://y.io/p`]( \n😀~||)!**`]**>~**wordhttp://x.io` [\nword)]!](|< **\n<\n<漢", + "expected": "! ] word [ word)]!]( < < < 漢" + }, + { + "input": "![https://y.io/p|||>](|\n\u0000", + "expected": "![" + }, + { + "input": " <~**https://y.io/p )|**`😀|**a😀# ![)~# ]([) <]]](http://x.io _\u0000#<\u0000**b", + "expected": "<] < b" + }, + { + "input": "aéhttps://y.io/p#~[||é#[`ahttps://y.io/p \n<>aword😀<>https://y.io/p](|a~!漢](…!b]\n…|| !", + "expected": "aé !" + }, + { + "input": "||", + "expected": null + }, + { + "input": "! \u0000||http://x.io<`b_ \u0000\n 漢http://x.io|| a!😀]https://y.io/p![||~>>aé||a~>", + "expected": "! a!😀] a" + }, + { + "input": "a||`http://x.io`\u0000", + "expected": "a" + }, + { + "input": "\u0000\n漢![![😀]\n😀\u0000](wordéé~||http://x.io!![", + "expected": "漢![![😀 ] 😀 ](wordéé" + }, + { + "input": "b**😀![!#]![)>! [\n\n>\n|a#… ](wordéhttp://x.io[**b](_`é\n\n", + "expected": "b 😀![! ] ![) ! [ a ](wordé" + }, + { + "input": " ||_][\n](漢_||…!>…é )é >word~https://y.io/p!a![ é漢\u0000b |#é!](", + "expected": "! é )é word é漢 b é!](" + }, + { + "input": "](~a_\u0000word`)\n😀<)😀`#", + "expected": "]( a word ) 😀<)😀" + }, + { + "input": "…**>![`https://y.io/p #!http://x.iohttps://y.io/p_ word`>_wordhttp://x.io😀**_#éa#a_", + "expected": "![ ! word word" + }, + { + "input": "b|[word_ […a](**é#)http://x.io#😀!|word![…\u0000b\n>https://y.io/pa漢**]( ![](]||wordhttps://y.io/p!|é", + "expected": "b word [ a b 漢 ]( ![](] word" + }, + { + "input": "![漢\nb\u0000|_]a[**http://x.io)b]||>~漢)漢😀 **…|\u0000`|| http://x.io](é\u0000http://x.io ~é", + "expected": "![漢 b ]a[ é" + }, + { + "input": "😀~https://y.io/p[ http://x.io!_><漢>a`[ #wordword  a\nword\n<a)", + "expected": "![![漢 wordword a word < a)" + }, + { + "input": "![) >)_ http://x.io😀|\naa`>##a", + "expected": "![) ) aa a" + }, + { + "input": "`~_]http://x.io]([||)漢wordééa _a http://x.io#!#|word**…a>#~", + "expected": "] wordééa a a" + }, + { + "input": "**\nhttps://y.io/p", + "expected": null + }, + { + "input": "…a`…b**", + "expected": "! ! )![😀!a b" + }, + { + "input": "_https://y.io/pa\u0000😀 …~  >[\u0000|| |>漢|é~é<#`漢 ||http://x.io >漢![漢\n)", + "expected": "😀 [ 漢![漢 )" + }, + { + "input": " >_]<**\u0000\u0000`😀b![)**https://y.io/p**漢b", + "expected": "]< 😀b![) 漢b" + }, + { + "input": "[é~](_)]([[\nhttps://y.io/p\u0000 `\n|!_\u0000|https://y.io/p #", + "expected": "é ]([[ !" + }, + { + "input": "word😀 a\u0000 https://y.io/pb\u0000漢漢#`](é [http://x.io\u0000_~!漢~]]word\u0000", + "expected": "word😀 a 漢漢 ](é [ !漢 ]]word" + }, + { + "input": "\u0000[http://x.io~**😀>![\n||)](https://y.io/p**https://y.io/p漢)word", + "expected": "[ word" + }, + { + "input": "漢 wordé||](] |😀 http://x.ioa[\nword[b]!😀||\u0000\u0000 a\u0000!\na\u0000\n]", + "expected": "漢 wordé a ! a ]" + }, + { + "input": "…😀#漢|é)a) word)https://y.io/p# < a_ \u0000|漢éhttps://y.io/phttps://y.io/pé_a|||]|| a](]word", + "expected": "😀 漢 é)a) word) < a 漢é a](]word" + }, + { + "input": "\u0000😀http://x.io!![a**~http://x.io|<**[bb)…a…#😀[](~\n![![<éa\u0000<[ [**# <>…é", + "expected": "😀 ![a a 😀[]( ![![< éa <[ [ < é" + }, + { + "input": "~漢\u0000![![||éhttp://x.io<](http://x.io!é ! **)é![", + "expected": "漢 é![" + }, + { + "input": "\n_>", + "expected": null + }, + { + "input": "_>]b…[\u0000<**]([<`", + "expected": "]b [ < ]([<" + }, + { + "input": " |\u0000", + "expected": "😀![ )b 漢漢 漢 ]( ![  http://x.io😀#漢\n_> 漢\u0000 **> )](a|**#><)|**<", + "expected": "é](wordé 漢 )](a <) <" + }, + { + "input": "`http://x.io😀   #b…**", + "expected": "b" + }, + { + "input": " https://y.io/phttp://x.io>]é😀\u0000<ééé~#~_#[] ! ||é\nwordword`#", + "expected": "![  ]", + "expected": "😀 漢]( !] é ![ ]" + }, + { + "input": "**||\u0000#>é[!](_漢\n)[**\n_#>\n…]||é)…  ![]a>http://x.iohttp://x.io|", + "expected": "é) ![]a" + }, + { + "input": "_b](… <<\n…😀`]😀~éhttp://x.iob漢#[\néword[**a…**![\u0000~", + "expected": "b]( << 😀 ]😀 é éword[ a ![" + }, + { + "input": "![ b|[_", + "expected": "![ b [" + }, + { + "input": "\n https://y.io/p ~**_||])[word", + "expected": "])[word" + }, + { + "input": "[http://x.io )<`](😀 ~](`漢)!", + "expected": ")< !" + }, + { + "input": " http://x.ioword! `~](\u0000 ]( http://x.io]", + "expected": "]( ](" + }, + { + "input": "#]", + "expected": "]" + }, + { + "input": "[http://x.iohttps://y.io/p \u0000|~[|http://x.io![http://x.io é\n![ < )\néb\n![| <**\u0000😀http://x.io ~_😀||![>", + "expected": "b ) 😀 ![" + }, + { + "input": "**!漢)漢 ~…https://y.io/p|# ||!_éhttp://x.iohttp://x.io", + "expected": "!漢)漢 ! é" + }, + { + "input": "|||])", + "expected": "])" + }, + { + "input": "bword!![漢[ <|||", + "expected": "bword!![漢[ <" + }, + { + "input": "https://y.io/p>_漢\n~~…[|_![https://y.io/p<…![漢)a**b**)…[[wordb\u0000b](#a😀", + "expected": "[ ![ ![漢 )a b ) [[wordb b]( a😀" + }, + { + "input": " ||<#\u0000b>#\n", + "expected": "< b" + }, + { + "input": "aawordhttp://x.ioé\u0000|", + "expected": "aaword é" + }, + { + "input": "<_![|**\u0000漢😀 [ba😀`https://y.io/p<](wordhttp://x.io![ ](] #é_", + "expected": "< ![ 漢😀 [ba😀 ](] é" + }, + { + "input": "😀]()\u0000\u0000word#~http://x.io ~#😀**[a)a>é )http://x.io)…)]https://y.io/p!~)**` #||……](]", + "expected": "é< ] ](]" + }, + { + "input": "http://x.ioaword😀](\u0000…\n)\n]\u0000\u0000[\n漢é…é)\n\u0000a)", + "expected": "]( ) ] [ 漢é é) a)" + }, + { + "input": "**![!\nhttps://y.io/pb…![~a\n漢…` ](漢", + "expected": "![! b ![ a 漢 ](漢" + }, + { + "input": "_|a!a# ](|< é! [ |…>\u0000…](a~_https://y.io/p", + "expected": "a!a ]( < é! [ ](a" + }, + { + "input": "http://x.io…#!漢http://x.io", + "expected": "!漢" + }, + { + "input": "_😀éhttp://x.io ![[!_ http://x.iob_!😀](b漢a)a# ~b", + "expected": "😀é a b" + }, + { + "input": "||b]**!https://y.io/p#>", + "expected": "😀 ]) ]( ]( a !" + }, + { + "input": "#||~**é!\n~![](]( éhttp://x.io ](http://x.io<é![|bhttps://y.io/p~\u0000 )|![**", + "expected": "é! ![" + }, + { + "input": " [😀 ||é#漢ééb\n[漢~>~](b… #||`", + "expected": "[😀" + }, + { + "input": "!>漢<…word|||~#\n<](\n…漢> `a…>é||]![\u0000 😀漢\u0000_<_ https://y.io/p", + "expected": "! 漢< word ]![ 😀漢 <" + }, + { + "input": "<||a_b😀[**_**b|![ a", + "expected": "<) ]( ![![]( a" + }, + { + "input": "~![# >_ [é_<)😀b)![]|#**||…](…http://x.io**\nword", + "expected": "![ [é <)😀b)![] ]( word" + }, + { + "input": "漢)\n![http://x.io \n", + "expected": "漢) ![" + }, + { + "input": "…a漢…!a)**word![ #  ", + "expected": "a漢 !a) word![" + }, + { + "input": "**", + "expected": null + }, + { + "input": "漢|_éhttps://y.io/phttps://y.io/phttps://y.io/paé)\u0000https://y.io/p# >http://x.ioé##![漢)](…>||…>](]\u0000]é😀 ahttps://y.io/p||) ", + "expected": "漢 é" + }, + { + "input": "!b| ||éword…](~`** ](\u0000~**[a)éword", + "expected": "!b éword ]( ]( [a)éword" + }, + { + "input": "https://y.io/pa**…[`!…  >||[` ] <]#!!#a](![😀a)_>", + "expected": "[ ! [ ] <] !! a](![😀a)" + }, + { + "input": "[~||~ ", + "expected": "[" + }, + { + "input": "é||\n 😀http://x.io#漢 **||!<", + "expected": "é !<" + }, + { + "input": "\u0000 !)])\u0000\n…é~http://x.iowordhttp://x.io ~word !||a_  |é![", + "expected": "!)]) é word ! a é![" + }, + { + "input": "http://x.io[! ||)😀………http://x.iohttp://x.io😀\n\n**)](a) ]…b…**漢wordéword!][ wordhttp://x.iob ", + "expected": ")😀 ) ] b 漢wordéword!][ word" + }, + { + "input": "||! ![http://x.io)! ||éhttps://y.io/p", + "expected": "é" + }, + { + "input": "_~https://y.io/p <漢\n>http://x.io_b漢**![|https://y.io/p**é`http://x.iohttp://x.io!< `…![https://y.io/p~ **||\u0000\n ]!![\u0000a", + "expected": "<漢 ![ ]!![ a" + }, + { + "input": "#😀#|****é`**\u0000!**a]http://x.io[](\u0000_<)]https://y.io/p… ![https://y.io/pb http://x.io", + "expected": "😀 é ! a] <)] ![" + }, + { + "input": "https://y.io/pbb`> a#ab__#|\n#😀](~] [漢\n", + "expected": "a ab 😀]( ] [漢" + }, + { + "input": " é)_**word[>😀!>#a", + "expected": "é) word[ 😀! a" + }, + { + "input": " \nhttp://x.io😀http://x.io", + "expected": null + }, + { + "input": "…#]漢", + "expected": "]漢" + }, + { + "input": "b!…![~…", + "expected": "b! ![" + }, + { + "input": "# _ ", + "expected": null + }, + { + "input": "](😀https://y.io/p é![>#é**< 漢 ", + "expected": "](😀 é![ é < 漢" + }, + { + "input": "word😀\n\u0000word[||~[ \u0000![ ||<", + "expected": "word😀 word[ <" + }, + { + "input": "[](!)<|漢😀 _]é[… |**]http://x.iob😀", + "expected": "[](!)< 漢😀 ]é[ ]" + }, + { + "input": "**…`_b\nhttp://x.io !é\n[<_ |#<_ |]b", + "expected": "b !é [< < ]b" + }, + { + "input": "**<))||http://x.io#**`|\n )…~>** >a😀 \u0000_))漢", + "expected": "<)) ) >>>b~é ![`# http://x.iohttp://x.io漢http://x.io ~**…> a漢![||https://y.io/p_a", + "expected": "a]()漢" + }, + { + "input": " a`ab>`https://y.io/p>é", + "expected": "a ab" + }, + { + "input": "**…http://x.io\n\n…\n< \u0000)é]**a](! )](é)[é](**!wordword", + "expected": "< )é] a](! )]( é)[é]( ! wordword" + }, + { + "input": "~]ba…[]#\n![`\n", + "expected": "]ba [] ![" + }, + { + "input": "😀`||…é_a~[\n**>", + "expected": "😀 é a [" + }, + { + "input": "a||[漢\n\n漢 ~ éword]![]![a\u0000", + "expected": "a [漢 漢 éword]![]![a" + }, + { + "input": "`http://x.io ![](_[…>b||https://y.io/p[ `é||](https://y.io/pa![` ](b |)_word ~_\u0000] ]\n#`", + "expected": "word ] ]" + }, + { + "input": "_\n** ](a`😀)`~\n http://x.io~ ##)>漢 ||![~]]", + "expected": "](a 😀) ) 漢 ![ ] ]" + }, + { + "input": "|||http://x.io||é_", + "expected": "é" + }, + { + "input": "|| aéhttps://y.io/phttps://y.io/p](awordhttp://x.ioa😀éword https://y.io/p_!http://x.io", + "expected": "aé" + }, + { + "input": ">|| 漢\u0000…b~bb _word__\n… _ [![]é", + "expected": "漢 b bb word [![]é" + }, + { + "input": "`漢http://x.io\n]([`![]~\u0000_b", + "expected": "漢 ]([ ![] b" + }, + { + "input": "_b_#](😀wordhttps://y.io/p||word]😀 a>)# ~`>http://x.io!b", + "expected": null + }, + { + "input": " _é`!`😀!_](_ [http://x.io", + "expected": "é ! 😀! ]( [" + }, + { + "input": "b\n|漢…b  漢 ~|word_>https://y.io/p 漢b](~< [ https://y.io/p#[…`< \n\u0000é!)http://x.io| ", + "expected": "b 漢 b 漢 word 漢b]( < [ < é!)" + }, + { + "input": ">#http://x.ioword**](😀b`\nhttps://y.io/p[\n~|]()[|** 漢~|`bé`)| a[>b[|![http://x.io", + "expected": "漢漢[ ]( b[ ![" + }, + { + "input": " éhttps://y.io/p> a ![<漢<##😀>漢 _ 😀\n**http://x.io](\u0000)[>!<…\u0000#_ ](||)", + "expected": "é a !<" + }, + { + "input": " 😀https://y.io/p<**漢||éhttp://x.io\u0000😀😀!`b`…!<漢**\nhttp://x.ioa !](…http://x.io\n\n__)http://x.io](http://x.io", + "expected": "😀 😀😀! b !<漢 !]( )" + }, + { + "input": " é]😀!\u0000[\u0000)](`https://y.io/p~http://x.io>\u0000 ](![)](<é…https://y.io/p\u0000` ", + "expected": "é]😀! )](<é" + }, + { + "input": " )b …", + "expected": ")b" + }, + { + "input": "![http://x.io > ~**漢https://y.io/p<😀é [bb😀 |", + "expected": "![ 漢 [b b😀" + }, + { + "input": "||😀<****https://y.io/p_||](wordhttp://x.io]word!<**>http://x.iohttps://y.io/p!<|| ![a漢**|![](![[b**!![[!word", + "expected": "](word ![a漢 ![](![[b !![[!word" + }, + { + "input": "|", + "expected": null + }, + { + "input": "😀|bé漢https://y.io/p…||**<\nb<…http://x.io_|~~ ||https://y.io/p|> [`漢 漢||漢>]漢word)[ ", + "expected": "😀 bé漢 [ 漢 漢 漢 ]漢word)[" + }, + { + "input": "`~|||éba <_……![漢", + "expected": "a! ![ < ]😀 word漢 ![ 漢 ](![] < ![漢" + }, + { + "input": "\u0000]http://x.io](\u0000](![~é]>](](😀…<[)]http://x.ioé[https://y.io/p)éhttps://y.io/phttp://x.io\u0000word ~)http://x.io![ 😀_>", + "expected": "] ](![ é] ](](😀 <[)] word ) 😀" + }, + { + "input": "a漢\n漢](http://x.io|>\u0000[😀~!a_|` <**|**é😀\u0000 ![  ||😀http://x.io\u0000#http://x.ioab]]([word…\n ", + "expected": "b b ] a 😀 [ word) 😀" + }, + { + "input": "http://x.io😀 a漢|https://y.io/pwordhttps://y.io/p~<` \u0000", + "expected": "a漢" + }, + { + "input": "word !)\u0000||a~\u0000a )é[!]\n[[<😀https://y.io/pé![_\u0000#> >漢!** …https://y.io/pb!!)", + "expected": "!) a a )é[!] [[<😀 漢! !)" + }, + { + "input": "word😀😀\u0000[漢", + "expected": "word😀😀 [漢" + }, + { + "input": "[漢酅a wordb]", + "expected": "[漢 é a wordb]" + }, + { + "input": ">_![|word]||![b~]`word]https://y.io/p|!…_https://y.io/p|a![http://x.io<é`![ 漢https://y.io/p a ** ](!ahttp://x.io…|", + "expected": "![ word] ![b ] word] 漢 a ](!a" + }, + { + "input": "<[!…~![ 😀\u0000**a![** ! b**…https://y.io/p_\u0000`~http://x.io漢漢#漢**_😀|| !~[!😀http://x.ioword", + "expected": "<[! ![ 😀 a![ ! b ! [!😀" + }, + { + "input": "||word…https://y.io/phttps://y.io/p![ word~#)https://y.io/péb #>]!\nhttp://x.ioé[](#_http://x.io_]**[**", + "expected": "word word ) ]!" + }, + { + "input": "漢😀é> _[b]( http://x.io**<](|[`worda>| ||~!word ](![![[![\u0000b漢`", + "expected": "漢😀é [b]( !word ](![ ![[![ b漢" + }, + { + "input": "word!_\u0000_~#><<||😀 !\n**a](\nhttp://x.io** _)!_😀[", + "expected": "😀 ](< ! 😀[" + }, + { + "input": "![](_||\n||\n_word[😀)", + "expected": null + }, + { + "input": "#|](😀é<\n![](||![漢\n||_# )https://y.io/p!\u0000`\n||>b…[…]… word!>😀", + "expected": "](😀é< b [ ] word! 😀" + }, + { + "input": "#~a] **` **`|!]http://x.io**[[] ]http://x.io#_\u0000~!https://y.io/p … _||…", + "expected": "a] !] ] ~](http://x.ioé`bab|b#https://y.io/p", + "expected": "b ](" + }, + { + "input": ")](aa漢漢**a#… ab ![\u0000\u0000|😀![ <éé`_ >`~", + "expected": ")](aa 漢漢 a ab ![ 😀![ <éé" + }, + { + "input": "](https://y.io/p~ ![]([<|](b…#|", + "expected": "]( ![]([< ](é😀…\u0000漢>é]é`_b`\u0000\n <<]![]…>< é[<  _||||<", + "expected": "é😀 漢 é]é b <<]![] < é[< <" + }, + { + "input": "<>`\u0000<…> http://x.ioa😀#**\u0000](é`https://y.io/p)😀_!😀>😀漢", + "expected": "< < ](é" + }, + { + "input": "||||`a [**!b éhttp://x.iohttps://y.io/pé😀é `ahttp://x.io", + "expected": "a [ !b é a" + }, + { + "input": "\u0000#http://x.io", + "expected": null + }, + { + "input": "](word<|`||wordé`b éa**", + "expected": "](word< wordé b éa" + }, + { + "input": "https://y.io/p漢漢worda ![ `http://x.io![**b wordb_baé", + "expected": "![ )](\u0000![)`>word", + "expected": ") < word" + }, + { + "input": "` !](<|b ~\u0000漢😀https://y.io/p~a>é> http://x.io ]( ~|||\n[漢 [||https://y.io/p`_<_|_]~https://y.io/p)||", + "expected": "!](< b 漢😀 ](" + }, + { + "input": "…a漢ab#`worda||https://y.io/p]( ]![_!**`<#[ 😀a#|](aba漢 > ># word…é é[", + "expected": "a漢ab worda ]![ ! < [ 😀a ](aba漢 word é é[" + }, + { + "input": "\u0000 **<\u0000word#https://y.io/p#>…!#", + "expected": "< word !" + }, + { + "input": "…a[…](`word[😀[|<\u0000`http://x.io!http://x.iohttp://x.ioa]\u0000![**||||http://x.io)|\u0000![ ~\n#https://y.io/p](https://y.io/p", + "expected": "a ![" + }, + { + "input": "])aéaba", + "expected": "])aéaba" + }, + { + "input": "😀**", + "expected": "😀" + }, + { + "input": "😀\u0000>word|| word\nhttp://x.io]**`_é#~漢#😀**_!\n_**\u0000**…#b)b!http://x.io # \u0000b ", + "expected": "😀 word word b)b! b" + }, + { + "input": "|é# )\u0000", + "expected": "é )" + }, + { + "input": " !![![word|<😀#[word#a**\n``<é)word\u0000 [\u0000\u0000http://x.io…<é>[)\u0000\u0000![#é word\nhttps://y.io/p ", + "expected": "!![![word <😀 [word a <é)word [ <é [) ![ é word" + }, + { + "input": "] …~||<[漢>` < ]b", + "expected": "] <[漢 < ]b" + }, + { + "input": "aaéé>||_[\n~#<😀a!\n é…](\u0000||<é漢[`http://x.io_<\u0000](…![a~<漢", + "expected": "aaéé <é漢[ ]( ![ http://x.io", + "expected": "😀" + }, + { + "input": "😀\u0000b**](é![ba**éword \n![ \u0000", + "expected": "😀 b ](é![ba éword ![" + }, + { + "input": "[漢 **|<é![ [>", + "expected": "[漢 <é![ [" + }, + { + "input": "http://x.io](é漢)\u0000…b\u0000>😀a## [||é![|https://y.io/p)!bhttps://y.io/p\n#`]", + "expected": "b 😀a [ é![ ]" + }, + { + "input": "b漢<](># ……_http://x.io||~|漢word||#a!b漢…!|ahttp://x.ioword)b||](](\n**", + "expected": "漢word ](](" + }, + { + "input": "`](`|| < _\n😀<\u0000… **` _😀https://y.io/p#é| #|`http://x.io # word>", + "expected": "]( < 😀< 😀 word" + }, + { + "input": "![ http://x.io#", + "expected": "![" + }, + { + "input": "!]()~a||[||** 😀_a)![_<]( ]( ", + "expected": "!]() a 😀 a)![ _  >b `https://y.io/p 漢#`\u0000a>", + "expected": "![) ]aa é b 漢 a" + }, + { + "input": "!\n`](~éé]([wordb\u0000`)é#|ahttp://x.iohttps://y.io/p_|#\u0000**", + "expected": "! ]( éé]([wordb )é a" + }, + { + "input": " é | https://y.io/p||😀  ! #||]…#word`!||https://y.io/p~😀http://x.iohttps://y.io/p\n_**>[**\n…<", + "expected": "![](]😀 worda漢 [ <" + }, + { + "input": "_ ![||~||word] ]<`]** !| \n|~\n> | [||>)!\u0000a~😀||é|", + "expected": "![ word] ]< ] ! [ é" + }, + { + "input": " 😀https://y.io/p![_**漢> bé", + "expected": "😀 bé" + }, + { + "input": "|aword~](!a!", + "expected": "aword ](! a!" + }, + { + "input": "**]()_~~http://x.iob[a![漢 _https://y.io/pa[~|word**![", + "expected": "]()" + }, + { + "input": ">a>|https://y.io/pa😀|😀http://x.io> ****word\n)éhttp://x.io word😀b é", + "expected": "aé[a word )é word😀b é" + }, + { + "input": "![** http://x.iohttp://x.io**`](b…\u0000 ![]]( _ !\nword[\u0000…]| b😀 _b", + "expected": "![ ![ ] ]( ! word[ ] b😀 b" + }, + { + "input": "a][word\u0000b", + "expected": "a][word b" + }, + { + "input": " ](`[<[word]` ![|word ~漢<[http://x.iohttp://x.io![](**[…`[_😀[~** >漢#é>##http://x.io ||", + "expected": "]( [<[word] ![ word 漢<[ [ 😀[ 漢 é" + }, + { + "input": "****#**[_b[](😀 word#!… é word)word😀http://x.io||`😀](**", + "expected": "b[word😀" + }, + { + "input": "||\n#\u0000>![https://y.io/p || \u0000 http://x.io\u0000>](word<[a)a~_ ~ >)béb!http://x.iohttps://y.io/p >\u0000! ", + "expected": "]( word<[a)a ) béb! !" + }, + { + "input": "_\n**a)word)~![ )éa ](https://y.io/p…word~`\u0000]( …!![#漢~!](\u0000!~ )", + "expected": "a)word)" + }, + { + "input": "é[)\u0000 b]< **a)\n`![]…# ]]!word[😀", + "expected": "![ b < a) ~#![<…a#< https://y.io/p _é**>é](\n**漢https://y.io/phttps://y.io/p[_< **\n", + "expected": "< ![< a < é é]( 漢" + }, + { + "input": "https://y.io/p <…![` ~<<_>aéhttps://y.io/p!word **漢|]( \n ~word漢😀>![]( _https://y.io/p ])](漢||😀http://x.io", + "expected": "< ](漢 😀" + } +] diff --git a/crates/buzz-cli/src/lib.rs b/crates/buzz-cli/src/lib.rs index 9c343a61fa..663bc5cea3 100644 --- a/crates/buzz-cli/src/lib.rs +++ b/crates/buzz-cli/src/lib.rs @@ -463,7 +463,7 @@ buzz agents archived" pub enum MessagesCmd { /// Send a message to a channel #[command( - after_help = "Examples:\n buzz messages send --channel --content \"hello\"\n buzz messages send --channel --content \"@alice check this\"\n echo \"hello from stdin\" | buzz messages send --channel --content -" + after_help = "Examples:\n buzz messages send --channel --content \"hello\"\n buzz messages send --channel --content \"@alice check this\"\n echo \"hello from stdin\" | buzz messages send --channel --content -\n buzz messages send --channel --content \"decision: ship it\" --from-thread " )] Send { /// Channel UUID (from 'buzz channels list') @@ -478,6 +478,9 @@ pub enum MessagesCmd { /// Event ID to reply to (creates a thread) #[arg(long)] reply_to: Option, + /// Thread root event ID: send this as a new top-level message tagged as sent from that thread (like desktop's "Send to channel") + #[arg(long, conflicts_with_all = ["reply_to", "broadcast"])] + from_thread: Option, /// Also publish to the Nostr network #[arg(long, default_value_t = false)] broadcast: bool, @@ -2433,6 +2436,29 @@ mod tests { Cli::command().debug_assert(); } + #[test] + fn messages_send_from_thread_conflicts_with_reply_and_broadcast() { + let base = [ + "buzz", + "messages", + "send", + "--channel", + "123e4567-e89b-12d3-a456-426614174000", + "--content", + "x", + "--from-thread", + ]; + let with = |extra: &[&'static str]| { + let mut args = base.to_vec(); + args.push("a"); + args.extend(extra); + Cli::try_parse_from(args) + }; + assert!(with(&[]).is_ok()); + assert!(with(&["--reply-to", "b"]).is_err()); + assert!(with(&["--broadcast"]).is_err()); + } + #[test] fn messages_thread_accepts_link_or_explicit_identifiers() { let channel = "123e4567-e89b-12d3-a456-426614174000";