Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
216 changes: 206 additions & 10 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -514,9 +514,17 @@
.code-card-resize-se{right:-3px;bottom:-3px;width:14px;height:14px;cursor:nwse-resize}
.code-card.code-far .code-card-body{overflow:hidden}
.code-card.code-far .file-preview-linenum{display:none}
.code-card.code-far .file-preview-line{min-height:3px;height:3px;overflow:hidden}
.code-card.code-far .file-preview-text{font-size:3px;line-height:3px;letter-spacing:-0.5px}
.code-card.code-far .file-preview-text{visibility:hidden}
.code-card.code-far .code-line-pill{display:none}
.code-card-source{position:relative;min-height:100%}
.code-color-blocks{display:none;position:absolute;inset:0;pointer-events:none;z-index:1}
.code-card.code-blocks .code-color-blocks{display:block}
.code-color-block{position:absolute;left:8px;right:8px;border-radius:3px;min-height:4px;pointer-events:auto;cursor:default}
.code-card.code-blocks:not(.code-far) .code-color-block{opacity:0.38}
.code-card.code-far .code-color-block{opacity:0.88}
.code-color-block:hover{outline:1px solid var(--acc);opacity:1}
svg.color-blocks circle.nc{display:none}
svg.color-blocks text.node-label{display:none}
.has-code-card{display:none!important}
.has-code-card .nc,.has-code-card .node-label{pointer-events:none}
.sym-mark{border-radius:3px;padding:0 1px;cursor:pointer}
Expand Down Expand Up @@ -5847,6 +5855,90 @@
return Math.min(8,1/zoom);
}

var COLOR_BLOCK_ZOOM=0.4;
var CODE_FAR_ZOOM=0.22;

function zoomShowsColorBlocks(k){
var zoom=Number(k);
if(!isFinite(zoom)||zoom<=0)return false;
return zoom<=COLOR_BLOCK_ZOOM;
}

function zoomHidesCodeText(k){
var zoom=Number(k);
if(!isFinite(zoom)||zoom<=0)return false;
return zoom<CODE_FAR_ZOOM;
}

function graphColorBlockSize(d){
var r=Math.max(8,Math.min(24,5+((d&&d.fnCount)||0)*0.8));
return Math.max(22,r*2+4);
}

function graphColorBlockScale(k){
var zoom=Number(k);
if(!isFinite(zoom)||zoom<=0)return 1;
var minScreen=16;
var screen=22*zoom;
if(screen>=minScreen)return 1;
return Math.min(6,minScreen/screen);
}

function parseCssHex(color){
var raw=String(color||'').trim();
var m=raw.match(/^#([0-9a-f]{3}|[0-9a-f]{6})$/i);
if(!m)return null;
var h=m[1];
if(h.length===3)h=h[0]+h[0]+h[1]+h[1]+h[2]+h[2];
return{r:parseInt(h.slice(0,2),16),g:parseInt(h.slice(2,4),16),b:parseInt(h.slice(4,6),16)};
}

function cssHexHue(color){
var rgb=parseCssHex(color);
if(!rgb)return NaN;
var r=rgb.r/255,g=rgb.g/255,b=rgb.b/255;
var max=Math.max(r,g,b),min=Math.min(r,g,b),d=max-min;
if(d===0)return 0;
var h=max===r?((g-b)/d)%6:max===g?(b-r)/d+2:(r-g)/d+4;
h=h*60;
if(h<0)h+=360;
return h;
}

function colorBlockLooksLikeDiff(color){
var h=cssHexHue(color);
if(!isFinite(h))return false;
return h<=25||h>=335||(h>=70&&h<=165);
}

function graphColorBlockFill(color){
var hex=String(color||'').trim();
var known={
'#00ff9d':'#22d3ee',
'#00cc7d':'#22d3ee',
'#00a86b':'#4d9fff',
'#22c55e':'#4d9fff',
'#84cc16':'#ff9f43',
'#98c379':'#56b6c2',
'#ff5f5f':'#a78bfa',
'#e06c75':'#d19a66',
'#ff6b6b':'#a78bfa'
};
var key=hex.toLowerCase();
if(known[key])return known[key];
if(!colorBlockLooksLikeDiff(hex))return hex||'#4d9fff';
var h=cssHexHue(hex);
return (h<=25||h>=335)?'#a78bfa':'#22d3ee';
}

function codeColorBlockKindColor(kind){
if(kind==='import')return '#c678dd';
if(kind==='export')return '#56b6c2';
if(kind==='var'||kind==='file')return '#d19a66';
if(kind==='class')return '#e5c07b';
return '#61afef';
}

function nodeReplacedByCard(path,cardPaths){
return !!(cardPaths&&cardPaths.has(path));
}
Expand Down Expand Up @@ -5993,6 +6085,10 @@
return vizType==='graph'||vizType==='code'||vizType==='graph3d';
}

