import type { Message } from "@earendil-works/pi-ai"; export const clip = (text: string, max = 200): string => { if (text.length <= max) { return text; } // Try to cut at a word boundary const cut = text.lastIndexOf(" ", max); let end = cut > max * 0.6 ? cut : max; // Avoid splitting a surrogate pair if (end > 0 && end < text.length) { const code = text.charCodeAt(end - 1); if (code >= 0xd800 && code <= 0xdbff) { end--; } } return text.slice(0, end); }; /** * Clip text to last sentence boundary at or before `max` chars. * Falls back to word boundary (clip()) if no sentence end is found in the * acceptable range. Trailing whitespace stripped. */ export const clipSentence = (text: string, max = 200): string => { if (text.length <= max) { return text; } // Look for sentence terminators followed by space/newline within [max*0.5, max] const window = text.slice(0, max); const matches = [...window.matchAll(/[.!?](?:\s|$)/g)]; const last = matches[matches.length - 1]; if (last) { const end = (last.index ?? 0) + 1; // include the punctuation if (end >= max * 0.5) { return text.slice(0, end); } } return clip(text, max); }; export const nonEmptyLines = (text: string): string[] => text .split("\n") .map((line) => line.trim()) .filter(Boolean); export const firstLine = (text: string, max = 200): string => clip(text.split("\n")[0] ?? "", max); export const textParts = (content: Message["content"]): string[] => { if (!content) { return []; } if (typeof content === "string") { return [content]; } return content .filter((part) => part.type === "text") .map((part) => part.text); }; export const textOf = (content: Message["content"]): string => textParts(content).join("\n"); /** Extract a snippet of ~`radius` chars around the first match of `term` in `text`. */ export const snippet = ( text: string, term: string, radius = 60, ): string | null => { const idx = text.toLowerCase().indexOf(term.toLowerCase()); if (idx === -1) { return null; } const start = Math.max(0, idx - radius); const end = Math.min(text.length, idx + term.length + radius); const prefix = start > 0 ? "..." : ""; const suffix = end < text.length ? "..." : ""; return `${prefix}${text.slice(start, end)}${suffix}`; };