mirror of
https://github.com/callstack/agent-device.git
synced 2026-09-14 20:06:34 +08:00
cd19a73c55
Renders every production file under src/ as a pannable graph in one self-contained HTML file — no external requests, no runtime dependency, layouts precomputed at build time so the viewer never runs a physics simulation on a phone. pnpm depgraph # -> .tmp/depgraph/index.html (+ index.json) pnpm depgraph:test It reuses the layering gate's model (`listSourceFiles`, `resolveImportEdges`, `zoneRank`) rather than extracting its own graph. That matters more than it sounds: a separate extractor with its own resolution behaviour would draw a graph nobody enforces. Because the model is shared, its R6 count reproduces TYPE_INVERSION_BASELINE exactly, which doubles as a self-check. The README now documents WHEN it is productive, because the honest answer is "for three questions, and it misleads on a fourth": - what am I about to break (dependent counts, including the type-only and dynamic edges a grep for `from '...'` misses); - where is the debt concentrated (zone-level counts); - what is wrong that CI does not enforce — ~1300 transitively redundant value edges and 8 type-only/dynamic cycles, both outside the gate by design. The fourth: a cluster's SIZE IS NOT ITS DIFFICULTY. `commands -> client` looked like the obvious win at 28 edges into one file; moving that file down took the gate from 42 to 48, because the vocabulary it holds depends on commands/, metro/, core/ and remote/. The render shows an edge's weight, not whether it can be reversed — so the README pairs every visual question with the numeric query that answers "can this actually move?", verified against the real output rather than written from memory. Also states plainly that `pnpm check:layering` is authoritative and nothing here gates a merge: it is an instrument, not a rule. scripts/depgraph/** joins scripts/layering/**, scripts/perf/** and scripts/maestro-conformance/** in Fallow's ignorePatterns, which is how this repo already treats tooling trees. Worth knowing rather than discovering: that exempts viewer.js from the complexity gate, and its `draw` function would fail it. Two exports added to scripts/layering/model.ts: `zoneRank` (the viewer colours nodes by rank, so an inversion reads as an edge pointing the wrong way down the ramp) and `targetDagZone`, previously module-private. `pnpm check` green, 4488 unit tests. Verified against current main: 898 files, 4627 edges, 25 zones, R6 count matching the gate. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Bfu8HofkhybiAm5LECfqur
126 lines
3.8 KiB
HTML
126 lines
3.8 KiB
HTML
<title>agent-device — module dependency graph</title>
|
||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
||
<style>
|
||
/*__STYLES__*/
|
||
</style>
|
||
|
||
<div id="stage">
|
||
<canvas id="canvas"></canvas>
|
||
</div>
|
||
|
||
<header id="strip">
|
||
<h1>agent-device <span id="commit"></span></h1>
|
||
<div class="stats" id="stats"></div>
|
||
</header>
|
||
|
||
<button id="rail-toggle" type="button" aria-expanded="false" aria-controls="rail">Controls</button>
|
||
|
||
<aside id="rail" aria-label="Graph controls">
|
||
<div class="sheet-head">
|
||
<span>Controls</span>
|
||
<button type="button" class="close" id="rail-done" aria-label="Hide controls">×</button>
|
||
</div>
|
||
|
||
<div class="group">
|
||
<h2>Find a file</h2>
|
||
<input
|
||
id="search"
|
||
type="search"
|
||
placeholder="core/interactors/tap"
|
||
autocomplete="off"
|
||
spellcheck="false"
|
||
aria-label="Filter files by path"
|
||
/>
|
||
<p class="hint" id="search-hint">
|
||
Drag to pan, pinch or scroll to zoom, tap a node for detail.
|
||
</p>
|
||
</div>
|
||
|
||
<div class="group">
|
||
<h2>Layout</h2>
|
||
<div class="segmented" id="layout-mode" role="group" aria-label="Layout">
|
||
<button type="button" data-layout="cluster" aria-pressed="true">Clusters</button>
|
||
<button type="button" data-layout="layered" aria-pressed="false">Layers</button>
|
||
</div>
|
||
<p class="hint" id="layout-hint"></p>
|
||
</div>
|
||
|
||
<div class="group">
|
||
<h2>Size nodes by</h2>
|
||
<div class="segmented" id="size-mode" role="group" aria-label="Node size metric">
|
||
<button type="button" data-size="degree" aria-pressed="true">Coupling</button>
|
||
<button type="button" data-size="in" aria-pressed="false">Dependents</button>
|
||
<button type="button" data-size="loc" aria-pressed="false">Lines</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="group">
|
||
<h2>Overlay</h2>
|
||
<div class="checks" id="overlays">
|
||
<label class="check"
|
||
><input type="checkbox" id="show-back" /><span
|
||
class="swatch"
|
||
style="background: var(--critical)"
|
||
></span
|
||
>Spine back-edges (R5)</label
|
||
>
|
||
<label class="check"
|
||
><input type="checkbox" id="show-cycles" /><span
|
||
class="swatch"
|
||
style="background: var(--critical)"
|
||
></span
|
||
>Import cycles</label
|
||
>
|
||
<label class="check"
|
||
><input type="checkbox" id="show-type-inversion" /><span
|
||
class="swatch"
|
||
style="background: var(--warning)"
|
||
></span
|
||
>Type-only inversions (R6)</label
|
||
>
|
||
<label class="check"
|
||
><input type="checkbox" id="show-redundant" /><span
|
||
class="swatch"
|
||
style="background: var(--warning)"
|
||
></span
|
||
>Redundant edges</label
|
||
>
|
||
<label class="check"><input type="checkbox" id="hide-weak" />Static value edges only</label>
|
||
<label class="check"
|
||
><input type="checkbox" id="show-labels" checked />Labels when zoomed</label
|
||
>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="group">
|
||
<h2>Zones — rank ◄ colour</h2>
|
||
<div class="legend" id="legend"></div>
|
||
<p class="hint">
|
||
Colour is the layering gate's spine rank: <b>kernel</b> sink through <b>cli</b>. Grey zones
|
||
are intentionally unranked — R1–R4 still apply, but the gate asserts no ordering over them.
|
||
</p>
|
||
</div>
|
||
|
||
<div class="group">
|
||
<h2>Hotspots — most depended on</h2>
|
||
<div class="hotspots" id="hotspots"></div>
|
||
</div>
|
||
</aside>
|
||
|
||
<section id="detail" aria-live="polite"></section>
|
||
|
||
<div id="zoom">
|
||
<button type="button" id="zoom-in" aria-label="Zoom in">+</button>
|
||
<button type="button" id="zoom-out" aria-label="Zoom out">−</button>
|
||
<button type="button" id="zoom-fit" aria-label="Fit graph to screen">⤢</button>
|
||
</div>
|
||
|
||
<div id="tooltip" role="status"></div>
|
||
|
||
<script id="graph-data" type="application/json">
|
||
"__DATA__"
|
||
</script>
|
||
<script>
|
||
/*__SCRIPT__*/
|
||
</script>
|