function vizHasZoomColorBlocks(vizType){
return vizType==='graph'||vizType==='code';
}

function collectVisibleCodeFiles(selectedPath,data,folderFilter,limit){
if(!data||!data.files)return[];
var filtered=folderFilter?data.files.filter(function(f){return f.folder===folderFilter||f.folder.startsWith(folderFilter+'/');}):data.files;
Expand Down Expand Up @@ -6832,12 +6928,14 @@
var title=card.querySelector?card.querySelector('.code-card-name'):null;
if(title)title.style.transform='scale('+titleScale+')';
if(card.classList){
if(k<0.22&&card.classList.add)card.classList.add('code-far');
if(zoomHidesCodeText(k)&&card.classList.add)card.classList.add('code-far');
else if(card.classList.remove)card.classList.remove('code-far');
if(zoomShowsColorBlocks(k)&&card.classList.add)card.classList.add('code-blocks');
else if(card.classList.remove)card.classList.remove('code-blocks');
}
if(style.visibility==='visible')placed++;
});
return{placed:placed,titleScale:titleScale};
return{placed:placed,titleScale:titleScale,colorBlocks:zoomShowsColorBlocks(k),codeFar:zoomHidesCodeText(k)};
}

function armRecentDelete(armedId,clickedId){
Expand Down Expand Up @@ -6901,6 +6999,59 @@
return symbols;
}

function codeColorBlockLineCount(file){
if(file&&typeof file.content==='string'&&file.content)return Math.max(1,file.content.split('\n').length);
var lines=Number(file&&file.lines);
if(isFinite(lines)&&lines>0)return Math.max(1,lines);
var max=1;
(file&&file.functions||[]).forEach(function(fn){
var line=Number(fn&&fn.line);
if(isFinite(line)&&line>max)max=line;
});
return max;
}

function codeColorBlockSections(file,connections,prefs){
prefs=normalizeCodeCardPrefs(prefs);
var total=codeColorBlockLineCount(file);
var byLine=Object.create(null);
(file&&file.functions||[]).forEach(function(fn){
if(!fn||!fn.name||!fn.line)return;
var kind=fn.type==='class'||fn.type==='dataclass'||fn.type==='abstract_class'?'class':(fn.isExported?'export':'fn');
byLine[fn.line]={name:fn.name,kind:kind,startLine:fn.line};
});
extractFileSymbols(file,connections).forEach(function(sym){
var line=codeCardSymbolLine(file,sym.name);
if(!line||byLine[line])return;
byLine[line]={name:sym.name,kind:sym.kind||'var',startLine:line};
});
var starts=Object.keys(byLine).map(Number).filter(isFinite).sort(function(a,b){return a-b;});
var sections=[];
if(!starts.length){
sections.push({name:(file&&(file.name||file.path))||'file',kind:'file',startLine:1,endLine:total});
}else{
if(starts[0]>1){
sections.push({name:(file&&(file.name||file.path))||'file',kind:'file',startLine:1,endLine:starts[0]-1});
}
starts.forEach(function(start,i){
var item=byLine[start];
var end=i+1<starts.length?starts[i+1]-1:total;
if(end<start)end=start;
sections.push({name:item.name,kind:item.kind,startLine:start,endLine:end});
});
}
return sections.map(function(section){
var startVisual=file?codeCardVisualLineIndex(file,section.startLine,prefs):section.startLine;
var endVisual=file?codeCardVisualLineIndex(file,section.endLine,prefs):section.endLine;
if(endVisual<startVisual)endVisual=startVisual;
return Object.assign({},section,{
color:codeColorBlockKindColor(section.kind),
top:8+(startVisual-1)*CODE_CARD_LINE_HEIGHT,
height:Math.max(6,(endVisual-startVisual+1)*CODE_CARD_LINE_HEIGHT)
});
});
}

