/** * Known acronyms kept fully uppercase after humanizing. * Comparison is case-insensitive against this set. */ const ACRONYMS = new Set([ 'AI', 'WA', 'WAC', 'LLM', ]); /** * Turn machine-style labels into Title Case display names. * Splits camelCase / PascalCase, dashes, and underscores into spaces. * Preserves known acronyms in ALL CAPS and normalizes Neon AI → Neon.ai. */ export function humanizeLabel(value) { if (value == null || value === '') return ''; const spaced = String(value) .replace(/([a-z0-9])([A-Z])/g, '$1 $2') .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2') .replace(/[_\-]+/g, ' ') .replace(/\s+/g, ' ') .trim(); const titled = spaced .split(' ') .filter(Boolean) .map((word) => { const upper = word.toUpperCase(); if (ACRONYMS.has(upper)) return upper; return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(); }) .join(' '); return normalizeNeonBrand(titled); } /** Any spelling of Neon + AI becomes Neon.ai */ function normalizeNeonBrand(text) { return String(text) .replace(/\bNeon[\s._-]*AI\b/gi, 'Neon.ai') .replace(/\bNeonai\b/gi, 'Neon.ai') .replace(/\bNeon\.Ai\b/g, 'Neon.ai') .replace(/\bNeon\.AI\b/g, 'Neon.ai'); } /** Short Neon model name from a path or model_id (e.g. BrainForge/Foo@1.0 → Foo). */ export function neonModelDisplayName(nameOrId) { if (!nameOrId) return ''; const withoutVersion = String(nameOrId).split('@')[0]; const short = withoutVersion.includes('/') ? withoutVersion.split('/').pop() : withoutVersion; return humanizeLabel(short); } export function neonPersonaDisplayName(personaName) { return humanizeLabel(personaName); }