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.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227const units: [Intl.RelativeTimeFormatUnit, number][] = [ ['year', 31536000], ['month', 2592000], ['week', 604800], ['day', 86400], ['hour', 3600], ['minute', 60],];
/** "4m", "2h", "3d": short enough for a list row. */export function ago(iso: string | null, now = Date.now()): string { if (!iso) return ''; const seconds = Math.max(0, (now - Date.parse(iso)) / 1000); if (seconds < 45) return 'now'; for (const [unit, size] of units) { if (seconds >= size) return `${Math.floor(seconds / size)}${unit === 'month' ? 'mo' : unit[0]}`; } return `${Math.round(seconds / 60)}m`;}
/** The workspace's last folder, with home shown as ~ and a remote host before it. */export function place(workspace: string): string { const remote = /^(?:[^/@]+@)?([^/:~][^/:]*):(\/.*)$/.exec(workspace); if (remote) return `${remote[1]}:${place(remote[2])}`; const trimmed = workspace.replace(/\/+$/, ''); if (/^\/(Users|home)\/[^/]+$/.test(trimmed)) return '~'; return trimmed.split('/').pop() || trimmed;}
/** The full path with the home folder shortened to ~. */export function homePath(workspace: string): string { return workspace.replace(/^\/(Users|home)\/[^/]+(?=\/|$)/, '~');}
export type MatchRank = [number, number, number];
export function compareRank(a: MatchRank, b: MatchRank): number { return b[0] - a[0] || b[1] - a[1] || b[2] - a[2];}
function lowerScalars(s: string): string[] { return Array.from(s, (ch) => Array.from(ch.toLowerCase())[0]);}
export function searchKey(s: string): string { return lowerScalars(s).join('').replace(/[\p{White_Space}\-_.:/]+/gu, '');}
/** Splits a query into lowercase separator-folded search words. */export function searchWords(query: string): string[] { return query.split(/\p{White_Space}+/u).map(searchKey).filter(Boolean);}
type Match = { rank: MatchRank; hits: number[] };type Token = { start: number; text: string[] };
function candidate(tier: number, bonus: number, score: number, hits: number[]): Match { return { rank: [tier, bonus, score], hits };}
function betterScore(a: Match | null, b: Match | null): Match | null { return b && (!a || b.rank[2] > a.rank[2]) ? b : a;}
function prefixLength(text: string[], query: string[], at = 0): number { let n = 0; while (n < text.length && at + n < query.length && text[n] === query[at + n]) n++; return n;}
function matchWord(word: string, rawField: string): Match | null { const query = Array.from(word); const field = lowerScalars(rawField); const tokens: Token[] = []; field.forEach((ch, i) => { if (!/[\p{L}\p{N}\p{M}]/u.test(ch) && !query.includes(ch)) return; const last = tokens[tokens.length - 1]; if (last && last.start + last.text.length === i) last.text.push(ch); else tokens.push({ start: i, text: [ch] }); }); if (!tokens.length) return null; const range = (start: number, length = query.length) => Array.from({ length }, (_, i) => start + i); const folded = tokens.flatMap((t) => t.text); const foldedHits = tokens.flatMap((t) => range(t.start, t.text.length)); const spaceWords = rawField.split(/\p{White_Space}+/u).filter(Boolean).length;
if (prefixLength(folded, query) === query.length) { return candidate(folded.length === query.length ? 5 : 4, 2, 100, foldedHits.slice(0, query.length)); } for (const [wi, { start, text }] of tokens.entries()) { if (prefixLength(text, query) === query.length) { return candidate(4, wi === 0 ? 2 : 1, (text.length === query.length ? 95 : 85) - wi, range(start)); } }
if (spaceWords <= 2 && query.length >= 2 && tokens.length >= 2) { const maxSkip = spaceWords === 1 && tokens.length <= 5 ? 1 : 0; const chain = (startTi: number, qi: number, ti: number, skips: number): Match | null => { if (qi === query.length) return candidate(3, 0, 78 - startTi * 2 - skips * 8, []); if (ti >= tokens.length) return null; const { start, text } = tokens[ti]; let best: Match | null = null; // Keep the first successful prefix length, then compare the skipped path. for (let n = prefixLength(text, query, qi); n >= 1; n--) { const rest = chain(startTi, qi + n, ti + 1, skips); if (rest) { best = candidate(3, 0, rest.rank[2] + (n - 1) * 3, range(start, n).concat(rest.hits)); break; } } return skips < maxSkip ? betterScore(best, chain(startTi, qi, ti + 1, skips + 1)) : best; }; let best: Match | null = null; for (let st = 0; st < tokens.length; st++) { if (tokens[st].text[0] === query[0]) best = betterScore(best, chain(st, 0, st, 0)); } if (best) { best.rank[1] = best.hits[0] === tokens[0].start ? 1 : 0; return best; } }
for (const { start, text } of tokens) { const token = text.join(''); const at = token.indexOf(word); if (at >= 0) { const hit = start + Array.from(token.slice(0, at)).length; return candidate(2, 0, 65 - Math.min(hit, 40), range(hit)); } }
let best: Match | null = null; if (query.length >= 2) { for (const [wi, { start, text }] of tokens.entries()) { if (text[0] !== query[0] || text.length < query.length) continue; const hits: number[] = []; let pos = 0; for (const ch of query) { const at = text.indexOf(ch, pos); if (at < 0) break; hits.push(start + at); pos = at + 1; } if (hits.length === query.length) { const gaps = pos - query.length; best = betterScore(best, candidate(1, 0, 55 - gaps * 3 - wi, hits)); } } } return best;}
/** * Ranks a row's fields against the search words (mirroring `cli/internal/tui/model_search.go`): * every word must match at least one field; hits are original UTF-16 offsets in fields[0]. */export function matchFields(words: string[], ...fields: string[]): Match | null { if (!words.length) return { rank: [0, 0, 0], hits: [] }; const whole = words.join(''); const exactWhole = whole && fields.some((f) => whole === searchKey(f)) ? 1 : 0; let tierSum = 0; let scoreSum = 0; const hitSet = new Set<number>(); for (const word of words) { let best: Match | null = null; for (const [i, field] of fields.entries()) { const m = matchWord(word, field); if (!m) continue; m.rank[2] -= i * 2; if (i !== 0) m.hits = []; if (!best || compareRank(m.rank, best.rank) < 0) best = m; } if (!best) return null; tierSum += best.rank[0] * 4 + best.rank[1]; scoreSum += best.rank[2]; for (const h of best.hits) hitSet.add(h); } const hits: number[] = []; let offset = 0; for (const [i, ch] of Array.from(fields[0] ?? '').entries()) { if (hitSet.has(i)) hits.push(offset); offset += ch.length; } return { rank: [exactWhole, tierSum, scoreSum], hits };}
/** Splits `text` into plain and highlighted segments from matched character indices. */export function splitSegments(text: string, hits: Set<number> | number[]): { text: string; hit: boolean }[] { const set = hits instanceof Set ? hits : new Set(hits); if (!set.size || !text.length) return [{ text, hit: false }]; const out: { text: string; hit: boolean }[] = []; let start = 0; let marked = set.has(0); let offset = 0; for (const ch of text) { const m = set.has(offset); if (m !== marked) { out.push({ text: text.slice(start, offset), hit: marked }); start = offset; marked = m; } offset += ch.length; } out.push({ text: text.slice(start), hit: marked }); return out;}
/** Whether `query` matches `fields` under the unified matcher. */export function matches(query: string, ...fields: string[]): boolean { return matchFields(searchWords(query), ...fields) !== null;}
/** The entries that fit the query, best first; ties keep their order. */export function rank<T>(entries: T[], query: string, fields: (entry: T) => string | string[]): T[] { const words = searchWords(query); if (!words.length) return [...entries]; return entries .map((entry) => { const f = fields(entry); const list = Array.isArray(f) ? f : [f]; return { entry, matched: matchFields(words, ...list) }; }) .filter((item): item is { entry: T; matched: Match } => item.matched !== null) .sort((a, b) => compareRank(a.matched.rank, b.matched.rank)) .map(({ entry }) => entry);}