function codeCardSymbolPills(file,connections,prefs){
var size=codeCardSize(file,prefs);
return extractFileSymbols(file,connections).map(function(sym){
Expand Down Expand Up @@ -8998,9 +9149,12 @@
function updateGraphHighlight(path,blast){
if(!nodesRef.current||!linksRef.current)return;
var affectedSet=new Set(blast?blast.affected:[]);
nodesRef.current.selectAll('.nc').transition().duration(200)
nodesRef.current.selectAll('.nc,.nb').transition().duration(200)
.attr('opacity',function(n){if(n.id===path)return 1;if(affectedSet.has(n.id))return 1;return path?0.2:1;})
.attr('fill',function(n){if(n.id===path)return'#ff5f5f';if(affectedSet.has(n.id))return'#ff9f43';return getNodeColor(n);});
.attr('fill',function(n){
var fill=n.id===path?'#ff5f5f':affectedSet.has(n.id)?'#ff9f43':getNodeColor(n);
return d3.select(this).classed('nb')?graphColorBlockFill(fill):fill;
});
linksRef.current.transition().duration(200)
.attr('stroke-opacity',function(l){var src=l.source.id||l.source;var tgt=l.target.id||l.target;if(src===path||tgt===path)return 0.8;return path?0.05:0.4;})
.attr('stroke',function(l){var src=l.source.id||l.source;var tgt=l.target.id||l.target;if(src===path||tgt===path)return'var(--acc)';return theme==='light'?'#ccc':'#333';});
Expand Down Expand Up @@ -9393,12 +9547,14 @@
});
}
if(nodesRef.current){
var markScale=readableLabelScale((codeZoomTransformRef.current||{}).k);
var zoomK=(codeZoomTransformRef.current||{}).k;
var markScale=readableLabelScale(zoomK);
var hideChrome=zoomShowsColorBlocks(zoomK);
nodesRef.current.classed('has-code-card',function(d){return nodeReplacedByCard(d.id,codeCardPathsRef.current);});
nodesRef.current.classed('code-faded',false);
nodesRef.current.attr('display',function(d){return nodeReplacedByCard(d.id,codeCardPathsRef.current)?'none':null;});
nodesRef.current.selectAll('circle.nc').attr('transform',function(d){return nodeReplacedByCard(d.id,codeCardPathsRef.current)?'':'scale('+markScale+')';});
nodesRef.current.selectAll('text.node-label').attr('transform',function(d){return nodeReplacedByCard(d.id,codeCardPathsRef.current)?'':'scale('+markScale+')';});
nodesRef.current.selectAll('circle.nc').attr('transform',function(d){return nodeReplacedByCard(d.id,codeCardPathsRef.current)||hideChrome?'':'scale('+markScale+')';});
nodesRef.current.selectAll('text.node-label').attr('transform',function(d){return nodeReplacedByCard(d.id,codeCardPathsRef.current)||hideChrome?'':'scale('+markScale+')';});
}
}

