File size: 1,777 Bytes
060fd46
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/**

 * 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);
}