Fixed initial load being slow
This commit is contained in:
+3
-4
@@ -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()))
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user