feat(chat): enhance film integration and UI improvements

- Updated ChatMessage component to display inline film cards and added functionality for selecting films.
- Improved ChatWindow to handle film-related content and update the panel with selected films.
- Refactored useAI to streamline film recommendation prompts and context.
- Enhanced ChatPage layout for better film library access and user experience.
- Updated service worker revision for PWA improvements.

Made-with: Cursor
This commit is contained in:
Dorian
2026-03-02 16:48:17 +00:00
parent ece4b8256f
commit 464069b2f2
13 changed files with 658 additions and 112 deletions
@@ -0,0 +1,81 @@
import { ref, computed } from 'vue'
import type { Film } from '@aiui/core/types/content'
import { mockFilms } from '@/mocks/films'
const panelOpen = ref(false)
const panelFilms = ref<Film[]>([])
const selectedFilm = ref<Film | null>(null)
const panelTitle = ref('Recommended Films')
const FILM_TAG_RE = /\[\[film:(f\d+)\]\]/g
export function useContentPanel() {
function extractFilmIds(text: string): string[] {
const ids: string[] = []
let match: RegExpExecArray | null
const re = new RegExp(FILM_TAG_RE.source, 'g')
while ((match = re.exec(text)) !== null) {
if (!ids.includes(match[1])) ids.push(match[1])
}
return ids
}
function resolveFilms(ids: string[]): Film[] {
return ids
.map((id) => mockFilms.find((f) => f.id === id))
.filter((f): f is Film => !!f)
}
function updatePanelFromText(text: string) {
const ids = extractFilmIds(text)
if (ids.length > 0) {
const films = resolveFilms(ids)
if (films.length > 0) {
panelFilms.value = films
panelOpen.value = true
panelTitle.value = films.length === 1
? films[0].title
: `${films.length} Recommended Films`
}
}
}
function stripFilmTags(text: string): string {
return text.replace(FILM_TAG_RE, '').replace(/\n{3,}/g, '\n\n').trim()
}
function openFilmDetail(film: Film) {
selectedFilm.value = film
}
function closeFilmDetail() {
selectedFilm.value = null
}
function closePanel() {
panelOpen.value = false
selectedFilm.value = null
}
function showAllFilms() {
panelFilms.value = [...mockFilms]
panelTitle.value = 'Your Film Library'
panelOpen.value = true
selectedFilm.value = null
}
return {
panelOpen,
panelFilms,
selectedFilm,
panelTitle,
extractFilmIds,
resolveFilms,
updatePanelFromText,
stripFilmTags,
openFilmDetail,
closeFilmDetail,
closePanel,
showAllFilms,
}
}