/** * The one way Kanna turns a message into a one-line preview. * * Hover cards show messages at two or three clamped lines, and the raw text is * written for a renderer that isn't there: a prompt that opens with a heading * spends its first line on `##`, a reply full of `**bold**` reads as asterisk * soup, and a fenced block contributes a line of backticks. What the reader * wants from a preview is the sentence, so this returns the sentence. * * Two jobs, in this order — unwrap the markup, then flatten the lines. They are * one function because doing either alone gives a wrong answer: flattening * first glues list markers into the middle of the text where the line-start * rules can no longer see them. * * Deliberately *not* a markdown parser. It is a lossy pass over the constructs * that actually show up at the head of a message, tuned to never mangle prose: * where a rule would risk eating real text — `snake_case` names, a bare `*`, * an XML-ish tag an agent wrote on purpose — it leaves the text alone. Being a * little under-stripped is invisible; eating a word is not. */ /** Fence lines (``` or ~~~), dropped so their contents read as ordinary text. */ const FENCE_LINE = /^[ \t]*(?:`{3,}|~{3,}).*$/gm /** `code` → code. Handles doubled backticks around text containing one. */ const INLINE_CODE = /`+([^`]+)`+/g /** `![alt](src)` → alt, before links, so the leading `!` doesn't survive. */ const IMAGE = /!\[([^\]]*)\]\([^)]*\)/g /** `[text](href)` → text, and `[text][ref]` → text. */ const INLINE_LINK = /\[([^\]]*)\]\([^)]*\)/g const REFERENCE_LINK = /\[([^\]]*)\]\[[^\]]*\]/g /** `` → https://example.com. Only ever a bare URL. */ const AUTOLINK = /<((?:https?|mailto):[^>\s]+)>/g /** `## Heading` → Heading. */ const HEADING = /^[ \t]{0,3}#{1,6}[ \t]+/gm /** `> quoted` → quoted, including nested `>>`. */ const BLOCKQUOTE = /^[ \t]{0,3}(?:>[ \t]?)+/gm /** `---`, `***`, `___` on their own line — structure with no text in it. */ const THEMATIC_BREAK = /^[ \t]{0,3}([-*_])(?:[ \t]*\1){2,}[ \t]*$/gm /** `- `, `* `, `1. `, `2) ` at the head of a line. */ const LIST_MARKER = /^[ \t]*(?:[-*+]|\d{1,9}[.)])[ \t]+/gm /** The `[ ]` / `[x]` a task list leaves behind once its marker is gone. */ const TASK_BOX = /^\[[ xX]\][ \t]*/gm /** `~~struck~~` → struck. */ const STRIKETHROUGH = /~~([^~]+)~~/g /** * `**bold**` / `*italic*` → the text inside. * * Requires a non-space just inside each delimiter so a lone `*` used as a * bullet or a multiplication sign is left alone. */ const ASTERISK_EMPHASIS = /(\*{1,3})(?=\S)([\s\S]*?\S)\1/g /** * `_italic_` → italic. Single underscores only, at a word boundary, and never * wrapping text that itself starts or ends with one. * * This is the rule that has to be careful, and the one place the pass * deliberately does less than a renderer would. `retry_count_ms` is handled by * the word boundary — CommonMark agrees that intraword underscores don't * emphasise. `__init__` is not: it is character-for-character a bold span, and * no rule can tell a dunder from `__bold__`. So doubles are left alone * entirely. In a tool whose messages are full of code, rendering `__really__` * with its underscores showing is a cosmetic blemish; turning `__init__` into * `init` quietly changes what the message says. `**bold**` is the form that * actually shows up anyway, and it's handled above. */ const UNDERSCORE_EMPHASIS = /(?