OwnGPT.v2 / client /src /components /Sidebar /SessionItem.jsx
parthib07's picture
Upload 199 files
212c959 verified
import { memo } from 'react'
import { Trash2 } from 'lucide-react'
const GENERIC_TITLES = new Set(['new', 'chat', 'conversation', 'session', 'untitled'])
const FILLER_WORDS = new Set([
'a',
'an',
'and',
'are',
'for',
'from',
'have',
'how',
'i',
'in',
'is',
'me',
'my',
'of',
'on',
'or',
'please',
'the',
'to',
'we',
'with',
'you',
'your',
])
function tokenize(text) {
return String(text || '')
.replace(/(You|Assistant):/gi, ' ')
.replace(/[^a-z0-9\s]/gi, ' ')
.split(/\s+/)
.filter(Boolean)
}
function toTitleCase(word) {
return word.charAt(0).toUpperCase() + word.slice(1)
}
function buildOverview(session) {
const sources = [session.title, session.last_message_preview]
const originalWords = tokenize(sources.join(' '))
const preferredWords = originalWords.filter((word) => {
const lower = word.toLowerCase()
return !GENERIC_TITLES.has(lower) && !FILLER_WORDS.has(lower)
})
const chosenWords = (preferredWords.length ? preferredWords : originalWords).slice(0, 3)
if (!chosenWords.length) return 'New Conversation'
return chosenWords.map((word) => toTitleCase(word.toLowerCase())).join(' ')
}
function SessionItem({ session, active, onSelect, onDelete }) {
const overview = buildOverview(session)
return (
<div
className={`group w-full rounded-lg px-3 py-2.5 text-left text-sm transition ${
active
? 'bg-secondary text-foreground'
: 'bg-transparent text-muted-foreground hover:bg-secondary hover:text-foreground'
}`}
>
<div className="flex items-center gap-2">
<button
type="button"
onClick={() => onSelect(session)}
title={session.title || overview}
className="min-w-0 flex-1 text-left"
aria-pressed={active}
>
<span className="block truncate">{overview}</span>
</button>
<button
type="button"
onClick={() => onDelete?.(session)}
className="inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-lg text-muted-foreground opacity-100 transition hover:bg-danger/10 hover:text-danger sm:opacity-0 sm:group-hover:opacity-100 sm:focus-visible:opacity-100"
aria-label={`Delete ${overview}`}
title="Delete chat"
>
<Trash2 className="h-3.5 w-3.5" />
</button>
</div>
</div>
)
}
export default memo(SessionItem)