diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 0d491fe..b9e748c 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -54,7 +54,14 @@ "Bash(npx eslint:*)", "Bash(git checkout:*)", "mcp__plugin_playwright_playwright__browser_hover", - "mcp__plugin_playwright_playwright__browser_run_code" + "mcp__plugin_playwright_playwright__browser_run_code", + "WebFetch(domain:pagespeed.web.dev)", + "WebFetch(domain:www.googleapis.com)", + "Bash(find:*)", + "Bash(du -sh:*)", + "Bash(sudo apt-get install:*)", + "Bash(pdftotext:*)", + "Bash(pdftoppm:*)" ] } } diff --git a/index.html b/index.html index 27e2572..275b4fe 100644 --- a/index.html +++ b/index.html @@ -4,11 +4,13 @@ + CVMIS: CHARLWOOD, A. - - + + + diff --git a/lighthouse.pdf b/lighthouse.pdf new file mode 100644 index 0000000..5c31ea3 Binary files /dev/null and b/lighthouse.pdf differ diff --git a/src/App.tsx b/src/App.tsx index 8466316..b4a520b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -56,10 +56,9 @@ function App() { }) useEffect(() => { - initModel() - }, []) - - useEffect(() => { + if (phase === 'login' || phase === 'pmr') { + initModel() + } if (phase === 'pmr') { sessionStorage.setItem('portfolio-visited', String(Date.now())) } diff --git a/src/components/DashboardLayout.tsx b/src/components/DashboardLayout.tsx index b281b7f..c346f72 100644 --- a/src/components/DashboardLayout.tsx +++ b/src/components/DashboardLayout.tsx @@ -1,17 +1,18 @@ -import { useState, useEffect, useCallback, useRef, useMemo } from 'react' +import { useState, useEffect, useCallback, useRef, useMemo, lazy, Suspense } from 'react' import { motion } from 'framer-motion' import Sidebar from './Sidebar' import { MobileBottomNav } from './MobileBottomNav' -import { CommandPalette } from './CommandPalette' -import { DetailPanel } from './DetailPanel' import { PatientSummaryTile } from './tiles/PatientSummaryTile' import { ParentSection } from './ParentSection' -import CareerConstellation from './constellation/CareerConstellation' import { TimelineInterventionsSubsection } from './TimelineInterventionsSubsection' -import { RepeatMedicationsSubsection } from './RepeatMedicationsSubsection' import { LastConsultationCard } from './LastConsultationCard' -import { ChatWidget } from './ChatWidget' import { MobileOverviewHeader } from './MobileOverviewHeader' + +const CommandPalette = lazy(() => import('./CommandPalette').then(m => ({ default: m.CommandPalette }))) +const DetailPanel = lazy(() => import('./DetailPanel').then(m => ({ default: m.DetailPanel }))) +const CareerConstellation = lazy(() => import('./constellation/CareerConstellation')) +const RepeatMedicationsSubsection = lazy(() => import('./RepeatMedicationsSubsection').then(m => ({ default: m.RepeatMedicationsSubsection }))) +const ChatWidget = lazy(() => import('./ChatWidget').then(m => ({ default: m.ChatWidget }))) import { useActiveSection } from '@/hooks/useActiveSection' import { useIsMobileNav } from '@/hooks/useIsMobileNav' import { useDetailPanel } from '@/contexts/DetailPanelContext' @@ -329,22 +330,26 @@ export function DashboardLayout() {
- + + +
- + + +
@@ -352,17 +357,23 @@ export function DashboardLayout() { {/* Command palette overlay */} - + + + {/* Detail panel */} - + + + {/* Floating chat widget */} - + + + {/* Mobile bottom navigation */} { if (extractor || loading) return loading = true try { - extractor = await pipeline('feature-extraction', 'Xenova/all-MiniLM-L6-v2') as FeatureExtractionPipeline + const { env, pipeline } = await import('@xenova/transformers') + env.localModelPath = '/models/' + env.allowRemoteModels = false + env.useBrowserCache = false + extractor = await pipeline('feature-extraction', 'Xenova/all-MiniLM-L6-v2') as import('@xenova/transformers').FeatureExtractionPipeline } catch { // Silently swallow — model unavailable, semantic search won't activate } finally { diff --git a/vite.config.ts b/vite.config.ts index 4f387b4..d499de7 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -9,6 +9,20 @@ export default defineConfig({ '@': path.resolve(__dirname, './src'), }, }, + build: { + rollupOptions: { + output: { + manualChunks: { + 'vendor-react': ['react', 'react-dom'], + 'vendor-d3': ['d3'], + 'vendor-motion': ['framer-motion'], + 'vendor-search': ['fuse.js'], + 'vendor-markdown': ['react-markdown'], + 'vendor-carousel': ['embla-carousel-react', 'embla-carousel-autoplay'], + }, + }, + }, + }, server: { proxy: { '/api': 'http://localhost:3000',