feat(app): add error boundaries and fix ESLint config for Vue+TS

- Wrap ChatMessage v-for loop in ChatWindow with ErrorBoundary
- Wrap ContentPanel grid/detail sections with ErrorBoundary
- Fix ESLint flat config: add vue-eslint-parser for TypeScript in SFCs
- Add browser globals to ESLint config
- Fix lint errors in contentExtraction.ts (useless escapes, prefer-const,
  unicode flag for emoji regex)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-03 19:40:22 +00:00
co-authored by Claude Opus 4.6
parent 0863a66ddc
commit f31e0ba28b
7 changed files with 656 additions and 126 deletions
@@ -53,96 +53,98 @@
<!-- Content area -->
<div class="flex-1 min-h-0">
<!-- Detail views (override tab content) -->
<FilmDetail
v-if="selectedFilm"
:film="selectedFilm"
@back="closeFilmDetail"
/>
<BookDetail
v-else-if="selectedBook"
:book="selectedBook"
@back="closeBookDetail"
/>
<TVSeriesDetail
v-else-if="selectedTVSeries"
:series="selectedTVSeries"
@back="closeTVSeriesDetail"
/>
<SongDetail
v-else-if="selectedSong"
:song="selectedSong"
@back="closeSongDetail"
/>
<PodcastDetail
v-else-if="selectedPodcast"
:podcast="selectedPodcast"
@back="closePodcastDetail"
/>
<ArticleDetail
v-else-if="selectedArticle"
:article="selectedArticle"
@back="closeArticleDetail"
/>
<ErrorBoundary title="Content failed to load">
<!-- Detail views (override tab content) -->
<FilmDetail
v-if="selectedFilm"
:film="selectedFilm"
@back="closeFilmDetail"
/>
<BookDetail
v-else-if="selectedBook"
:book="selectedBook"
@back="closeBookDetail"
/>
<TVSeriesDetail
v-else-if="selectedTVSeries"
:series="selectedTVSeries"
@back="closeTVSeriesDetail"
/>
<SongDetail
v-else-if="selectedSong"
:song="selectedSong"
@back="closeSongDetail"
/>
<PodcastDetail
v-else-if="selectedPodcast"
:podcast="selectedPodcast"
@back="closePodcastDetail"
/>
<ArticleDetail
v-else-if="selectedArticle"
:article="selectedArticle"
@back="closeArticleDetail"
/>
<!-- Grid views by active tab -->
<FilmGrid
v-else-if="activeTab === 'film'"
:films="panelFilms"
:title="panelTitle"
@select-film="openFilmDetail"
/>
<BookGrid
v-else-if="activeTab === 'book'"
:books="panelBooks"
:title="panelTitle"
@select-book="openBookDetail"
/>
<TVSeriesGrid
v-else-if="activeTab === 'tvshow'"
:series="panelTVSeries"
:title="panelTitle"
@select-series="openTVSeriesDetail"
/>
<SongGrid
v-else-if="activeTab === 'song'"
:songs="panelSongs"
:title="panelTitle"
@select-song="openSongDetail"
/>
<PodcastGrid
v-else-if="activeTab === 'podcast'"
:podcasts="panelPodcasts"
:title="panelTitle"
@select-podcast="openPodcastDetail"
/>
<NewsGrid
v-else-if="activeTab === 'news'"
:articles="panelWebResults"
:title="panelTitle"
:query="panelQuery"
variant="news"
/>
<NewsGrid
v-else-if="activeTab === 'websites'"
:articles="panelWebsites"
:title="panelTitle"
:query="panelQuery"
variant="websites"
/>
<MagazineGrid
v-else-if="activeTab === 'magazine'"
:sections="panelMagazineSections"
:title="panelTitle"
:query="panelQuery"
:hero-image="panelMagazineHeroImage ?? undefined"
/>
<ProjectGrid
v-else-if="activeTab === 'code'"
/>
<NostrGrid
v-else-if="activeTab === 'nostr'"
/>
<!-- Grid views by active tab -->
<FilmGrid
v-else-if="activeTab === 'film'"
:films="panelFilms"
:title="panelTitle"
@select-film="openFilmDetail"
/>
<BookGrid
v-else-if="activeTab === 'book'"
:books="panelBooks"
:title="panelTitle"
@select-book="openBookDetail"
/>
<TVSeriesGrid
v-else-if="activeTab === 'tvshow'"
:series="panelTVSeries"
:title="panelTitle"
@select-series="openTVSeriesDetail"
/>
<SongGrid
v-else-if="activeTab === 'song'"
:songs="panelSongs"
:title="panelTitle"
@select-song="openSongDetail"
/>
<PodcastGrid
v-else-if="activeTab === 'podcast'"
:podcasts="panelPodcasts"
:title="panelTitle"
@select-podcast="openPodcastDetail"
/>
<NewsGrid
v-else-if="activeTab === 'news'"
:articles="panelWebResults"
:title="panelTitle"
:query="panelQuery"
variant="news"
/>
<NewsGrid
v-else-if="activeTab === 'websites'"
:articles="panelWebsites"
:title="panelTitle"
:query="panelQuery"
variant="websites"
/>
<MagazineGrid
v-else-if="activeTab === 'magazine'"
:sections="panelMagazineSections"
:title="panelTitle"
:query="panelQuery"
:hero-image="panelMagazineHeroImage ?? undefined"
/>
<ProjectGrid
v-else-if="activeTab === 'code'"
/>
<NostrGrid
v-else-if="activeTab === 'nostr'"
/>
</ErrorBoundary>
</div>
</aside>
</Transition>
@@ -167,6 +169,7 @@ import ArticleDetail from './ArticleDetail.vue'
import MagazineGrid from './MagazineGrid.vue'
import ProjectGrid from './ProjectGrid.vue'
import NostrGrid from './NostrGrid.vue'
import ErrorBoundary from '@/components/ui/ErrorBoundary.vue'
const { isDark } = useTheme()
const {