feat: initial Lam Agent extension (#1)
This commit was merged in pull request #1.
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
/** Small pure helpers shared across the extension. Kept dependency-free so they are trivial to test. */
|
||||
|
||||
/**
|
||||
* Collapses whitespace and clips to `max` characters, adding an ellipsis when clipped.
|
||||
* Used for list rows and session titles, where a stray newline would break the layout.
|
||||
*/
|
||||
export function truncate(text: string, max: number): string {
|
||||
const collapsed = text.replace(/\s+/g, " ").trim();
|
||||
if (collapsed.length <= max) return collapsed;
|
||||
return `${collapsed.slice(0, Math.max(0, max - 1)).trimEnd()}…`;
|
||||
}
|
||||
|
||||
/** "just now", "12m ago", "3h ago", then an absolute date once it is older than a day. */
|
||||
export function formatRelativeTime(timestamp: number, now: number = Date.now()): string {
|
||||
const deltaSeconds = Math.round((now - timestamp) / 1000);
|
||||
|
||||
if (!Number.isFinite(deltaSeconds) || deltaSeconds < 0) return "just now";
|
||||
if (deltaSeconds < 45) return "just now";
|
||||
|
||||
const minutes = Math.round(deltaSeconds / 60);
|
||||
if (minutes < 60) return `${minutes}m ago`;
|
||||
|
||||
const hours = Math.round(minutes / 60);
|
||||
if (hours < 24) return `${hours}h ago`;
|
||||
|
||||
const days = Math.round(hours / 24);
|
||||
if (days < 7) return `${days}d ago`;
|
||||
|
||||
return new Date(timestamp).toLocaleDateString(undefined, { month: "short", day: "numeric" });
|
||||
}
|
||||
|
||||
/** Pulls a human-readable message out of anything that can be thrown. */
|
||||
export function errorMessage(error: unknown): string {
|
||||
if (error instanceof Error) return error.message;
|
||||
if (typeof error === "string") return error;
|
||||
try {
|
||||
return JSON.stringify(error);
|
||||
} catch {
|
||||
return String(error);
|
||||
}
|
||||
}
|
||||
|
||||
/** True when a rejected promise came from an AbortController rather than a real failure. */
|
||||
export function isAbortError(error: unknown): boolean {
|
||||
return error instanceof Error && (error.name === "AbortError" || error.message.includes("aborted"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the first line of a prompt into a session title.
|
||||
* Prompt text often starts with markdown decoration, which reads badly in a list row.
|
||||
*/
|
||||
export function titleFromPrompt(prompt: string, max: number): string {
|
||||
const firstLine = prompt.split("\n").find((line) => line.trim().length > 0) ?? prompt;
|
||||
const cleaned = firstLine.replace(/^[#>*\-\s]+/, "").trim();
|
||||
return truncate(cleaned || "New chat", max);
|
||||
}
|
||||
Reference in New Issue
Block a user