184 lines
16 KiB
HTML
184 lines
16 KiB
HTML
<!doctype html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
|
||
<meta name="theme-color" content="#1e1b16">
|
||
<meta name="description" content="Explore an entire codebase as a fast, searchable 2D and 3D landscape.">
|
||
<title>CodeNavigator</title>
|
||
<link rel="icon" href="./favicon.svg" type="image/svg+xml">
|
||
<link rel="preload" href="./fonts/archivo-black.woff2" as="font" type="font/woff2" crossorigin>
|
||
<link rel="preload" href="./fonts/atkinson-400.woff2" as="font" type="font/woff2" crossorigin>
|
||
<link rel="stylesheet" href="./styles.css">
|
||
</head>
|
||
<body>
|
||
<main id="app">
|
||
<header class="toolbar" aria-label="Codebase controls">
|
||
<div class="brand" aria-label="CodeNavigator"><svg viewBox="0 0 28 28" aria-hidden="true"><rect x="2.5" y="10" width="7" height="15" rx="1" fill="#c8f135" stroke="#000" stroke-width="1.6"/><rect x="10.5" y="3" width="7" height="22" rx="1" fill="#ff5ea8" stroke="#000" stroke-width="1.6"/><rect x="18.5" y="14" width="7" height="11" rx="1" fill="#5cc8ff" stroke="#000" stroke-width="1.6"/></svg><span class="brand-name">Code<em>Navigator</em></span></div>
|
||
<button class="tool primary" id="loadButton" title="Open a codebase (Ctrl+O)"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M3.5 7.5h6l2-2h4l2 2h3v11h-17z"/><path d="M3.5 9.5h17"/></svg><span class="label">Open</span></button>
|
||
<div class="tool-group" role="group" aria-label="View mode">
|
||
<button class="tool active" data-view="2d" title="Flat map"><svg viewBox="0 0 24 24" aria-hidden="true"><rect x="3.5" y="3.5" width="17" height="17" rx="1.5"/><path d="M3.5 11h17M12 3.5v17"/></svg><span class="label">2D</span></button>
|
||
<button class="tool" data-view="3d" title="3D landscape"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="m12 3 8 4.5v9L12 21l-8-4.5v-9zM4 7.5l8 4.5 8-4.5M12 12v9"/></svg><span class="label">3D</span></button>
|
||
<button class="tool" data-view="city" title="Walkable city"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M3 21h18M5 21V10l4-2v13M9 21V4l6 3v14M15 21v-9l4-1.5V21"/></svg><span class="label">City</span></button>
|
||
</div>
|
||
<div class="tool-group" role="group" aria-label="Map options">
|
||
<button class="tool" id="mapButton" title="Fit everything in view (F)"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M8 4H4v4m12-4h4v4M4 16v4h4m12-4v4h-4"/></svg><span class="label">Fit</span></button>
|
||
<button class="tool" id="layersButton" title="Semantic layers"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="m4 8 8-4 8 4-8 4zM4 12l8 4 8-4M4 16l8 4 8-4"/></svg><span class="label">Layers</span></button>
|
||
<button class="tool" id="codeButton" title="Show source code on tiles"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="m8 7-5 5 5 5M16 7l5 5-5 5M14 4l-4 16"/></svg><span class="label">Code</span></button>
|
||
<button class="tool icon-only" id="paletteButton" title="Change palette" aria-label="Change palette"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M20 13a8 8 0 1 0-8 7h1.5a1.5 1.5 0 0 0 0-3H12a2 2 0 0 1 0-4z"/><circle cx="7.5" cy="11" r="1"/><circle cx="10" cy="7.5" r="1"/><circle cx="14.5" cy="7.5" r="1"/></svg></button>
|
||
</div>
|
||
<div class="tool-group metric-switch" role="group" aria-label="Map metric">
|
||
<button class="metric active" data-metric="lines" title="Size tiles by lines"><b>#</b> <span>Lines</span></button>
|
||
<button class="metric" data-metric="references" title="Size tiles by references"><b>⌁</b> <span>Refs</span></button>
|
||
</div>
|
||
<div class="toolbar-spacer"></div>
|
||
<div class="scene-stats" id="sceneStats" aria-live="polite">
|
||
<span id="headlineStats">no repo yet</span>
|
||
<small id="fileStats">nothing open yet</small>
|
||
</div>
|
||
<label class="filter-wrap">
|
||
<span class="sr-only">Filter files and symbols</span>
|
||
<input id="searchInput" type="search" placeholder="find a file or symbol" autocomplete="off" spellcheck="false">
|
||
<kbd>/</kbd>
|
||
</label>
|
||
<button class="tool icon-only" id="settingsButton" title="Settings" aria-label="Settings" aria-haspopup="dialog" aria-controls="settingsDialog"><svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="3"/><path d="m9.5 3-.5 2-2 1-2-.5L3 9l1.5 1.5v3L3 15l2 3.5 2-.5 2 1 .5 2h5l.5-2 2-1 2 .5 2-3.5-1.5-1.5v-3L21 9l-2-3.5-2 .5-2-1-.5-2z"/></svg></button>
|
||
<span class="live-dot" role="status" aria-label="Connecting to indexer" title="Connecting to indexer"></span>
|
||
</header>
|
||
|
||
<section class="workspace">
|
||
<div class="viewport" id="viewport">
|
||
<canvas id="glCanvas"></canvas>
|
||
<canvas id="overlayCanvas"></canvas>
|
||
<div class="welcome" id="welcome">
|
||
<span class="eyebrow"><i></i>code, but it's a place now</span>
|
||
<h1>See your code<br><span>as a city.</span></h1>
|
||
<div class="welcome-sticker" aria-hidden="true">now with fire<small>(security alerts)</small></div>
|
||
<p>Every file is a building, every folder a street. <b>Fly over it, walk around it, get lost in it.</b> Search every symbol, follow imports door to door, and watch the insecure bits smoulder.</p>
|
||
<div class="welcome-actions"><button type="button" class="button-primary" id="welcomeOpen">Open a codebase →</button><span>or hit <kbd>Ctrl</kbd> <kbd>O</kbd></span></div>
|
||
<a class="welcome-oss" href="https://github.com/andydixon/CodeNavigator" target="_blank" rel="noopener"><svg viewBox="0 0 16 16" aria-hidden="true"><path fill="currentColor" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/></svg><span><b>It's open source now!</b> Poke around the code →</span><small>GPLv3 · free forever · PRs welcome</small></a>
|
||
<div class="welcome-features"><span>public + private GitHub</span><span class="local-only">local folders</span><span>walkable cities</span><span>import routes</span><span>things on fire</span><span>little people</span></div>
|
||
</div>
|
||
<div class="empty-state" id="emptyState" hidden>
|
||
<strong>Nothing to see here</strong>
|
||
<span>No source files in there. Try another repo?</span>
|
||
</div>
|
||
<div class="progress-card" id="progressCard" role="status" aria-live="polite" hidden>
|
||
<button type="button" class="progress-dismiss close-button" id="dismissProgress" aria-label="Dismiss notification"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="m6 6 12 12M18 6 6 18"/></svg></button>
|
||
<div class="progress-copy">
|
||
<strong id="progressTitle">Indexing codebase</strong>
|
||
<span id="progressMessage">Discovering files…</span>
|
||
</div>
|
||
<div class="progress-track"><i id="progressFill"></i></div>
|
||
<span class="progress-count" id="progressCount"></span>
|
||
<a class="dialog-button progress-action" id="progressAction" hidden></a>
|
||
</div>
|
||
<div class="tooltip" id="tooltip" hidden></div>
|
||
<div class="camera-help" id="cameraHelp" hidden>
|
||
<button type="button" data-camera-preset="isometric">Isometric</button>
|
||
<button type="button" data-camera-preset="top">Top</button>
|
||
<button type="button" data-camera-preset="front">Front</button>
|
||
<span><b>Drag</b> orbit</span>
|
||
<span><b>Shift + drag</b> pan</span>
|
||
<span><b>Scroll</b> dolly</span>
|
||
<span><b>F</b> reset</span>
|
||
</div>
|
||
<div class="city-hud" id="cityHud" hidden>
|
||
<div class="city-row" role="group" aria-label="City camera">
|
||
<button type="button" class="chip" data-city-view="heli" title="Helicopter (1)"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M3 6h18M12 6v3M6 13a6 4 0 0 0 12 0 6 4 0 0 0-12 0zM18 13h3M9 17l-2 3M15 17l2 3"/></svg><span class="label">Heli</span><kbd>1</kbd></button>
|
||
<button type="button" class="chip" data-city-view="walk" title="Walk (2)"><svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="13" cy="4" r="2"/><path d="m9 21 2-6 3 2v4M8 11l3-3 3 1 2 3M11 8l-1 6"/></svg><span class="label">Walk</span><kbd>2</kbd></button>
|
||
<button type="button" class="chip" data-city-view="fly" title="Fly (3)"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M2 16 22 8l-9 11-2-5z"/><path d="m11 14 11-6"/></svg><span class="label">Fly</span><kbd>3</kbd></button>
|
||
<span class="divider" aria-hidden="true"></span>
|
||
<button type="button" class="chip" id="tourButton" title="Guided tour (T)"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 21s-7-6.5-7-12a7 7 0 0 1 14 0c0 5.5-7 12-7 12z"/><circle cx="12" cy="9" r="2.5"/></svg><span class="label">Tour</span><kbd>T</kbd></button>
|
||
<button type="button" class="chip" id="shareButton" title="Copy a link to this exact spot"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M10 14a4 4 0 0 0 5.7 0l3-3a4 4 0 0 0-5.7-5.7l-1 1M14 10a4 4 0 0 0-5.7 0l-3 3a4 4 0 0 0 5.7 5.7l1-1"/></svg><span class="label">Copy link</span></button>
|
||
</div>
|
||
<span class="city-help" id="cityHelp"></span>
|
||
</div>
|
||
<div class="crosshair" id="crosshair" hidden></div>
|
||
<div class="tour-caption" id="tourCaption" hidden></div>
|
||
<div class="joystick" id="joystick" hidden><i></i></div>
|
||
<div class="city-target" id="cityTarget" hidden></div>
|
||
<div class="city-address" id="cityAddress" hidden></div>
|
||
<canvas class="minimap" id="minimap" width="220" height="150" hidden title="Click to travel"></canvas>
|
||
<nav class="breadcrumb" id="breadcrumb" aria-label="Selected file path">
|
||
<button id="homeButton" aria-label="Fit entire codebase">←</button>
|
||
<span id="breadcrumbText">nothing open</span>
|
||
</nav>
|
||
<div class="fps" id="fps">— fps</div>
|
||
<div class="legend" id="legend"></div>
|
||
<div class="canvas-controls" aria-label="Landscape navigation"><button type="button" id="zoomOutButton" title="Zoom out" aria-label="Zoom out">−</button><button type="button" id="fitButton" title="Fit codebase (F)" aria-label="Fit codebase"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M8 4H4v4m12-4h4v4M4 16v4h4m12-4v4h-4"/></svg></button><button type="button" id="zoomInButton" title="Zoom in" aria-label="Zoom in">+</button></div>
|
||
</div>
|
||
|
||
<button type="button" class="inspector-toggle" id="inspectorToggle" title="Hide the sidebar ([)" aria-controls="inspector" aria-expanded="true"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="m9 5 7 7-7 7"/></svg></button>
|
||
<aside class="inspector" id="inspector">
|
||
<div class="tabs" role="tablist">
|
||
<button class="tab active" data-tab="inspector" role="tab">Inspector</button>
|
||
<button class="tab" data-tab="results" role="tab">Results <span id="resultBadge"></span></button>
|
||
<button class="tab" data-tab="history" role="tab">History</button>
|
||
</div>
|
||
<section class="panel active" id="panel-inspector" role="tabpanel">
|
||
<p class="panel-hint" id="inspectHint">Select an entity on the map</p>
|
||
<div id="inspectContent"></div>
|
||
</section>
|
||
<section class="panel" id="panel-results" role="tabpanel">
|
||
<div class="result-summary" id="resultSummary">Type in the filter to search files and symbols.</div>
|
||
<div class="result-list" id="resultList"></div>
|
||
</section>
|
||
<section class="panel" id="panel-history" role="tabpanel">
|
||
<div class="result-summary">Recently inspected</div>
|
||
<div class="result-list" id="historyList"></div>
|
||
</section>
|
||
</aside>
|
||
</section>
|
||
</main>
|
||
|
||
<dialog id="loadDialog" aria-labelledby="loadTitle">
|
||
<form method="dialog" class="dialog-shell">
|
||
<div class="dialog-head">
|
||
<div><small>LET'S GO</small><h1 id="loadTitle">Open a codebase</h1><p class="dialog-description">Big repos welcome. Weird ones too.</p></div>
|
||
<button class="close-button" value="cancel" aria-label="Close"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="m6 6 12 12M18 6 6 18"/></svg></button>
|
||
</div>
|
||
<p class="github-notice" id="githubNotice" role="status" hidden></p>
|
||
<button type="button" class="source-card local-only" id="localFolderButton">
|
||
<span class="source-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M3 7h7l2-2h4l2 2h3v12H3zM3 10h18"/></svg></span>
|
||
<span><strong>Local folder</strong><small>Temporarily indexed by this server</small></span>
|
||
<b>→</b>
|
||
</button>
|
||
<div class="source-card github-card">
|
||
<span class="source-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="7" cy="5" r="2"/><circle cx="7" cy="19" r="2"/><circle cx="17" cy="5" r="2"/><path d="M7 7v10m10-10v3a4 4 0 0 1-4 4H7"/></svg></span>
|
||
<label for="githubInput"><strong>GitHub repository</strong><small>Public, or private once you sign in with GitHub</small></label>
|
||
<div class="github-row"><input id="githubInput" type="url" autocomplete="off" spellcheck="false" aria-describedby="githubError" placeholder="https://github.com/owner/repo"><button type="button" id="githubButton">Explore →</button></div>
|
||
<p class="form-error" id="githubError" role="alert"></p>
|
||
<p class="github-account" id="githubAccount" hidden></p>
|
||
</div>
|
||
<p class="dialog-foot">Code is held temporarily by this server, visible only to this browser, and cleared after an hour of inactivity.</p>
|
||
</form>
|
||
</dialog>
|
||
|
||
<dialog id="settingsDialog" class="settings-dialog" aria-labelledby="settingsTitle">
|
||
<form method="dialog" class="dialog-shell settings-shell">
|
||
<div class="dialog-head">
|
||
<div><small>FIDDLE WITH STUFF</small><h1 id="settingsTitle">Settings</h1><p class="dialog-description">Tweak it until it feels right.</p></div>
|
||
<button class="close-button" value="cancel" aria-label="Close settings"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="m6 6 12 12M18 6 6 18"/></svg></button>
|
||
</div>
|
||
<div id="settingsContent"></div>
|
||
<div class="preference-row"><label for="paletteSelect">Landscape palette<small>Color-code the layers of your codebase</small></label><select id="paletteSelect"><option value="0">Botanical</option><option value="1">Prismatic</option><option value="2">Autumn</option></select></div>
|
||
<div class="preference-row"><label for="sourceToggle">Source text<small>Show code directly in the landscape</small></label><input type="checkbox" class="switch" id="sourceToggle" checked></div>
|
||
<div class="preference-row"><label for="residentsToggle">City residents<small>Little figures wandering the streets; keeps the city animating</small></label><input type="checkbox" class="switch" id="residentsToggle" checked></div>
|
||
<div class="preference-row"><label for="fpsToggle">Performance monitor<small>Display frame rate on the canvas</small></label><input type="checkbox" class="switch" id="fpsToggle"></div>
|
||
<div class="shortcut-guide"><span>Keyboard essentials</span><div><kbd>/</kbd> Search <kbd>F</kbd> Fit landscape <kbd>Esc</kbd> Close</div></div>
|
||
<div class="settings-note">
|
||
<span class="settings-note-icon" aria-hidden="true">⌁</span>
|
||
<p><strong>Room to explore</strong><small>Move from the whole codebase to a single line. Scroll to zoom; drag to move. In 3D, hold Shift while dragging to pan.</small></p>
|
||
</div>
|
||
<div class="settings-actions">
|
||
<button class="dialog-button" value="close">Done</button>
|
||
</div>
|
||
</form>
|
||
</dialog>
|
||
<input id="folderFallback" type="file" webkitdirectory multiple hidden>
|
||
|
||
<script src="./config.js"></script>
|
||
<script type="module" src="./app.js"></script>
|
||
</body>
|
||
</html>
|