knosky 0.6.2 → 0.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knosky",
3
- "version": "0.6.2",
3
+ "version": "0.6.3",
4
4
  "description": "Turn any repo or docs folder into an explorable city, plus a local MCP that grounds your AI in your own source with citations. Local-first, free, $0 tokens.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -188,9 +188,6 @@
188
188
  _clip={wx0:(0-cam.x)/cam.zoom-pad,wy0:(0-cam.y)/cam.zoom-pad,
189
189
  wx1:(innerWidth-cam.x)/cam.zoom+pad,wy1:(innerHeight-cam.y)/cam.zoom+pad};}
190
190
  function inView(wx,wy){return wx>=_clip.wx0&&wx<=_clip.wx1&&wy>=_clip.wy0&&wy<=_clip.wy1;}
191
- // District cluster descriptors — one entry per district, built at ingest time
192
- let CLUSTERS=[];
193
- function buildClustersNow(){CLUSTERS=[];const acc={};for(const n of NODES){const k=n.d;if(!acc[k])acc[k]={sumX:0,sumY:0,count:0};acc[k].sumX+=n.bx;acc[k].sumY+=n.by;acc[k].count++;}Object.keys(acc).forEach(k=>{const a=acc[k];const cfg=DCFG[k]||{name:k,color:'#7c83a3'};CLUSTERS.push({d:k,label:cfg.name,color:cfg.color,count:a.count,wx:a.sumX/a.count,wy:a.sumY/a.count});});}
194
191
 
195
192
  const TW=64,TH=32;
196
193
  // central relative-size system (multiples of a tile) — tune here to keep everything proportional
@@ -454,6 +451,15 @@
454
451
  ctx.beginPath();ctx.moveTo(c.B[0],c.B[1]);ctx.lineTo(c.R[0],c.R[1]);ctx.lineTo(c.R[0],c.R[1]+DEPTH);ctx.lineTo(c.B[0],c.B[1]+DEPTH);ctx.closePath();
455
452
  g=ctx.createLinearGradient(0,c.R[1],0,c.B[1]+DEPTH);g.addColorStop(0,PAL.night?"#1b160f":"#3a2a19");g.addColorStop(1,PAL.night?"#0d0906":"#1c1409");ctx.fillStyle=g;ctx.fill();
456
453
  ctx.strokeStyle=PAL.night?"#1f3a1c":"#3f6a37";ctx.lineWidth=3;ctx.beginPath();ctx.moveTo(c.L[0],c.L[1]);ctx.lineTo(c.B[0],c.B[1]);ctx.lineTo(c.R[0],c.R[1]);ctx.stroke();}
454
+ // LOD_CLUSTER top face: a single flat green fill so the zoomed-all-the-way-out
455
+ // view reads as a clean city silhouette (like a map/globe from orbit) instead of
456
+ // an empty outline or a cluttered badge overlay. No per-tile detail at this LOD.
457
+ function drawIslandTop(){const c=islandCorners();
458
+ ctx.beginPath();ctx.moveTo(c.T[0],c.T[1]);ctx.lineTo(c.R[0],c.R[1]);ctx.lineTo(c.B[0],c.B[1]);ctx.lineTo(c.L[0],c.L[1]);ctx.closePath();
459
+ const g=ctx.createLinearGradient(c.T[0],c.T[1],c.B[0],c.B[1]);
460
+ g.addColorStop(0,PAL.night?"#233a22":"#4f8a4a");g.addColorStop(1,PAL.night?"#162415":"#375f34");
461
+ ctx.fillStyle=g;ctx.fill();
462
+ ctx.strokeStyle=PAL.night?"#2c4a2a":"#2f5a2c";ctx.lineWidth=1.5;ctx.stroke();}
457
463
 
458
464
  // ---------- ground ----------
459
465
  const GRASS="#3c6a3a";
@@ -638,40 +644,18 @@
638
644
  if(flip){ctx.save();ctx.translate(x*2,0);ctx.scale(-1,1);ctx.drawImage(VEH,sx,sy,vCW,vCH,dx,dy,dW,dH);ctx.restore();}
