67 lines
1.9 KiB
TypeScript
67 lines
1.9 KiB
TypeScript
function isCjkCodePoint(codePoint: number): boolean {
|
|
return (
|
|
(codePoint >= 0x3400 && codePoint <= 0x4dbf)
|
|
|| (codePoint >= 0x4e00 && codePoint <= 0x9fff)
|
|
|| (codePoint >= 0xf900 && codePoint <= 0xfaff)
|
|
|| (codePoint >= 0x3040 && codePoint <= 0x30ff)
|
|
|| (codePoint >= 0xac00 && codePoint <= 0xd7af)
|
|
)
|
|
}
|
|
|
|
function isCjkChar(ch: string): boolean {
|
|
const codePoint = ch.codePointAt(0)
|
|
return typeof codePoint === 'number' && isCjkCodePoint(codePoint)
|
|
}
|
|
|
|
function isWordChar(ch: string): boolean {
|
|
return !isCjkChar(ch) && /^[\p{L}\p{N}_]$/u.test(ch)
|
|
}
|
|
|
|
export function compactSessionTitle(input: string, maxUnits = 10, fallback = 'New Chat'): string {
|
|
const text = String(input || '').replace(/\s+/g, ' ').trim()
|
|
if (!text || maxUnits <= 0) return fallback
|
|
|
|
let units = 0
|
|
let index = 0
|
|
let cutIndex = text.length
|
|
|
|
while (index < text.length) {
|
|
const codePoint = text.codePointAt(index)
|
|
if (typeof codePoint !== 'number') break
|
|
const ch = String.fromCodePoint(codePoint)
|
|
if (/\s/.test(ch)) {
|
|
index += ch.length
|
|
continue
|
|
}
|
|
if (isCjkChar(ch)) {
|
|
units += 1
|
|
index += ch.length
|
|
if (units === maxUnits) {
|
|
cutIndex = index
|
|
break
|
|
}
|
|
continue
|
|
}
|
|
if (isWordChar(ch)) {
|
|
while (index < text.length) {
|
|
const innerCodePoint = text.codePointAt(index)
|
|
if (typeof innerCodePoint !== 'number') break
|
|
const innerChar = String.fromCodePoint(innerCodePoint)
|
|
if (!isWordChar(innerChar)) break
|
|
index += innerChar.length
|
|
}
|
|
units += 1
|
|
if (units === maxUnits) {
|
|
cutIndex = index
|
|
break
|
|
}
|
|
continue
|
|
}
|
|
index += ch.length
|
|
}
|
|
|
|
const compact = text.slice(0, cutIndex).trim() || fallback
|
|
const hasMoreUnits = Array.from(text.slice(cutIndex)).some(ch => isCjkChar(ch) || isWordChar(ch))
|
|
return hasMoreUnits ? `${compact}...` : compact
|
|
}
|