@@ -1411,97 +1411,137 @@ function App() {
14111411 } ) ( ) }
14121412
14131413 { /* Dual View Layout */ }
1414- < div className = "relative flex-1 flex w-full h-full overflow-hidden" >
1414+ < div className = "relative flex-1 flex w-full h-full overflow-hidden bg-black " >
14151415
14161416 { /* Left / Single View */ }
1417- < div className = { `relative h-full transition-all duration-300 ${ isComparisonMode ? 'w-1/2 border-r border-white/10' : 'w-full' } ` } >
1418- < ProteinViewer
1419- ref = { leftRef }
1420- // Spread Left Controller State
1421- pdbId = { left . pdbId }
1422- dataSource = { left . dataSource }
1423- file = { left . file || undefined }
1424- fileType = { left . fileType }
1425- isLightMode = { isLightMode }
1426- isSpinning = { left . isSpinning }
1427- representation = { left . representation }
1428- showSurface = { left . showSurface }
1429- showLigands = { left . showLigands }
1430- showIons = { left . showIons }
1431- coloring = { left . coloring }
1432- palette = { colorPalette } // Global
1433- backgroundColor = { left . customBackgroundColor || ( isLightMode ? 'white' : 'black' ) }
1434- measurementTextColor = { measurementTextColorMode } // Global?
1435- enableAmbientOcclusion = { true }
1436-
1437- onStructureLoaded = { ( info ) => handleLoad ( info , left ) }
1438- onAtomClick = { ( info ) => handleAtomClick ( info , left , leftRef ) }
1439- isMeasurementMode = { isMeasurementMode } // Global
1440- measurements = { left . measurements }
1441- onAddMeasurement = { ( m ) => {
1442- left . setMeasurements ( [ ...left . measurements , m ] ) ;
1443- setActiveView ( 'left' ) ;
1444- } }
1445- onHover = { setHoveredResidue } // Global
1446-
1447- quality = { isPublicationMode ? 'high' : 'medium' } // Global
1448- resetCamera = { left . resetKey }
1449- customColors = { left . customColors }
1450- className = "w-full h-full"
1451- />
1452-
1453- { /* Active Indicator for Dual Mode */ }
1417+ < div className = { `flex flex-col h-full transition-all duration-300 ${ isComparisonMode ? 'w-1/2 border-r border-[#333]' : 'w-full' } ` } >
1418+ { /* Viewport Header (Dual Mode Only) */ }
14541419 { isComparisonMode && (
14551420 < div
14561421 onClick = { ( ) => setActiveView ( 'left' ) }
1457- className = { `absolute top-4 left-4 z-10 px-3 py-1 rounded-full text-xs font-bold cursor-pointer transition-colors backdrop-blur-md border ${ activeView === 'left' ? 'bg-indigo-600/90 text-white border-indigo-400 shadow-[0_0_15px_rgba(79,70,229,0.5)]' : 'bg-black/40 text-white/50 border-white/10 hover:bg-black/60 hover:text-white' } ` }
1422+ className = { `shrink-0 h-9 flex items-center justify-between px-3 border-b transition-colors cursor-pointer select-none
1423+ ${ activeView === 'left' ? 'bg-[#1a1a1a] border-indigo-500/50' : 'bg-black border-[#222] opacity-60 hover:opacity-100' }
1424+ ` }
14581425 >
1459- Left View
1426+ < div className = "flex items-center gap-2" >
1427+ < div className = { `w-2 h-2 rounded-full shadow-sm transition-all ${ activeView === 'left' ? 'bg-indigo-500 shadow-indigo-500/50 scale-110' : 'bg-neutral-700' } ` } />
1428+ < span className = { `text-[10px] font-bold uppercase tracking-widest ${ activeView === 'left' ? 'text-indigo-400' : 'text-neutral-500' } ` } > Left View</ span >
1429+ </ div >
1430+ < div className = "flex items-center gap-3" >
1431+ < span className = "text-[10px] text-neutral-400 font-mono max-w-[120px] truncate" >
1432+ { left . proteinTitle || left . pdbId || ( left . file ? left . file . name : "No Structure" ) }
1433+ </ span >
1434+ { /* Quick Action: Reset */ }
1435+ < button
1436+ onClick = { ( e ) => { e . stopPropagation ( ) ; left . handleResetView ( ) ; } }
1437+ className = "p-1 hover:bg-white/10 rounded text-neutral-500 hover:text-white transition-colors"
1438+ title = "Reset Camera"
1439+ >
1440+ < RefreshCw className = "w-3 h-3" />
1441+ </ button >
1442+ </ div >
14601443 </ div >
14611444 ) }
1462- </ div >
14631445
1464- { /* Right View */ }
1465- { isComparisonMode && (
1466- < div className = "relative w-1/2 h-full" >
1446+ < div className = "relative flex-1 w-full h-full" >
14671447 < ProteinViewer
1468- ref = { rightRef }
1469- pdbId = { right . pdbId }
1470- dataSource = { right . dataSource }
1471- file = { right . file || undefined }
1472- fileType = { right . fileType }
1448+ ref = { leftRef }
1449+ pdbId = { left . pdbId }
1450+ dataSource = { left . dataSource }
1451+ file = { left . file || undefined }
1452+ fileType = { left . fileType }
14731453 isLightMode = { isLightMode }
1474- isSpinning = { right . isSpinning }
1475- representation = { right . representation }
1476- showSurface = { right . showSurface }
1477- showLigands = { right . showLigands }
1478- showIons = { right . showIons }
1479- coloring = { right . coloring }
1454+ isSpinning = { left . isSpinning }
1455+ representation = { left . representation }
1456+ showSurface = { left . showSurface }
1457+ showLigands = { left . showLigands }
1458+ showIons = { left . showIons }
1459+ coloring = { left . coloring }
14801460 palette = { colorPalette }
1481- backgroundColor = { right . customBackgroundColor || ( isLightMode ? 'white' : 'black' ) }
1461+ backgroundColor = { left . customBackgroundColor || ( isLightMode ? 'white' : 'black' ) }
14821462 measurementTextColor = { measurementTextColorMode }
14831463 enableAmbientOcclusion = { true }
14841464
1485- onStructureLoaded = { ( info ) => handleLoad ( info , right ) }
1486- onAtomClick = { ( info ) => handleAtomClick ( info , right , rightRef ) }
1465+ onStructureLoaded = { ( info ) => handleLoad ( info , left ) }
1466+ onAtomClick = { ( info ) => handleAtomClick ( info , left , leftRef ) }
14871467 isMeasurementMode = { isMeasurementMode }
1488- measurements = { right . measurements }
1468+ measurements = { left . measurements }
14891469 onAddMeasurement = { ( m ) => {
1490- right . setMeasurements ( [ ...right . measurements , m ] ) ;
1491- setActiveView ( 'right ' ) ;
1470+ left . setMeasurements ( [ ...left . measurements , m ] ) ;
1471+ setActiveView ( 'left ' ) ;
14921472 } }
14931473 onHover = { setHoveredResidue }
14941474
14951475 quality = { isPublicationMode ? 'high' : 'medium' }
1496- resetCamera = { right . resetKey }
1497- customColors = { right . customColors }
1476+ resetCamera = { left . resetKey }
1477+ customColors = { left . customColors }
14981478 className = "w-full h-full"
14991479 />
1480+ </ div >
1481+ </ div >
1482+
1483+ { /* Right View */ }
1484+ { isComparisonMode && (
1485+ < div className = "flex flex-col w-1/2 h-full bg-black" >
1486+ { /* Viewport Header */ }
15001487 < div
15011488 onClick = { ( ) => setActiveView ( 'right' ) }
1502- className = { `absolute top-4 left-4 z-10 px-3 py-1 rounded-full text-xs font-bold cursor-pointer transition-colors backdrop-blur-md border ${ activeView === 'right' ? 'bg-indigo-600/90 text-white border-indigo-400 shadow-[0_0_15px_rgba(79,70,229,0.5)]' : 'bg-black/40 text-white/50 border-white/10 hover:bg-black/60 hover:text-white' } ` }
1489+ className = { `shrink-0 h-9 flex items-center justify-between px-3 border-b transition-colors cursor-pointer select-none
1490+ ${ activeView === 'right' ? 'bg-[#1a1a1a] border-indigo-500/50' : 'bg-black border-[#222] opacity-60 hover:opacity-100' }
1491+ ` }
15031492 >
1504- Right View
1493+ < div className = "flex items-center gap-2" >
1494+ < div className = { `w-2 h-2 rounded-full shadow-sm transition-all ${ activeView === 'right' ? 'bg-indigo-500 shadow-indigo-500/50 scale-110' : 'bg-neutral-700' } ` } />
1495+ < span className = { `text-[10px] font-bold uppercase tracking-widest ${ activeView === 'right' ? 'text-indigo-400' : 'text-neutral-500' } ` } > Right View</ span >
1496+ </ div >
1497+ < div className = "flex items-center gap-3" >
1498+ < span className = "text-[10px] text-neutral-400 font-mono max-w-[120px] truncate" >
1499+ { right . proteinTitle || right . pdbId || ( right . file ? right . file . name : "No Structure" ) }
1500+ </ span >
1501+ < button
1502+ onClick = { ( e ) => { e . stopPropagation ( ) ; right . handleResetView ( ) ; } }
1503+ className = "p-1 hover:bg-white/10 rounded text-neutral-500 hover:text-white transition-colors"
1504+ title = "Reset Camera"
1505+ >
1506+ < RefreshCw className = "w-3 h-3" />
1507+ </ button >
1508+ </ div >
1509+ </ div >
1510+
1511+ < div className = "relative flex-1 w-full h-full" >
1512+ < ProteinViewer
1513+ ref = { rightRef }
1514+ pdbId = { right . pdbId }
1515+ dataSource = { right . dataSource }
1516+ file = { right . file || undefined }
1517+ fileType = { right . fileType }
1518+ isLightMode = { isLightMode }
1519+ isSpinning = { right . isSpinning }
1520+ representation = { right . representation }
1521+ showSurface = { right . showSurface }
1522+ showLigands = { right . showLigands }
1523+ showIons = { right . showIons }
1524+ coloring = { right . coloring }
1525+ palette = { colorPalette }
1526+ backgroundColor = { right . customBackgroundColor || ( isLightMode ? 'white' : 'black' ) }
1527+ measurementTextColor = { measurementTextColorMode }
1528+ enableAmbientOcclusion = { true }
1529+
1530+ onStructureLoaded = { ( info ) => handleLoad ( info , right ) }
1531+ onAtomClick = { ( info ) => handleAtomClick ( info , right , rightRef ) }
1532+ isMeasurementMode = { isMeasurementMode }
1533+ measurements = { right . measurements }
1534+ onAddMeasurement = { ( m ) => {
1535+ right . setMeasurements ( [ ...right . measurements , m ] ) ;
1536+ setActiveView ( 'right' ) ;
1537+ } }
1538+ onHover = { setHoveredResidue }
1539+
1540+ quality = { isPublicationMode ? 'high' : 'medium' }
1541+ resetCamera = { right . resetKey }
1542+ customColors = { right . customColors }
1543+ className = "w-full h-full"
1544+ />
15051545 </ div >
15061546 </ div >
15071547 ) }
0 commit comments