Something went wrong. Try again.
small gleam coding and persistent agent daemon with a detachable cli and a canvas ui forked from okami.mom/albedo
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859// Passages of text a note is tied to, kept as a W3C TextQuoteSelector keeps// them: the exact text and a little of what comes before and after, so the// passage is found again when the chat or the page draws it anew, and the// right one of several alike.
/** How much text either side of a passage is kept to tell it from others like it. */export const CONTEXT = 32;/** The longest passage a note keeps. */export const LONGEST = 2000;
export type Quote = { quote: string; prefix: string; suffix: string };
/** The passage between two offsets of a text, with its context. */export function quoteAt(text: string, start: number, end: number): Quote { const stop = Math.min(end, start + LONGEST); return { quote: text.slice(start, stop), prefix: text.slice(Math.max(0, start - CONTEXT), start), suffix: text.slice(stop, stop + CONTEXT) };}
function sharedEnd(a: string, b: string): number { let n = 0; while (n < a.length && n < b.length && a[a.length - 1 - n] === b[b.length - 1 - n]) n++; return n;}
function sharedStart(a: string, b: string): number { let n = 0; while (n < a.length && n < b.length && a[n] === b[n]) n++; return n;}
/** * Where a passage is in a text: of every place its words appear, the one * whose surroundings match the kept context best (the first on a tie), or * null when they appear nowhere. */export function locate(text: string, { quote, prefix, suffix }: Quote): { start: number; end: number } | null { if (!quote) return null; let best = -1; let score = -1; for (let at = text.indexOf(quote), seen = 0; at >= 0 && seen < 1000; at = text.indexOf(quote, at + 1), seen++) { const around = sharedEnd(text.slice(Math.max(0, at - prefix.length), at), prefix) + sharedStart(text.slice(at + quote.length, at + quote.length + suffix.length), suffix); if (around > score) [best, score] = [at, around]; } return best < 0 ? null : { start: best, end: best + quote.length };}
/** * A passage as it reads, for a message or a label: blank lines and the * space around it dropped, each line's indent kept, so code stays code. */export function tidy(quote: string): string { return quote .split('\n') .map((line) => line.trimEnd()) .filter(Boolean) .join('\n') .trimStart();}