diff --git a/index.html b/index.html index 70576a9..fba5c8d 100644 --- a/index.html +++ b/index.html @@ -17,21 +17,21 @@ - - - - - - - + + + + + + + @@ -3287,7 +3368,12 @@ var _aj=useState(''),excludePatternDraft=_aj[0],setExcludePatternDraft=_aj[1]; var _ak=useState(false),launchFolderAfterExcludeSave=_ak[0],setLaunchFolderAfterExcludeSave=_ak[1]; var _al=useState(null),confirmDialog=_al[0],setConfirmDialog=_al[1]; + var _am=useState(window.innerWidth),viewportWidth=_am[0],setViewportWidth=_am[1]; + var _an=useState(null),mobilePanel=_an[0],setMobilePanel=_an[1]; + var _ao=useState(48),topbarHeight=_ao[0],setTopbarHeight=_ao[1]; + var isMobile=viewportWidth<=980; var svgRef=useRef(null); + var topbarRef=useRef(null); var filePreviewRef=useRef(null); var treemapRef=useRef(null); var matrixRef=useRef(null); @@ -3306,6 +3392,45 @@ useEffect(function(){document.body.className=theme==='light'?'light':'';},[theme]); + useEffect(function(){ + function onResize(){setViewportWidth(window.innerWidth);} + window.addEventListener('resize',onResize); + onResize(); + return function(){window.removeEventListener('resize',onResize);}; + },[]); + + useEffect(function(){ + if(!topbarRef.current)return; + function measureTopbar(){ + if(topbarRef.current){ + setTopbarHeight(topbarRef.current.offsetHeight||48); + } + } + measureTopbar(); + if(typeof ResizeObserver==='undefined'){ + window.addEventListener('resize',measureTopbar); + return function(){window.removeEventListener('resize',measureTopbar);}; + } + var observer=new ResizeObserver(measureTopbar); + observer.observe(topbarRef.current); + return function(){observer.disconnect();}; + },[]); + + useEffect(function(){ + if(!isMobile){ + setMobilePanel(null); + return; + } + setLegendCollapsed(true); + setShowGraphConfig(false); + },[isMobile]); + + useEffect(function(){ + if(!data){ + setMobilePanel(null); + } + },[data]); + useEffect(function(){ return function(){ if(confirmResolverRef.current){ @@ -3359,6 +3484,8 @@ setFolderFilter(null); setPrData(null); setFilePreview(null); + setMobilePanel(null); + setShowGraphConfig(false); } function openExcludeModal(continueToFolder){ @@ -3410,6 +3537,10 @@ }); } + function toggleMobilePanel(panel){ + setMobilePanel(function(prev){return prev===panel?null:panel;}); + } + function analyze(){ var p=parseUrl(repoUrl); if(!p){setError('Invalid URL. Use format: owner/repo');return;} @@ -4062,6 +4193,10 @@ if(file){ setSelected(file); setRightTab('details'); + if(isMobile){ + setMobilePanel('details'); + setLegendCollapsed(true); + } var blast=calcBlast(path,data.connections,data.files); setBlastRadius(blast); setOwnership(null); @@ -4075,7 +4210,7 @@ } updateGraphHighlight(path,blast); } - },[data,repoInfo,localDirHandle]); + },[data,repoInfo,localDirHandle,isMobile]); selectFileRef.current=selectFile; function updateGraphHighlight(path,blast){ @@ -5119,9 +5254,11 @@ function resetAnalysis(){setData(null);setSelected(null);setBlastRadius(null);setOwnership(null);setRepoInfo(null);setRepoUrl('');setPrData(null);setFolderFilter(null);setLocalDirHandle(null);window.history.replaceState({},'',window.location.pathname);} function filterByFolder(path){setFolderFilter(function(prev){return prev===path?null:path;});} var health=useMemo(function(){return calcHealth(data);},[data]); + var mobileSidebarWidth=Math.min(Math.max(viewportWidth-16,240),380); + var mobileRightPanelWidth=Math.min(Math.max(viewportWidth-16,240),460); - return React.createElement('div',{className:'app'}, - React.createElement('div',{className:'topbar'}, + return React.createElement('div',{className:'app',style:{'--topbar-height':topbarHeight+'px'}}, + React.createElement('div',{className:'topbar',ref:topbarRef}, React.createElement('div',{className:'logo',onClick:function(){setShowPrivacy(true);}}, React.createElement('div',{className:'logo-mark'},React.createElement(Icon,{name:'logo',size:'l'})), React.createElement('span',{className:'logo-text'},'CODEFLOW') @@ -5165,6 +5302,16 @@ ' Reset' ) ), + isMobile&&React.createElement('div',{className:'mobile-panel-actions'}, + React.createElement('button',{className:'top-btn'+(mobilePanel==='explorer'?' active':''),'aria-label':'Toggle explorer panel',onClick:function(){toggleMobilePanel('explorer');},type:'button'}, + React.createElement(Icon,{name:'folder',size:'m'}), + 'Explorer' + ), + React.createElement('button',{className:'top-btn'+(mobilePanel==='details'?' active':''),'aria-label':'Toggle details panel',onClick:function(){toggleMobilePanel('details');},disabled:!data,type:'button'}, + React.createElement(Icon,{name:selected?'file':'layout',size:'m'}), + selected?'Inspector':'Insights' + ) + ), React.createElement('div',{className:'topbar-actions'}, React.createElement('button',{className:'top-btn','aria-label':'Analyze Pull Request',onClick:function(){setShowPR(true);},disabled:!data||localDirHandle},React.createElement(Icon,{name:'pull-request',size:'m'}),'PR'), React.createElement('button',{className:'top-btn','aria-label':'Export analysis',onClick:function(){setShowExport(true);},disabled:!data},React.createElement(Icon,{name:'export',size:'m'}),'Export'), @@ -5173,7 +5320,17 @@ ) ), React.createElement('div',{className:'main'}, - React.createElement('div',{className:'sidebar',style:{width:sidebarWidth}}, + isMobile&&React.createElement('button',{type:'button',className:'mobile-panel-backdrop'+(mobilePanel?' visible':''),'aria-label':'Close mobile panel',onClick:function(){setMobilePanel(null);}}), + React.createElement('div',{className:'sidebar'+(isMobile&&mobilePanel==='explorer'?' mobile-visible':''),style:{width:isMobile?mobileSidebarWidth:sidebarWidth}}, + isMobile&&React.createElement('div',{className:'mobile-panel-header'}, + React.createElement('div',{className:'mobile-panel-meta'}, + React.createElement('div',{className:'mobile-panel-title'},'Explorer'), + React.createElement('div',{className:'mobile-panel-subtitle'},data?(folderFilter?'Filtered by '+folderFilter:data.files.length+' files ready to browse'):'Analyze a repo or open a folder') + ), + React.createElement('button',{className:'mobile-panel-close',type:'button','aria-label':'Close explorer panel',onClick:function(){setMobilePanel(null);}}, + React.createElement(Icon,{name:'close',size:'m'}) + ) + ), React.createElement('div',{className:'resize-handle',onMouseDown:function(e){ e.preventDefault(); var startX=e.clientX,startW=sidebarWidth; @@ -5332,7 +5489,16 @@ tooltip&&React.createElement('div',{className:'tooltip',style:{left:tooltip.x,top:tooltip.y}},React.createElement('div',{className:'tooltip-title'},tooltip.title),React.createElement('div',{className:'tooltip-content'},tooltip.content)) ) ), - React.createElement('div',{className:'right-panel',style:{width:rightPanelWidth}}, + React.createElement('div',{className:'right-panel'+(isMobile&&mobilePanel==='details'?' mobile-visible':''),style:{width:isMobile?mobileRightPanelWidth:rightPanelWidth}}, + isMobile&&React.createElement('div',{className:'mobile-panel-header'}, + React.createElement('div',{className:'mobile-panel-meta'}, + React.createElement('div',{className:'mobile-panel-title'},selected?selected.name:'Insights'), + React.createElement('div',{className:'mobile-panel-subtitle'},selected?selected.path:(data?'Browse issues, patterns, and security findings':'Select a file to inspect it')) + ), + React.createElement('button',{className:'mobile-panel-close',type:'button','aria-label':'Close details panel',onClick:function(){setMobilePanel(null);}}, + React.createElement(Icon,{name:'close',size:'m'}) + ) + ), React.createElement('div',{className:'resize-handle',onMouseDown:function(e){ e.preventDefault(); var startX=e.clientX,startW=rightPanelWidth;