Fixed initial load being slow

This commit is contained in:
2026-02-19 21:38:39 +00:00
parent edc1327987
commit e452b66a7f
8 changed files with 69 additions and 84 deletions
+3 -4
View File
@@ -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()))
}
+34 -23
View File
@@ -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() {
</div>
</div>
<div ref={constellationWrapperRef} className="pathway-graph-sticky">
<CareerConstellation
onRoleClick={handleRoleClick}
onSkillClick={handleSkillClick}
onNodeHover={handleNodeHover}
highlightedNodeId={highlightedNodeId}
containerHeight={chronologyHeight}
animationReady={constellationReady}
globalFocusActive={globalFocusId !== null}
/>
<Suspense fallback={null}>
<CareerConstellation
onRoleClick={handleRoleClick}
onSkillClick={handleSkillClick}
onNodeHover={handleNodeHover}
highlightedNodeId={highlightedNodeId}
containerHeight={chronologyHeight}
animationReady={constellationReady}
globalFocusActive={globalFocusId !== null}
/>
</Suspense>
</div>
</div>
<div data-tile-id="section-skills" style={{ marginTop: '22px' }}>
<RepeatMedicationsSubsection onNodeHighlight={handleNodeHighlight} focusRelatedIds={focusRelatedIds} />
<Suspense fallback={null}>
<RepeatMedicationsSubsection onNodeHighlight={handleNodeHighlight} focusRelatedIds={focusRelatedIds} />
</Suspense>
</div>
</ParentSection>
</div>
@@ -352,17 +357,23 @@ export function DashboardLayout() {
</div>
{/* Command palette overlay */}
<CommandPalette
isOpen={commandPaletteOpen}
onClose={handlePaletteClose}
onAction={handlePaletteAction}
/>
<Suspense fallback={null}>
<CommandPalette
isOpen={commandPaletteOpen}
onClose={handlePaletteClose}
onAction={handlePaletteAction}
/>
</Suspense>
{/* Detail panel */}
<DetailPanel />
<Suspense fallback={null}>
<DetailPanel />
</Suspense>
{/* Floating chat widget */}
<ChatWidget onAction={handlePaletteAction} />
<Suspense fallback={null}>
<ChatWidget onAction={handlePaletteAction} />
</Suspense>
{/* Mobile bottom navigation */}
<MobileBottomNav
-45
View File
@@ -3,15 +3,6 @@
@tailwind utilities;
/* Premium UI fonts — Elvaro Grotesque (primary) */
@font-face {
font-family: 'Elvaro Grotesque';
src: url('/Fonts/Elvaro Grotesque Sans Family/WOFF/TBJElvaro-Light.woff2') format('woff2'),
url('/Fonts/Elvaro Grotesque Sans Family/WOFF/TBJElvaro-Light.woff') format('woff');
font-weight: 300;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Elvaro Grotesque';
src: url('/Fonts/Elvaro Grotesque Sans Family/WOFF/TBJElvaro-Regular.woff2') format('woff2'),
@@ -48,34 +39,7 @@
font-display: swap;
}
@font-face {
font-family: 'Elvaro Grotesque';
src: url('/Fonts/Elvaro Grotesque Sans Family/WOFF/TBJElvaro-ExtraBold.woff2') format('woff2'),
url('/Fonts/Elvaro Grotesque Sans Family/WOFF/TBJElvaro-ExtraBold.woff') format('woff');
font-weight: 800;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Elvaro Grotesque';
src: url('/Fonts/Elvaro Grotesque Sans Family/WOFF/TBJElvaro-Black.woff2') format('woff2'),
url('/Fonts/Elvaro Grotesque Sans Family/WOFF/TBJElvaro-Black.woff') format('woff');
font-weight: 900;
font-style: normal;
font-display: swap;
}
/* Monospace — Interval Mono */
@font-face {
font-family: 'Interval Mono';
src: url('/Fonts/IntervalMono/WOFF/TBJInterval-Light.woff2') format('woff2'),
url('/Fonts/IntervalMono/WOFF/TBJInterval-Light.woff') format('woff');
font-weight: 300;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Interval Mono';
src: url('/Fonts/IntervalMono/WOFF/TBJInterval-Regular.woff2') format('woff2'),
@@ -85,15 +49,6 @@
font-display: swap;
}
@font-face {
font-family: 'Interval Mono';
src: url('/Fonts/IntervalMono/WOFF/TBJInterval-Bold.woff2') format('woff2'),
url('/Fonts/IntervalMono/WOFF/TBJInterval-Bold.woff') format('woff');
font-weight: 700;
font-style: normal;
font-display: swap;
}
/* Premium UI fonts — Blumir (alternative) */
@font-face {
font-family: 'Blumir';
+6 -9
View File
@@ -1,18 +1,15 @@
import { env, pipeline, type FeatureExtractionPipeline } from '@xenova/transformers'
// Serve model files from /models/ (Vite serves public/ at root)
env.localModelPath = '/models/'
env.allowRemoteModels = false
env.useBrowserCache = false
let extractor: FeatureExtractionPipeline | null = null
let extractor: import('@xenova/transformers').FeatureExtractionPipeline | null = null
let loading = false
export async function initModel(): Promise<void> {
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 {