@gsteja2307/orbit-3d-explorer 0.0.0-stage → 1.0.0

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/README.md CHANGED
@@ -1,3 +1,30 @@
1
- # Temporary Holding Version
1
+ # Orbit 3D Explorer
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Published package: `@gsteja2307/orbit-3d-explorer`
4
+
5
+ Launch directly with Node.js 18 or newer:
6
+
7
+ ```sh
8
+ npx @gsteja2307/orbit-3d-explorer
9
+ ```
10
+
11
+ Then open http://127.0.0.1:4173. Stop the server with Ctrl+C. If that port is already in use, stop the other local preview first.
12
+
13
+ For local development:
14
+
15
+ Run with Node.js 18 or newer:
16
+
17
+ ```sh
18
+ npm install
19
+ npm run dev
20
+ ```
21
+
22
+ Open http://127.0.0.1:4173 in a WebGL-capable browser. Run commands from this project folder. Three.js is an npm dependency, pinned in package-lock.json. The scripts copy its browser assets into dist/vendor. Fonts optionally load from Google Fonts with system fallbacks.
23
+
24
+ Use `npm start` to run without server watch mode, `npm run check` for JavaScript syntax checks, and `npm run build` to refresh the static distribution and check it. The `dist/` folder can be hosted by any static web server. Browser changes require a page refresh; server watch mode restarts when server files change.
25
+
26
+ Click people to view roles, manager, contact information, skills, collaborators, direct reports and project allocation. Click projects to explore their team and connected app IDs; click applications to see owners and related projects. Drag to orbit, scroll to zoom, switch to 2D, search, filter departments, focus on a neighborhood, or use the directory view. Export downloads the sample dataset as JSON.
27
+
28
+ All 16 people, 6 projects, and 8 applications are fictional sample data. Edit the people, projects and apps arrays in dist/app.js to replace them. Application launch URLs are not configured. This is a browser-only prototype, without a database, authentication, live HR integrations, or shared editing. Do not deploy real employee data publicly; add authenticated access and an appropriate data backend first.
29
+
30
+ Files: dist/index.html (layout), dist/style.css (styles), dist/app.js (data and graph), dist/vendor (Three.js 0.169.0).
package/cli.mjs ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import './server.mjs';
package/dist/app.js ADDED
@@ -0,0 +1,31 @@
1
+ import * as THREE from 'three';
2
+ import {OrbitControls} from './vendor/OrbitControls.js';
3
+ const $=s=>document.querySelector(s),colors={person:0x65dfc2,project:0xa494ff,app:0xedbb70};
4
+ const people=[['e1','Maya Chen','VP of Engineering','Engineering',null,'San Francisco','Leadership,Architecture'],['e2','Alex Morgan','Staff Software Engineer','Engineering','e1','London','React,TypeScript,System design'],['e3','Priya Sharma','Engineering Manager','Engineering','e1','Bengaluru','Platform,Leadership,Go'],['e4','James Wilson','Senior Backend Engineer','Engineering','e3','London','Go,PostgreSQL,APIs'],['e5','Sofia Rodriguez','Product Designer','Design','e6','Barcelona','Figma,UX research,Design systems'],['e6','Noah Kim','Head of Design','Design',null,'Seoul','Strategy,Design systems'],['e7','Emma Thompson','Product Manager','Product','e8','London','Roadmaps,Analytics,Discovery'],['e8','Oliver Davis','Director of Product','Product',null,'New York','Strategy,Leadership'],['e9','Arjun Patel','Data Engineer','Data','e10','Bengaluru','Python,dbt,Snowflake'],['e10','Lily Zhang','Data Science Lead','Data',null,'Singapore','ML,Python,Statistics'],['e11','Ethan Brooks','Frontend Engineer','Engineering','e3','Austin','React,WebGL,TypeScript'],['e12','Isabella Rossi','UX Researcher','Design','e6','Milan','Research,Accessibility'],['e13','Lucas Martin','DevOps Engineer','Engineering','e3','Paris','Kubernetes,Terraform,AWS'],['e14','Ava Johnson','QA Engineer','Engineering','e3','New York','Playwright,Automation'],['e15','Daniel Park','ML Engineer','Data','e10','Seoul','PyTorch,Python,ML ops'],['e16','Zara Ahmed','Product Analyst','Product','e8','Dubai','SQL,Analytics,Experiments']].map((a,i)=>({id:a[0],name:a[1],title:a[2],department:a[3],manager:a[4],location:a[5],skills:a[6].split(','),type:'person',email:a[1].toLowerCase().replace(' ','.')+'@acme.example',employeeId:'EMP-'+(1001+i),joined:'202'+(i%4+1),timezone:['UTC−07:00','UTC+01:00','UTC+05:30','UTC+01:00','UTC+02:00','UTC+09:00','UTC+01:00','UTC−04:00','UTC+05:30','UTC+08:00','UTC−05:00','UTC+02:00','UTC+02:00','UTC−04:00','UTC+09:00','UTC+04:00'][i]}));
5
+ const projects=[{id:'p1',name:'Atlas Platform',description:'A unified platform for customer experiences, identity, and shared services.',status:'On track',progress:72,owner:'e7',members:[['e2','Technical lead',45],['e4','Backend engineer',60],['e5','Product designer',40],['e7','Product owner',65],['e11','Frontend engineer',70],['e14','Quality engineer',50]],apps:['a1','a2','a3'],due:'Dec 15, 2026'},{id:'p2',name:'Design System',description:'Accessible components and shared patterns for every Acme product.',status:'On track',progress:85,owner:'e6',members:[['e2','Engineering advisor',15],['e5','Design lead',40],['e6','Sponsor',20],['e11','Component engineer',30],['e12','Accessibility researcher',50]],apps:['a3','a4'],due:'Nov 20, 2026'},{id:'p3',name:'Data Intelligence',description:'Turn trusted company data into insights and actionable dashboards.',status:'At risk',progress:48,owner:'e10',members:[['e9','Data engineer',75],['e10','Technical lead',60],['e15','ML engineer',50],['e16','Product analyst',60]],apps:['a5','a6'],due:'Nov 30, 2026'},{id:'p4',name:'Cloud Migration',description:'Move core services to resilient infrastructure with observable deployments.',status:'On track',progress:61,owner:'e3',members:[['e1','Sponsor',20],['e3','Program lead',50],['e4','Backend engineer',40],['e13','Infrastructure lead',80]],apps:['a2','a7'],due:'Jan 22, 2027'},{id:'p5',name:'Customer 360',description:'A complete view of customer journeys, feedback, and engagement.',status:'Discovery',progress:24,owner:'e8',members:[['e7','Product manager',35],['e8','Sponsor',25],['e12','UX researcher',50],['e16','Analyst',40],['e9','Data consultant',25]],apps:['a5','a8'],due:'Feb 12, 2027'},{id:'p6',name:'AI Assistant',description:'An intelligent workspace assistant that helps teams find answers faster.',status:'On track',progress:39,owner:'e8',members:[['e2','Architecture lead',30],['e10','ML advisor',20],['e15','ML lead',50],['e14','QA engineer',50],['e8','Product sponsor',25]],apps:['a1','a6','a8'],due:'Feb 26, 2027'}].map(p=>({...p,type:'project',department:people.find(e=>e.id===p.owner).department}));
6
+ const apps=[['a1','Atlas Web','APP-001','Web application','e2','Production'],['a2','Identity API','APP-002','Shared service','e4','Production'],['a3','Component Hub','APP-003','Developer portal','e11','Production'],['a4','Design Library','APP-004','Design toolkit','e5','Production'],['a5','Insights Console','APP-005','Analytics platform','e9','Staging'],['a6','ML Gateway','APP-006','Machine learning API','e15','Production'],['a7','Cloud Console','APP-007','Infrastructure','e13','Production'],['a8','Customer Hub','APP-008','Internal application','e7','Staging']].map(a=>({id:a[0],name:a[1],appId:a[2],title:a[3],owner:a[4],environment:a[5],type:'app',department:people.find(e=>e.id===a[4]).department}));
7
+ const nodes=[...people,...projects,...apps],byId=Object.fromEntries(nodes.map(n=>[n.id,n])),edges=[];
8
+ projects.forEach(p=>{p.members.forEach(([id,role])=>edges.push({a:id,b:p.id,kind:role}));p.apps.forEach(id=>edges.push({a:p.id,b:id,kind:'Connected app'}))});people.filter(p=>p.manager).forEach(p=>edges.push({a:p.id,b:p.manager,kind:'Reports to'}));
9
+ let selected='e2',filter='all',focus=false,flat=false,directory=false;const connected=id=>new Set([id,...edges.filter(e=>e.a===id||e.b===id).map(e=>e.a===id?e.b:e.a)]);const initials=n=>n.name.split(' ').map(s=>s[0]).slice(0,2).join('');
10
+ const scene=new THREE.Scene(),camera=new THREE.PerspectiveCamera(45,1,.1,2000);camera.position.set(10,95,260);const renderer=new THREE.WebGLRenderer({antialias:true,alpha:true});renderer.setPixelRatio(Math.min(devicePixelRatio,2));$('#scene').append(renderer.domElement);const controls=new OrbitControls(camera,renderer.domElement);controls.enableDamping=true;controls.minDistance=65;controls.maxDistance=520;controls.autoRotate=false;controls.autoRotateSpeed=.35;scene.add(new THREE.AmbientLight(0xffffff,2));const light=new THREE.DirectionalLight(0xffffff,3);light.position.set(50,100,100);scene.add(light);
11
+ projects.forEach((n,i)=>{let a=i/6*Math.PI*2;n.pos=new THREE.Vector3(Math.cos(a)*65,Math.sin(i*2)*20,Math.sin(a)*50)});
12
+ people.forEach((n,i)=>{let a=i/16*Math.PI*2+.2;n.pos=new THREE.Vector3(Math.cos(a)*105,Math.sin(i*2.4)*38,Math.sin(a)*80)});apps.forEach((n,i)=>{let a=i/8*Math.PI*2+.4;n.pos=new THREE.Vector3(Math.cos(a)*145,Math.sin(i*1.8)*28,Math.sin(a)*100)});
13
+ const meshes=[];nodes.forEach(n=>{const geo=n.type==='person'?new THREE.SphereGeometry(2.7,20,16):n.type==='project'?new THREE.IcosahedronGeometry(4.3,0):new THREE.BoxGeometry(4.3,4.3,4.3);const mesh=new THREE.Mesh(geo,new THREE.MeshStandardMaterial({color:colors[n.type],emissive:colors[n.type],emissiveIntensity:.25,roughness:.4,metalness:.2,transparent:true}));mesh.position.copy(n.pos);mesh.userData.node=n;scene.add(mesh);n.mesh=mesh;meshes.push(mesh);const label=document.createElement('button');label.className='nodeLabel';label.textContent=n.name;if(n.type==='app'){const small=document.createElement('small');small.textContent=n.appId;label.append(small)}label.onclick=()=>select(n.id);$('#labels').append(label);n.label=label});
14
+ edges.forEach(e=>{e.line=new THREE.Line(new THREE.BufferGeometry().setFromPoints([byId[e.a].pos,byId[e.b].pos]),new THREE.LineBasicMaterial({color:0x3c5968,transparent:true,opacity:.32}));scene.add(e.line)});
15
+ const grid=new THREE.GridHelper(600,40,0x233843,0x1b2b35);grid.position.y=-55;grid.material.transparent=true;grid.material.opacity=.25;scene.add(grid);
16
+ function resize(){let r=$('#scene').getBoundingClientRect();camera.aspect=r.width/r.height;camera.updateProjectionMatrix();renderer.setSize(r.width,r.height)}new ResizeObserver(resize).observe($('#scene'));
17
+ function render(){requestAnimationFrame(render);controls.update();renderer.render(scene,camera);const r=$('#scene').getBoundingClientRect();nodes.forEach(n=>{const p=n.mesh.position.clone().project(camera);n.label.style.left=((p.x+1)/2*r.width)+'px';n.label.style.top=((-p.y+1)/2*r.height+19)+'px';n.label.style.display=n.mesh.visible&&p.z<1&&p.z>-1?'block':'none';n.label.style.zIndex=Math.round((1-p.z)*1000);});}render();
18
+ const ray=new THREE.Raycaster();let down;renderer.domElement.addEventListener('pointerdown',e=>down=[e.clientX,e.clientY]);renderer.domElement.addEventListener('pointerup',e=>{if(!down||Math.hypot(e.clientX-down[0],e.clientY-down[1])>5)return;const r=renderer.domElement.getBoundingClientRect();ray.setFromCamera(new THREE.Vector2((e.clientX-r.left)/r.width*2-1,-(e.clientY-r.top)/r.height*2+1),camera);const hit=ray.intersectObjects(meshes.filter(m=>m.visible))[0];if(hit)select(hit.object.userData.node.id)});
19
+ function row(id,sub=''){const n=byId[id];return `<button class="connection ${n.type==='project'?'projectLink':n.type==='app'?'appLink':''}" data-node="${id}"><span class="mini">${n.type==='person'?initials(n):n.type==='project'?'◇':'▣'}</span><span><strong>${n.name}</strong><small>${sub||n.title||n.status}</small></span><span class="arrow">↗</span></button>`}
20
+ function section(title,content,count=''){return `<div class="sectionTitle">${title}<span>${count}</span></div>${content}`}
21
+ function select(id){selected=id;detail();update()}
22
+ function detail(){const n=byId[selected];let html=`<div class="detailTop">${n.type==='person'?'EMPLOYEE PROFILE':n.type==='project'?'PROJECT OVERVIEW':'APPLICATION DETAILS'}<button id="copy" title="Copy profile ID">⧉</button></div><div class="bigAvatar">${n.type==='person'?initials(n):n.type==='project'?'◇':'▣'}</div><h2>${n.name}</h2><div class="role">${n.title||'Cross-functional initiative'}</div><span class="badge">● ${n.type==='person'?'Active · '+n.department:n.status||n.environment}</span>`;
23
+ if(n.type==='person'){const ps=projects.filter(p=>p.members.some(m=>m[0]===n.id)),allocation=ps.reduce((s,p)=>s+p.members.find(m=>m[0]===n.id)[2],0),collabs=[...new Set(ps.flatMap(p=>p.members.map(m=>m[0])))].filter(id=>id!==n.id);html+=`<div class="meta"><div><span>Employee ID</span><b>${n.employeeId}</b></div><div><span>Location</span><b>${n.location}</b></div><div><span>Joined</span><b>${n.joined}</b></div><div><span>Time zone</span><b>${n.timezone}</b></div></div><p><a href="mailto:${n.email}">${n.email}</a></p>`;html+=section('REPORTS TO',n.manager?row(n.manager):'<p>Top-level leader</p>');html+=section('PROJECTS',ps.map(p=>{let m=p.members.find(m=>m[0]===n.id);return row(p.id,m[1]+' · '+m[2]+'% allocation')}).join(''),ps.length);html+=section('TOTAL ALLOCATION',`<div class="load"><i style="width:${Math.min(allocation,100)}%"></i></div><p>${allocation}% allocated${allocation>100?' · Over capacity':''}</p>`);html+=section('SKILLS',`<div class="chips">${n.skills.map(s=>`<span>${s}</span>`).join('')}</div>`);html+=section('WORKS WITH',collabs.map(id=>row(id)).join(''),collabs.length);const reports=people.filter(p=>p.manager===n.id);if(reports.length)html+=section('DIRECT REPORTS',reports.map(p=>row(p.id)).join(''),reports.length)}
24
+ if(n.type==='project'){html+=`<p style="margin-top:17px">${n.description}</p><div class="meta"><div><span>Target date</span><b>${n.due}</b></div><div><span>Completion</span><b>${n.progress}%</b></div></div><div class="load"><i style="width:${n.progress}%"></i></div>`;html+=section('PROJECT OWNER',row(n.owner));html+=section('CONNECTED APPLICATIONS',n.apps.map(id=>row(id,byId[id].appId+' · '+byId[id].environment)).join(''),n.apps.length);html+=section('PROJECT TEAM',n.members.map(([id,role,load])=>row(id,role+' · '+load+'%')).join(''),n.members.length)}
25
+ if(n.type==='app'){html+=`<div class="meta"><div><span>Application ID</span><b>${n.appId}</b></div><div><span>Environment</span><b>${n.environment}</b></div></div><p>Sample application registry entry. Connect your real application URL in the data model to enable launch links.</p>`;html+=section('APPLICATION OWNER',row(n.owner));const ps=projects.filter(p=>p.apps.includes(n.id));html+=section('CONNECTED PROJECTS',ps.map(p=>row(p.id)).join(''),ps.length);const team=[...new Set(ps.flatMap(p=>p.members.map(m=>m[0])))];html+=section('CONTRIBUTORS',team.map(id=>row(id)).join(''),team.length)}
26
+ html+=`<button id="focus" class="focus">${focus?'Show entire network':'◎ Focus on connections'}</button>`;$('#details').innerHTML=html;$('#details').querySelectorAll('[data-node]').forEach(b=>b.onclick=()=>{select(b.dataset.node);$('#details').scrollTop=0});$('#focus').onclick=()=>{focus=!focus;detail();update()};$('#copy').onclick=async()=>{try{await navigator.clipboard.writeText(n.employeeId||n.appId||n.id);toast('ID copied to clipboard')}catch{toast('ID: '+(n.employeeId||n.appId||n.id))}};}
27
+ function update(){const q=$('#search').value.toLowerCase(),dep=$('#department').value,near=connected(selected);let count=0;nodes.forEach(n=>{const match=(!q||[n.name,n.title,n.appId,n.employeeId,...n.skills||[]].join(' ').toLowerCase().includes(q))&&(!dep||n.department===dep)&&(filter==='all'||filter===n.type)&&(!focus||near.has(n.id));n.mesh.visible=match;if(match)count++;n.mesh.material.opacity=near.has(n.id)?1:.38;n.mesh.scale.setScalar(n.id===selected?1.65:1);n.label.classList.toggle('selected',n.id===selected);n.label.style.opacity=near.has(n.id)?1:.6});edges.forEach(e=>{e.line.visible=byId[e.a].mesh.visible&&byId[e.b].mesh.visible;const active=e.a===selected||e.b===selected;e.line.material.color.setHex(active?colors[byId[selected].type]:0x395165);e.line.material.opacity=active?.8:.16});$('#count').textContent=`${count} nodes · ${flat?'2D':'Live 3D'}`;$('#empty').hidden=count>0;$('#directory').innerHTML=nodes.filter(n=>n.mesh.visible).map(n=>`<button class="directoryRow" data-node="${n.id}"><span>${n.name}</span><small>${n.appId||n.title||n.status}</small></button>`).join('')||'<p>No matching records.</p>';$('#directory').querySelectorAll('button').forEach(b=>b.onclick=()=>select(b.dataset.node));}
28
+ $('#search').oninput=update;$('#department').onchange=update;document.querySelectorAll('[data-type]').forEach(b=>b.onclick=()=>{filter=b.dataset.type;document.querySelectorAll('[data-type]').forEach(x=>x.classList.toggle('active',x===b));update()});$('#reset').onclick=()=>{filter='all';focus=false;$('#search').value='';$('#department').value='';document.querySelectorAll('[data-type]').forEach(x=>x.classList.toggle('active',x.dataset.type==='all'));camera.position.set(10,95,260);controls.target.set(0,0,0);if(flat)$('#flat').click();select('e2')};$('#rotate').onclick=()=>{controls.autoRotate=!controls.autoRotate;$('#rotate').style.color=controls.autoRotate?'#65dfc2':''};$('#zoomIn').onclick=()=>{camera.position.sub(controls.target).multiplyScalar(.85).add(controls.target)};$('#zoomOut').onclick=()=>{camera.position.sub(controls.target).multiplyScalar(1.15).add(controls.target)};
29
+ $('#flat').onclick=()=>{flat=!flat;nodes.forEach(n=>n.mesh.position.set(n.pos.x,flat?0:n.pos.y,n.pos.z));edges.forEach(e=>e.line.geometry.setFromPoints([byId[e.a].mesh.position,byId[e.b].mesh.position]));camera.position.set(flat?0:10,flat?290:95,flat?.1:260);controls.target.set(0,0,0);controls.enableRotate=!flat;$('#flat').textContent=flat?'3D':'2D';update()};
30
+ function view(d){directory=d;$('#directory').hidden=!d;$('#directoryTab').classList.toggle('active',d);$('#graphTab').classList.toggle('active',!d)}$('#directoryTab').onclick=()=>view(true);$('#graphTab').onclick=()=>view(false);
31
+ $('#export').onclick=()=>{const data={workspace:'Acme (sample data)',people,projects,applications:apps};const clean=JSON.stringify(data,(k,v)=>['pos','mesh','label'].includes(k)?undefined:v,2);const url=URL.createObjectURL(new Blob([clean],{type:'application/json'}));const a=document.createElement('a');a.href=url;a.download='orbit-directory.json';a.click();setTimeout(()=>URL.revokeObjectURL(url),1000);toast('Sample directory exported')};function toast(t){$('#toast').textContent=t;$('#toast').style.display='block';setTimeout(()=>$('#toast').style.display='none',2500)}document.addEventListener('keydown',e=>{if(e.key==='/'&&!['INPUT','SELECT'].includes(document.activeElement.tagName)){e.preventDefault();$('#search').focus()}if(e.key==='Escape'){$('#search').value='';focus=false;detail();update()}});detail();update();
@@ -0,0 +1,6 @@
1
+ <!doctype html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Orbit • People & Projects</title><link rel="stylesheet" href="style.css"><script type="importmap">{"imports":{"three":"./vendor/three.module.js"}}</script></head><body>
2
+ <aside class="rail"><a class="logo" href="./" aria-label="Orbit home">◈</a><button class="railbtn active" id="graphTab" title="Network view">⌘</button><button class="railbtn" id="directoryTab" title="Directory view">☷</button><div class="railbottom">OG</div></aside>
3
+ <div class="shell"><header><div><span class="brand">orbit<span class="dot">.</span></span><span class="divider"></span><span class="workspace">Acme workspace <span class="muted">⌄</span></span></div><div class="headerRight"><span class="demo">● SAMPLE WORKSPACE</span><button id="export">↓ Export data</button><div class="avatar">JD</div></div></header>
4
+ <main><div class="heading"><div><div class="eyebrow">YOUR ORGANIZATION, CONNECTED</div><h1>People & projects</h1><p>Discover the people, work, and applications that connect your team.</p></div><div class="stats"><div><b>16</b><span>People</span></div><div><b>6</b><span>Projects</span></div><div><b>8</b><span>Applications</span></div></div></div>
5
+ <div class="toolbar"><label class="search">⌕ <input id="search" placeholder="Search people, projects, app IDs…" aria-label="Search directory"><kbd>/</kbd></label><select id="department" aria-label="Department"><option value="">All departments</option><option>Engineering</option><option>Product</option><option>Design</option><option>Data</option></select><div class="tabs"><button class="active" data-type="all">All nodes</button><button data-type="person">People</button><button data-type="project">Projects</button><button data-type="app">Apps</button></div><button id="reset">↺ Reset view</button></div>
6
+ <div class="content"><section class="graph" id="graph"><div id="scene"></div><div id="labels"></div><div class="graphTitle"><span class="live"></span> Organization graph <span id="count">30 nodes · Live 3D</span></div><div id="empty" hidden>No matching nodes. Try another search or department.</div><div class="legend"><span><i class="person"></i>People</span><span><i class="project"></i>Projects</span><span><i class="app"></i>Applications</span></div><div class="graphTools"><button id="rotate" title="Toggle auto rotation">⟳</button><button id="zoomIn" title="Zoom in">+</button><button id="zoomOut" title="Zoom out">−</button><button id="flat" title="Toggle 2D / 3D">2D</button></div><div class="hint">Drag to orbit <em>·</em> Scroll to zoom <em>·</em> Click to explore</div><div id="directory" hidden></div></section><aside id="details" class="details"></aside></div><footer><span><i class="live"></i> Explore your team's connections</span><span>Demo data · Select any node to get started</span></footer></main></div><div id="toast" role="status"></div><script type="module" src="app.js"></script></body></html>
package/dist/style.css ADDED
@@ -0,0 +1,2 @@
1
+ @import url('https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=Space+Grotesk:wght@400;500;600;700&display=swap');
2
+ :root{color-scheme:dark;--bg:#0c1016;--panel:#11171f;--line:#252d38;--text:#e7edf4;--muted:#7e8b9d;--teal:#65dfc2;--violet:#a494ff;--amber:#edbb70}*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--text);font:14px 'DM Sans',system-ui,sans-serif}button,input,select{font:inherit}button,select{cursor:pointer;color:var(--text);background:#19212c;border:1px solid var(--line);border-radius:7px;padding:9px 12px}button:hover{background:#283446;border-color:#596b80}button:focus-visible,input:focus-visible,select:focus-visible{outline:2px solid var(--teal);outline-offset:3px}.rail{position:fixed;width:70px;height:100vh;border-right:1px solid var(--line);display:flex;align-items:center;flex-direction:column;gap:18px;padding:24px 0;background:#0e141c}.logo{font-size:37px;color:var(--teal);text-decoration:none;margin-bottom:39px}.railbtn{font-size:25px;border:0;background:transparent;width:44px;height:45px;padding:0;color:var(--muted)}.railbtn.active{color:var(--teal);background:#18312f}.railbottom{margin-top:auto;border:1px solid #435249;border-radius:50%;padding:9px;color:#b7c8bc;font-size:11px}.shell{margin-left:70px}header{height:82px;border-bottom:1px solid var(--line);display:flex;align-items:center;justify-content:space-between;padding:0 32px}header>div{display:flex;align-items:center;gap:24px}.brand{font:700 29px 'Space Grotesk',sans-serif;letter-spacing:-1.5px}.dot{color:var(--teal)}.divider{height:22px;width:1px;background:var(--line)}.workspace{color:#b1bccb;font-size:12px}.muted{color:var(--muted)}.headerRight{gap:18px}.demo{font-size:9px;letter-spacing:1.2px;color:#90a49d}.avatar{background:#36344d;color:#d7cbff;border:1px solid #514869;border-radius:50%;width:34px;height:34px;display:grid;place-items:center;font-size:11px}main{padding:30px 30px 0}.heading{display:flex;align-items:center;justify-content:space-between;margin-bottom:26px}.eyebrow{font-size:9px;color:var(--teal);letter-spacing:2px;font-weight:700}h1{font:500 30px 'Space Grotesk',sans-serif;letter-spacing:-1px;margin:10px 0}p{color:var(--muted);line-height:1.6;margin:6px 0;font-size:12px}.stats{display:flex;gap:32px}.stats div{display:flex;flex-direction:column;gap:5px;border-left:1px solid var(--line);padding-left:25px}.stats b{font:500 25px 'Space Grotesk',sans-serif}.stats span{font-size:10px;color:var(--muted)}.toolbar{display:flex;gap:12px;margin-bottom:18px;align-items:center}.search{display:flex;align-items:center;gap:10px;flex:1;background:#131b24;border:1px solid var(--line);border-radius:7px;padding:9px 12px;font-size:19px;color:var(--muted)}input{background:transparent;border:0;outline:none;color:var(--text);width:100%;font-size:12px}kbd{font-size:10px;border:1px solid #394353;border-radius:3px;padding:1px 5px}.toolbar select,.toolbar>button{font-size:11px;height:37px}.tabs{display:flex;padding:3px;border:1px solid var(--line);border-radius:7px}.tabs button{font-size:11px;border:0;background:transparent;padding:7px 12px;color:var(--muted)}.tabs button.active{background:#273540;color:white}.content{display:grid;grid-template-columns:minmax(0,1fr) 330px;gap:17px;height:calc(100vh - 297px);min-height:510px;max-height:900px}.graph{position:relative;overflow:hidden;border:1px solid var(--line);border-radius:11px;background:radial-gradient(ellipse at center,#19232d 0%,#101720 70%)}#scene,#labels{position:absolute;inset:0}#scene canvas{display:block;width:100%;height:100%}#labels{pointer-events:none;overflow:hidden}.nodeLabel{position:absolute;transform:translate(-50%,-50%);font-size:10px;white-space:nowrap;color:#bdcad6;text-shadow:0 2px 4px #000;pointer-events:auto;cursor:pointer;background:#121a22c9;border:1px solid #34445188;padding:4px 7px;border-radius:5px}.nodeLabel.selected{color:#fff;border-color:var(--teal);background:#1b3c36}.nodeLabel small{display:block;font-size:8px;color:#8496a8;margin-top:2px}.graphTitle{position:absolute;top:20px;left:20px;font-size:11px;display:flex;align-items:center;gap:8px}.graphTitle>span:last-child{font-size:9px;color:var(--muted);border-left:1px solid #394451;padding-left:10px;margin-left:4px}.live{display:inline-block;width:6px;height:6px;background:var(--teal);border-radius:50%}.legend{position:absolute;bottom:55px;left:20px;display:flex;gap:16px;font-size:9px;color:#9aabba}.legend span{display:flex;align-items:center;gap:6px}.legend i{width:7px;height:7px;border-radius:50%}.person{background:var(--teal)}.project{background:var(--violet)}.app{background:var(--amber)}.graphTools{position:absolute;right:15px;bottom:53px;display:flex;gap:4px}.graphTools button{padding:5px 9px;font-size:13px;background:#19232f}.hint{position:absolute;bottom:0;width:100%;text-align:center;padding:13px;border-top:1px solid #26313b;color:#637488;font-size:9px}.hint em{padding:0 12px;font-style:normal}.details{border:1px solid var(--line);border-radius:11px;background:#121922;overflow:auto;padding:22px}.detailTop{display:flex;justify-content:space-between;color:var(--muted);font-size:9px;letter-spacing:1.7px}.detailTop button{padding:0;border:0;background:transparent;font-size:16px}.bigAvatar{margin-top:22px;width:58px;height:58px;border-radius:15px;background:linear-gradient(135deg,#244d4b,#233a47);border:1px solid #436862;display:grid;place-items:center;color:#9ce9d6;font:500 21px 'Space Grotesk',sans-serif}.details h2{font:500 23px 'Space Grotesk',sans-serif;margin:14px 0 4px;letter-spacing:-.7px}.role{font-size:11px;color:#99a8bc}.badge{display:inline-block;padding:4px 7px;margin-top:12px;background:#1a332e;color:#83d3b7;border:1px solid #2c4a40;border-radius:5px;font-size:9px}.meta{display:grid;grid-template-columns:1fr 1fr;gap:15px;margin:20px 0}.meta span{font-size:9px;color:var(--muted);display:block;margin-bottom:5px}.meta b{font-size:10px;font-weight:500}.sectionTitle{display:flex;justify-content:space-between;font-size:10px;margin:23px 0 10px;letter-spacing:.3px}.sectionTitle span{color:var(--muted)}.connection{width:100%;display:flex;align-items:center;gap:10px;text-align:left;margin-top:7px;padding:10px;background:#171f2a;border:1px solid #2a3442}.connection .mini{width:29px;height:29px;background:#283c3d;border-radius:7px;display:grid;place-items:center;color:var(--teal);font-size:10px;flex-shrink:0}.connection.projectLink .mini{background:#302a49;color:var(--violet)}.connection.appLink .mini{background:#413527;color:var(--amber)}.connection strong{display:block;font-size:11px;font-weight:500}.connection small{font-size:9px;color:var(--muted);display:block;margin-top:4px}.connection .arrow{margin-left:auto;color:#6c7a8c}.chips{display:flex;flex-wrap:wrap;gap:5px}.chips span{font-size:9px;border:1px solid #344151;border-radius:5px;padding:5px 7px;color:#a3b2c5}.focus{width:100%;background:#70dec1;color:#10251f;border:0;margin-top:22px;font-size:11px;font-weight:700}.focus:hover{background:#a1efd9}.load{height:4px;border-radius:4px;background:#2b3643;margin:9px 0}.load i{height:100%;display:block;background:var(--teal);border-radius:4px}footer{padding:17px 0;display:flex;justify-content:space-between;color:#647387;font-size:9px}footer .live{width:4px;height:4px;margin-right:6px}#empty{position:absolute;top:45%;left:15%;right:15%;text-align:center;color:var(--muted)}#directory{position:absolute;inset:55px 12px 45px;overflow:auto;background:#111923;padding:10px;z-index:3}.directoryRow{display:flex;justify-content:space-between;width:100%;margin:6px 0;text-align:left;font-size:12px}.directoryRow small{color:var(--muted)}#toast{position:fixed;bottom:25px;left:50%;transform:translateX(-50%);background:#29483d;color:#e0fff2;border-radius:8px;padding:12px 20px;font-size:12px;display:none;z-index:10}a{color:var(--teal)}@media(max-width:1100px){.content{grid-template-columns:minmax(0,1fr) 290px}.tabs button{padding:7px}.workspace,.demo{display:none}.stats{gap:15px}main{padding:25px 18px 0}}@media(max-width:800px){.rail{width:52px}.shell{margin-left:52px}header{padding:0 18px;height:65px}.stats{display:none}.toolbar{flex-wrap:wrap}.search{min-width:100%}.content{grid-template-columns:1fr;height:auto;max-height:none}.graph{height:560px}.details{max-height:600px}.heading h1{font-size:26px}footer{gap:15px}.graphTitle>span:last-child{display:none}.headerRight button{font-size:10px}}
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright © 2010-2024 three.js authors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.