Expand Down Expand Up @@ -9482,16 +9638,34 @@
graphNodesByIdRef.current=Object.create(null);
nodes.forEach(function(n){graphNodesByIdRef.current[n.id]=n;});
function collideR(d){return liveCodeCollideRadius(d,keepReadable&&codeCardPathsRef.current.has(d.id)?codeCardSizesRef.current[d.id]:null);}
function applyCanvasColorBlocks(k){
var on=zoomShowsColorBlocks(k);
var s=on?graphColorBlockScale(k):1;
if(svgRef.current&&svgRef.current.classList){
if(on)svgRef.current.classList.add('color-blocks');
else svgRef.current.classList.remove('color-blocks');
}
if(nodesRef.current){
nodesRef.current.selectAll('rect.nb').attr('display',on?null:'none').attr('transform',on?'scale('+s+')':null);
}
}
function applyReadableLabels(k){
applyCanvasColorBlocks(k);
if(!keepReadable)return;
var s=readableLabelScale(k);
var blocks=zoomShowsColorBlocks(k);
nodeLayer.selectAll('g').each(function(d){
var hidden=nodeReplacedByCard(d&&d.id,codeCardPathsRef.current);
var sel=d3.select(this);
sel.classed('has-code-card',hidden);
sel.classed('code-faded',false);
sel.attr('display',hidden?'none':null);
if(hidden)return;
if(blocks){
sel.select('circle.nc').attr('transform',null);
sel.select('text.node-label').attr('transform',null);
return;
}
sel.select('circle.nc').attr('transform','scale('+s+')');
sel.select('text.node-label').attr('transform','scale('+s+')');
});
Expand Down Expand Up @@ -9619,8 +9793,10 @@
if(keepReadable)return;
link.attr('stroke',theme==='light'?'#ccc':'#333').attr('stroke-opacity',0.4);
node.selectAll('.nc').attr('opacity',1).attr('fill',getC);
node.selectAll('.nb').attr('opacity',1).attr('fill',function(d){return graphColorBlockFill(getC(d));});
});
node.append('circle').attr('class','nc').attr('r',getR).attr('fill',getC).attr('stroke',function(d){var c=d3.color(getC(d));return c?c.brighter(0.3):'#fff';}).attr('stroke-width',1.5);
node.append('rect').attr('class','nb').attr('x',function(d){return -graphColorBlockSize(d)/2;}).attr('y',function(d){return -graphColorBlockSize(d)/2;}).attr('width',graphColorBlockSize).attr('height',graphColorBlockSize).attr('rx',3).attr('fill',function(d){return graphColorBlockFill(getC(d));}).attr('stroke',function(d){var c=d3.color(graphColorBlockFill(getC(d)));return c?c.darker(0.35):'#000';}).attr('stroke-width',1).attr('display','none');
// Hide labels for large graphs to reduce DOM overhead. Code view keeps titles readable.
if(keepReadable||!isLargeGraph||graphConfig.showLabels){
node.append('text').attr('class','node-label').attr('text-anchor','middle').attr('dy',0).attr('fill',theme==='light'?'#333':'#eee').attr('font-size',function(d){return (keepReadable?Math.max(9,Math.min(12,getR(d)*0.7)):Math.max(6,Math.min(10,getR(d)*0.6)))+'px';}).attr('font-family','JetBrains Mono').attr('font-weight','600').attr('pointer-events','none').text(function(d){var n=d.name.replace(/\.[^.]+$/,'');if(keepReadable)return n.length>18?n.slice(0,17)+'…':n;var maxLen=Math.max(4,Math.floor(getR(d)/2));return n.length>maxLen+1?n.slice(0,maxLen)+'…':n;});
Expand Down Expand Up @@ -9683,6 +9859,7 @@
if(tickCount%hullInterval===0)updateHulls();
});
node.selectAll('text').attr('opacity',(keepReadable||graphConfig.showLabels)?1:0);
applyCanvasColorBlocks((codeZoomTransformRef.current&&codeZoomTransformRef.current.k)||1);
if(keepReadable){
applyOpenedCardPlacements({skipFly:true});
placeRemainingCodeNodes();
Expand Down Expand Up @@ -11396,6 +11573,25 @@
var cardPrefs=currentCodeCardPrefs();
var cardSize=applyCodeCardUserSize(codeCardSize(file,cardPrefs),codeCardUserSizeRef.current[file.path]);
var pills=isPrimary?codeCardSymbolPills(file,data?data.connections:[],cardPrefs):[];
var colorBlocks=codeColorBlockSections(file,data?data.connections:[],cardPrefs);
var blockLayer=colorBlocks.length?React.createElement('div',{className:'code-color-blocks'},
colorBlocks.map(function(block){
return React.createElement('div',{
key:block.name+':'+block.startLine,
className:'code-color-block '+block.kind,
title:block.name,
style:{top:block.top+'px',height:block.height+'px',background:block.color},
onMouseEnter:function(e){
var host=codeCanvasRef.current;
if(!host)return;
var r=host.getBoundingClientRect();
setTooltip({x:e.clientX-r.left+10,y:e.clientY-r.top,title:block.name,content:block.kind+(block.startLine?' · L'+block.startLine:'')});
},
onMouseLeave:function(){setTooltip(null);}
});
})
):null;
body=React.createElement('div',{className:'code-card-source'},body,blockLayer);
return React.createElement('div',{key:file.path,'data-code-card':file.path,className:'code-card'+(isPrimary?' primary':' linked')+(cardSize.clipped?' clipped':'')+(cardSize.expand?' expand':'')+(cardSize.wrap?' wrap':''),onClick:function(e){
var taken=consumeCodeCardClick(codeCardIgnoreClickRef.current);
codeCardIgnoreClickRef.current=taken.ignoreNextClick;
Expand Down Expand Up @@ -11863,7 +12059,7 @@
),
React.createElement('div',{className:'panel-content'},
rightTab==='details'&&(selected?React.createElement(React.Fragment,null,
React.createElement('button',{className:'top-btn',style:{width:'100%',marginBottom:12},onClick:function(){setSelected(null);setBlastRadius(null);if(nodesRef.current){nodesRef.current.selectAll('.nc').transition().duration(200).attr('opacity',1).attr('fill',getNodeColor);}if(linksRef.current){linksRef.current.transition().duration(200).attr('stroke-opacity',0.4).attr('stroke',theme==='light'?'#ccc':'#333');}}},'← Back to Issues'),
React.createElement('button',{className:'top-btn',style:{width:'100%',marginBottom:12},onClick:function(){setSelected(null);setBlastRadius(null);if(nodesRef.current){nodesRef.current.selectAll('.nc,.nb').transition().duration(200).attr('opacity',1).attr('fill',function(n){return d3.select(this).classed('nb')?graphColorBlockFill(getNodeColor(n)):getNodeColor(n);});}if(linksRef.current){linksRef.current.transition().duration(200).attr('stroke-opacity',0.4).attr('stroke',theme==='light'?'#ccc':'#333');}}},'← Back to Issues'),
React.createElement('div',{className:'panel-header',style:{margin:'0 -12px 12px',padding:12}},
React.createElement('div',{style:{display:'flex',justifyContent:'space-between',alignItems:'flex-start'}},
React.createElement('div',null,
Expand Down
Loading
Loading