639
645
  else ctx.drawImage(VEH,sx,sy,vCW,vCH,dx,dy,dW,dH);}
640
646
 
641
- // ---------- cluster badges (LOD_CLUSTER tier — entire districts collapsed to one badge) ----------
642
- function drawClusterBadge(cl){
643
- const x=cl.wx,y=cl.wy;
644
- if(!inView(x,y))return;
645
- if(focusK&&cl.d!==focusK)return;
646
- const R=28/cam.zoom,FONT_C="'Plus Jakarta Sans',system-ui,sans-serif";
647
- // outer glow
648
- ctx.save();
649
- ctx.globalAlpha=0.22;
650
- ctx.beginPath();ctx.arc(x,y,R*1.6,0,7);ctx.fillStyle=cl.color;ctx.fill();
651
- ctx.globalAlpha=1;
652
- // filled circle
653
- ctx.beginPath();ctx.arc(x,y,R,0,7);ctx.fillStyle=cl.color;ctx.fill();
654
- // border ring
655
- ctx.lineWidth=2;ctx.strokeStyle="rgba(255,255,255,.45)";ctx.stroke();
656
- // count label
657
- const label=cl.count>=1000?(Math.round(cl.count/100)/10)+'k':String(cl.count);
658
- ctx.fillStyle="#0a0a12";ctx.textAlign="center";ctx.textBaseline="middle";ctx.font="700 "+(13/cam.zoom)+"px "+FONT_C;
659
- ctx.fillText(label,x,y);
660
- // district name beneath
661
- ctx.globalAlpha=0.75;ctx.fillStyle=cl.color;ctx.font="600 "+(10/cam.zoom)+"px "+FONT_C;
662
- ctx.fillText(cl.label,x,y+R+12);
663
- ctx.restore();}
664
-
665
647
  // ---------- render ----------
666
648
  let focusK=null,filteredHidden=new Set(),selectedId=null,connSet=new Set();
667
649
  function render(){NOW=performance.now()-T0;updateLodAndClip();moveSky();ctx.setTransform(DPR,0,0,DPR,0,0);drawBackdrop();
668
650
  ctx.save();ctx.translate(cam.x,cam.y);ctx.scale(cam.zoom,cam.zoom);
669
651
  ctx.globalAlpha=Math.min(1,NOW/450);
670
652
 
671
- // LOD_CLUSTER: render one badge per district, skip all expensive geometry
653
+ // LOD_CLUSTER: zoomed all the way out -- show a clean city silhouette only,
654
+ // no per-district badges. Districts are reachable by zooming in or clicking
655
+ // a district in the sidebar (toggleDistrict -> focusDistrictCam already
656
+ // zooms to a tier where ground + buildings render).
672
657
  if(_lod===LOD_CLUSTER){
673
- drawIslandShadow();drawSlab();
674
- CLUSTERS.forEach(drawClusterBadge);
658
+ drawIslandShadow();drawSlab();drawIslandTop();
675
659
  ctx.globalAlpha=1;ctx.restore();return;}
676
660
 
677
661
  drawIslandShadow();drawSlab();drawGround();drawCloudShadows();drawShadows();drawChurn();
@@ -774,7 +758,7 @@
774
758
  document.getElementById('q').addEventListener('input',e=>applySearch(e.target.value.trim()));
775
759
  let looping=false;
776
760
  function boot(){if(!NODES.length){document.getElementById('boot').textContent="Waiting for city data…";document.getElementById('boot').style.display='';return;}
777
- document.getElementById('boot').style.display='none';buildClustersNow();buildChips();buildNav();applySearch(document.getElementById('q').value.trim());fitAll();cam.x=cam.tx;cam.y=cam.ty;cam.zoom=cam.tz;
761
+ document.getElementById('boot').style.display='none';buildChips();buildNav();applySearch(document.getElementById('q').value.trim());fitAll();cam.x=cam.tx;cam.y=cam.ty;cam.zoom=cam.tz;
778
762
  if(!looping){looping=true;T0=performance.now();loop();}}
779
763
 
780
764
  // ---------- data ingest: receive the authenticated payload over postMessage,