export type KeepPromptKind = 'video' | 'image' export type KeepPrompt = { id: string label: string text: string } export type KeepPromptLists = { video: KeepPrompt[] image: KeepPrompt[] } export const KEEP_PROMPT_MAX = 40 export const KEEP_PROMPT_TEXT_MAX = 500 export const KEEP_PROMPT_LABEL_MAX = 40 export function emptyKeepPrompts(): KeepPromptLists { return { video: [], image: [] } } export function sanitizeKeepPromptText(raw: unknown) { return String(raw || '') .replace(/\r\n/g, '\n') .replace(/[\0\r]/g, '') .trim() .slice(0, KEEP_PROMPT_TEXT_MAX) } export function sanitizeKeepPromptLabel(raw: unknown, fallback = '') { const label = String(raw || '').replace(/\s+/g, ' ').trim().slice(0, KEEP_PROMPT_LABEL_MAX) if (label) return label const fromText = sanitizeKeepPromptText(fallback).replace(/\s+/g, ' ').slice(0, KEEP_PROMPT_LABEL_MAX) return fromText } function normalizeItem(raw: unknown): KeepPrompt | null { if (!raw || typeof raw !== 'object') return null const rec = raw as Record const text = sanitizeKeepPromptText(rec.text) if (!text) return null const id = String(rec.id || '').trim() || crypto.randomUUID() return { id, label: sanitizeKeepPromptLabel(rec.label, text), text } } export function normalizeKeepPromptList(raw: unknown): KeepPrompt[] { if (!Array.isArray(raw)) return [] const seen = new Set() const out: KeepPrompt[] = [] for (const entry of raw) { if (out.length >= KEEP_PROMPT_MAX) break const item = normalizeItem(entry) if (!item || seen.has(item.id)) continue seen.add(item.id) out.push(item) } return out } export function normalizeKeepPrompts(raw: unknown): KeepPromptLists { if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return emptyKeepPrompts() const rec = raw as Record return { video: normalizeKeepPromptList(rec.video), image: normalizeKeepPromptList(rec.image) } } export function parseKeepPromptKind(raw: unknown): KeepPromptKind | '' { return raw === 'video' || raw === 'image' ? raw : '' } export function insertKeepSnippet(text: string, snippet: string) { const src = String(text || '') const token = sanitizeKeepPromptText(snippet) if (!token) return src if (src.includes(token)) return src if (!src.trim()) return token const needsBreak = !/\n$/.test(src) return needsBreak ? `${src.trimEnd()}\n${token}` : `${src}${token}` }