Files
archlens/docs/arch/html/archlens.html
Gabriel Kaszewski c4fb1ed699
All checks were successful
CI / Check / Test (push) Successful in 2m51s
Architecture Docs / Generate diagrams (push) Successful in 3m36s
fix: remove unused assignment and import warnings in tests
2026-06-17 10:34:56 +02:00

142 lines
18 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Architecture Diagram</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: sans-serif; background: #1a1a2e; color: #eee; display: flex; height: 100vh; }
#sidebar { width: 280px; background: #16213e; padding: 1rem; overflow-y: auto; border-right: 1px solid #0f3460; }
#cy { flex: 1; }
h2 { color: #e94560; margin-bottom: 0.5rem; font-size: 1rem; }
#detail { padding: 0.5rem 0; font-size: 0.85rem; }
.member { padding: 2px 0; color: #aaa; }
</style>
</head>
<body>
<div id="sidebar">
<h2>Architecture Diagram</h2>
<div id="detail"><p>Click a node to see details.</p></div>
</div>
<div id="cy"></div>
<script>
const GRAPH = {"nodes":[{"id":"n0","label":"AsciiRenderer","module":"Adapters","kind":"Struct","fields":[],"methods":["+new() -> Self","-format_kind(element: &CodeElement) -> &'static str"]},{"id":"n1","label":"D2Renderer","module":"Adapters","kind":"Struct","fields":["level: DiagramLevel"],"methods":["+new() -> Self","+with_level(level: DiagramLevel) -> Self"]},{"id":"n2","label":"CargoWorkspaceAnalyzer","module":"Adapters","kind":"Struct","fields":[],"methods":["+new() -> Self"]},{"id":"n3","label":"WorkspaceToml","module":"Adapters","kind":"Struct","fields":["workspace: Option"],"methods":[]},{"id":"n4","label":"WorkspaceSection","module":"Adapters","kind":"Struct","fields":["members: Vec"],"methods":[]},{"id":"n5","label":"MemberToml","module":"Adapters","kind":"Struct","fields":["package: Option","dependencies: HashMap"],"methods":[]},{"id":"n6","label":"PackageSection","module":"Adapters","kind":"Struct","fields":["name: String"],"methods":[]},{"id":"n7","label":"PythonProjectAnalyzer","module":"Adapters","kind":"Struct","fields":[],"methods":["+new() -> Self"]},{"id":"n8","label":"ProjectSection","module":"Adapters","kind":"Struct","fields":["name: Option","dependencies: Vec"],"methods":[]},{"id":"n9","label":"PoetrySection","module":"Adapters","kind":"Struct","fields":["name: Option","dependencies: HashMap"],"methods":[]},{"id":"n10","label":"ToolSection","module":"Adapters","kind":"Struct","fields":["poetry: PoetrySection"],"methods":[]},{"id":"n11","label":"PyprojectToml","module":"Adapters","kind":"Struct","fields":["project: Option","tool: ToolSection"],"methods":[]},{"id":"n12","label":"StdoutOutputWriter","module":"Adapters","kind":"Struct","fields":[],"methods":["+new() -> Self"]},{"id":"n13","label":"FileOutputWriter","module":"Adapters","kind":"Struct","fields":["output_path: OutputPath"],"methods":["+new(output_dir: PathBuf) -> Self","+single_file(path: PathBuf) -> Self"]},{"id":"n14","label":"OutputPath","module":"Adapters","kind":"Enum","fields":[],"methods":[]},{"id":"n15","label":"MermaidRenderer","module":"Adapters","kind":"Struct","fields":["level: DiagramLevel","show_weights: bool"],"methods":["+new() -> Self","+with_level(level: DiagramLevel) -> Self","+with_weights(show: bool) -> Self","-display_name(qualified: &str) -> &str","-format_element_name(element: &CodeElement) -> String","-format_visibility(visibility: Visibility) -> &'static str","-render_class_diagram(graph: &CodeGraph) -> String","-push_class_lines(lines: &mut Vec<String>, deferred: &mut Vec<String>, element: &CodeElement, indent: &str, in_namespace: bool)","-render_module_flowchart(graph: &CodeGraph) -> String","-render_project_flowchart(graph: &CodeGraph) -> String","-sanitize_id(name: &str) -> String"]},{"id":"n16","label":"LanguageExtractor","module":"Adapters","kind":"Trait","fields":[],"methods":[]},{"id":"n17","label":"TreeSitterAnalyzer","module":"Adapters","kind":"Struct","fields":["rust: RustExtractor","python: PythonExtractor"],"methods":["+new() -> Self","-extractor_for(language: Language) -> Option<&dyn LanguageExtractor>"]},{"id":"n18","label":"RustExtractor","module":"Adapters","kind":"Struct","fields":[],"methods":[]},{"id":"n19","label":"PythonExtractor","module":"Adapters","kind":"Struct","fields":[],"methods":[]},{"id":"n20","label":"WalkdirDiscovery","module":"Adapters","kind":"Struct","fields":[],"methods":["+new() -> Self","-detect_language(path: &Path) -> Option<Language>","-is_test_file(path: &Path, language: Language) -> bool","-is_excluded(path: &Path, root: &Path, excludes: &[String]) -> bool"]},{"id":"n21","label":"HtmlRenderer","module":"Adapters","kind":"Struct","fields":[],"methods":["+new() -> Self"]},{"id":"n22","label":"GraphData","module":"Adapters","kind":"Struct","fields":["nodes: Vec","edges: Vec"],"methods":[]},{"id":"n23","label":"NodeData","module":"Adapters","kind":"Struct","fields":["id: String","label: String","module: String","kind: String","fields: Vec","methods: Vec"],"methods":[]},{"id":"n24","label":"EdgeData","module":"Adapters","kind":"Struct","fields":["source: String","target: String","kind: String"],"methods":[]},{"id":"n25","label":"RawRules","module":"Adapters","kind":"Struct","fields":["allow: Vec","deny: Vec"],"methods":[]},{"id":"n26","label":"RawConfig","module":"Adapters","kind":"Struct","fields":["analysis: RawAnalysis","output: RawOutput","modules: HashMap","rules: RawRules"],"methods":[]},{"id":"n27","label":"RawAnalysis","module":"Adapters","kind":"Struct","fields":["exclude: Vec","level: Option"],"methods":[]},{"id":"n28","label":"RawOutput","module":"Adapters","kind":"Struct","fields":["format: Option","path: Option","split_by_module: bool"],"methods":[]},{"id":"n29","label":"TomlConfigLoader","module":"Adapters","kind":"Struct","fields":["raw: RawConfig"],"methods":["+from_path(path: &Path) -> Result<Self, DomainError>","-parse_level(level: &Option<String>) -> DiagramLevel"]},{"id":"n30","label":"Cli","module":"Presentation","kind":"Struct","fields":["command: Option","path: PathBuf","level: String","format: String","output: Option","config: Option","scope: Option","exclude: Vec","include_tests: bool","no_weights: bool","watch: bool","since: Option","split_by_module: bool","strict: bool","check: bool","verbose: u8"],"methods":[]},{"id":"n31","label":"Command","module":"Presentation","kind":"Enum","fields":[],"methods":[]},{"id":"n32","label":"AnalyzeCodebase","module":"Application","kind":"Struct","fields":["file_discovery: F","source_analyzer: S"],"methods":["+new(file_discovery: F, source_analyzer: S) -> Self","+execute(root: &Path, config: &AnalysisConfig) -> Result<AnalyzeCodebaseResult, DomainError>"]},{"id":"n33","label":"AnalyzeCodebaseResult","module":"Application","kind":"Struct","fields":["graph: CodeGraph","warnings: Vec"],"methods":["+graph() -> &CodeGraph","+warnings() -> &[AnalysisWarning]"]},{"id":"n34","label":"Relationship","module":"Domain","kind":"Struct","fields":["source: String","target: String","kind: RelationshipKind","source_file: Option"],"methods":["+new(source: &str, target: &str, kind: RelationshipKind) -> Result<Self, DomainError>","+with_source_file(file: FilePath) -> Self","+source() -> &str","+target() -> &str","+kind() -> RelationshipKind","+source_file() -> Option<&FilePath>"]},{"id":"n35","label":"CodeElement","module":"Domain","kind":"Struct","fields":["name: String","qualified_name: Option","kind: CodeElementKind","file_path: FilePath","line: usize","visibility: Visibility","module: Option","generics: Vec","attributes: Vec","fields: Vec","methods: Vec"],"methods":["+new(name: &str, kind: CodeElementKind, file_path: FilePath, line: usize) -> Result<Self, DomainError>","+with_visibility(visibility: Visibility) -> Self","+with_module(module: ModuleName) -> Self","+with_generics(generics: Vec<String>) -> Self","+with_attributes(attributes: Vec<String>) -> Self","+with_qualified_name(qn: String) -> Self","+name() -> &str","+qualified_name() -> &str","+kind() -> CodeElementKind","+file_path() -> &FilePath","+line() -> usize","+visibility() -> Visibility","+module() -> Option<&ModuleName>","+generics() -> &[String]","+attributes() -> &[String]","+with_fields(fields: Vec<String>) -> Self","+with_methods(methods: Vec<String>) -> Self","+fields() -> &[String]","+methods() -> &[String]"]},{"id":"n36","label":"DomainError","module":"Domain","kind":"Enum","fields":[],"methods":[]},{"id":"n37","label":"DiagramRenderer","module":"Domain","kind":"Trait","fields":[],"methods":[]},{"id":"n38","label":"SourceAnalyzer","module":"Domain","kind":"Trait","fields":[],"methods":[]},{"id":"n39","label":"ConfigLoader","module":"Domain","kind":"Trait","fields":[],"methods":[]},{"id":"n40","label":"FileDiscovery","module":"Domain","kind":"Trait","fields":[],"methods":[]},{"id":"n41","label":"ProjectAnalyzer","module":"Domain","kind":"Trait","fields":[],"methods":[]},{"id":"n42","label":"OutputWriter","module":"Domain","kind":"Trait","fields":[],"methods":[]},{"id":"n43","label":"AnalysisResult","module":"Domain","kind":"Struct","fields":["elements: Vec","relationships: Vec","warnings: Vec"],"methods":["+new(elements: Vec<CodeElement>, relationships: Vec<Relationship>, warnings: Vec<AnalysisWarning>) -> Self","+empty() -> Self","+elements() -> &[CodeElement]","+relationships() -> &[Relationship]","+warnings() -> &[AnalysisWarning]"]},{"id":"n44","label":"AnalysisConfig","module":"Domain","kind":"Struct","fields":["excludes: Vec","level: DiagramLevel","module_mappings: HashMap","scope: Option","include_tests: bool","changed_files: Option"],"methods":["+with_excludes(excludes: Vec<String>) -> Self","+with_level(level: DiagramLevel) -> Self","+with_module_mappings(mappings: HashMap<String, String>) -> Self","+excludes() -> &[String]","+level() -> DiagramLevel","+with_scope(scope: String) -> Self","+module_mappings() -> &HashMap<String, String>","+scope() -> Option<&str>","+with_include_tests(include: bool) -> Self","+include_tests() -> bool","+with_changed_files(files: HashSet<String>) -> Self","+changed_files() -> Option<&HashSet<String>>"]},{"id":"n45","label":"AnalysisWarning","module":"Domain","kind":"Struct","fields":["file_path: FilePath","line: usize","message: String"],"methods":["+new(file_path: FilePath, line: usize, message: &str) -> Result<Self, DomainError>","+file_path() -> &FilePath","+line() -> usize","+message() -> &str"]},{"id":"n46","label":"CodeElementKind","module":"Domain","kind":"Enum","fields":[],"methods":[]},{"id":"n47","label":"RelationshipKind","module":"Domain","kind":"Enum","fields":[],"methods":[]},{"id":"n48","label":"Visibility","module":"Domain","kind":"Enum","fields":[],"methods":[]},{"id":"n49","label":"DiagramLevel","module":"Domain","kind":"Enum","fields":[],"methods":[]},{"id":"n50","label":"OutputConfig","module":"Domain","kind":"Struct","fields":["split_by_module: bool","output_path: Option"],"methods":["+with_split_by_module(split: bool) -> Self","+with_output_path(path: String) -> Self","+split_by_module() -> bool","+output_path() -> Option<&str>"]},{"id":"n51","label":"RenderedFile","module":"Domain","kind":"Struct","fields":["name: String","content: String"],"methods":["+new(name: &str, content: &str) -> Result<Self, DomainError>","+name() -> &str","+content() -> &str"]},{"id":"n52","label":"RenderOutput","module":"Domain","kind":"Struct","fields":["files: Vec"],"methods":["+new(files: Vec<RenderedFile>) -> Self","+single(file: RenderedFile) -> Self","+files() -> &[RenderedFile]"]},{"id":"n53","label":"ModuleName","module":"Domain","kind":"Struct","fields":[],"methods":["+new(value: &str) -> Result<Self, DomainError>","+from_path(file_path: &str, root: &Path, module_mappings: &HashMap<String, String>) -> Option<Self>","+from_directory_group(member_path: &str) -> Option<Self>","+capitalize(s: &str) -> String","+as_str() -> &str"]},{"id":"n54","label":"Language","module":"Domain","kind":"Enum","fields":[],"methods":["+name() -> &'static str"]},{"id":"n55","label":"SourceFile","module":"Domain","kind":"Struct","fields":["path: FilePath","language: Language"],"methods":["+new(path: FilePath, language: Language) -> Self","+path() -> &FilePath","+language() -> Language"]},{"id":"n56","label":"FilePath","module":"Domain","kind":"Struct","fields":[],"methods":["+new(value: &str) -> Result<Self, DomainError>","+as_str() -> &str"]},{"id":"n57","label":"RuleKind","module":"Domain","kind":"Enum","fields":[],"methods":[]},{"id":"n58","label":"RuleViolation","module":"Domain","kind":"Struct","fields":["source_module: String","target_module: String","kind: RuleKind"],"methods":["+new(source_module: &str, target_module: &str, kind: RuleKind) -> Self","+source_module() -> &str","+target_module() -> &str","+kind() -> &RuleKind","+message() -> String"]},{"id":"n59","label":"BoundaryRule","module":"Domain","kind":"Struct","fields":["source: String","target: String"],"methods":["+parse(s: &str) -> Option<Self>","+source() -> &str","+target() -> &str","+matches(src_module: &str, tgt_module: &str) -> bool"]},{"id":"n60","label":"CodeGraph","module":"Domain","kind":"Struct","fields":["elements: Vec","relationships: Vec"],"methods":["+new() -> Self","+add_element(element: CodeElement)","+add_relationship(relationship: Relationship)","+elements() -> &[CodeElement]","+relationships() -> &[Relationship]","+modules() -> Vec<ModuleName>","+elements_by_module() -> (HashMap<String, Vec<&CodeElement>>, Vec<&CodeElement>)","+resolve_relationships() -> CodeGraph","+filter_external_imports(known_modules: &HashSet<String>) -> CodeGraph","+qualify() -> CodeGraph","+cross_module_deps_for(module: &ModuleName) -> Vec<(ModuleName, usize)>","+subgraph_by_module(module: &ModuleName) -> CodeGraph"]}],"edges":[{"source":"n0","target":"n37","kind":"Inheritance"},{"source":"n1","target":"n49","kind":"Composition"},{"source":"n1","target":"n37","kind":"Inheritance"},{"source":"n2","target":"n41","kind":"Inheritance"},{"source":"n10","target":"n9","kind":"Composition"},{"source":"n11","target":"n10","kind":"Composition"},{"source":"n7","target":"n41","kind":"Inheritance"},{"source":"n12","target":"n42","kind":"Inheritance"},{"source":"n13","target":"n14","kind":"Composition"},{"source":"n13","target":"n42","kind":"Inheritance"},{"source":"n15","target":"n49","kind":"Composition"},{"source":"n15","target":"n37","kind":"Inheritance"},{"source":"n17","target":"n18","kind":"Composition"},{"source":"n17","target":"n19","kind":"Composition"},{"source":"n17","target":"n38","kind":"Inheritance"},{"source":"n18","target":"n16","kind":"Inheritance"},{"source":"n19","target":"n16","kind":"Inheritance"},{"source":"n20","target":"n40","kind":"Inheritance"},{"source":"n21","target":"n37","kind":"Inheritance"},{"source":"n26","target":"n27","kind":"Composition"},{"source":"n26","target":"n28","kind":"Composition"},{"source":"n26","target":"n25","kind":"Composition"},{"source":"n29","target":"n26","kind":"Composition"},{"source":"n29","target":"n39","kind":"Inheritance"},{"source":"n33","target":"n60","kind":"Composition"},{"source":"n34","target":"n47","kind":"Composition"},{"source":"n35","target":"n46","kind":"Composition"},{"source":"n35","target":"n56","kind":"Composition"},{"source":"n35","target":"n48","kind":"Composition"},{"source":"n44","target":"n49","kind":"Composition"},{"source":"n45","target":"n56","kind":"Composition"},{"source":"n55","target":"n56","kind":"Composition"},{"source":"n55","target":"n54","kind":"Composition"},{"source":"n58","target":"n57","kind":"Composition"}]};
// Inline minimal Cytoscape-compatible renderer using Canvas API
(function() {
const canvas = document.createElement('canvas');
const container = document.getElementById('cy');
canvas.style.width = '100%';
canvas.style.height = '100%';
container.appendChild(canvas);
const detail = document.getElementById('detail');
function resize() {
canvas.width = container.clientWidth;
canvas.height = container.clientHeight;
draw();
}
// Group nodes by module
const modules = {};
GRAPH.nodes.forEach(n => {
const m = n.module || '(ungrouped)';
if (!modules[m]) modules[m] = [];
modules[m].push(n);
});
// Layout: arrange modules in a grid, nodes within each module in a column
const positions = {};
const modNames = Object.keys(modules);
const cols = Math.ceil(Math.sqrt(modNames.length));
const cellW = 220, cellH = 200, pad = 60;
modNames.forEach((mod, mi) => {
const col = mi % cols, row = Math.floor(mi / cols);
const bx = pad + col * (cellW + pad);
const by = pad + row * (cellH + pad);
modules[mod].forEach((n, ni) => {
positions[n.id] = {
x: bx + 20 + (ni % 2) * 90,
y: by + 30 + Math.floor(ni / 2) * 50
};
});
});
let selected = null;
const nodeRadius = 18;
function draw() {
const ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw module backgrounds
modNames.forEach((mod, mi) => {
const col = mi % cols, row = Math.floor(mi / cols);
const bx = pad / 2 + col * (cellW + pad);
const by = pad / 2 + row * (cellH + pad);
ctx.fillStyle = 'rgba(15,52,96,0.4)';
ctx.fillRect(bx, by, cellW + pad / 2, cellH + pad / 2);
ctx.fillStyle = '#4fc3f7';
ctx.font = '11px sans-serif';
ctx.fillText(mod, bx + 6, by + 14);
});
// Draw edges
GRAPH.edges.forEach(e => {
const sp = positions[e.source], tp = positions[e.target];
if (!sp || !tp) return;
ctx.strokeStyle = e.kind === 'Inheritance' ? '#e94560' : '#aaa';
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(sp.x, sp.y);
ctx.lineTo(tp.x, tp.y);
ctx.stroke();
});
// Draw nodes
GRAPH.nodes.forEach(n => {
const p = positions[n.id];
if (!p) return;
ctx.fillStyle = selected && selected.id === n.id ? '#e94560' : '#0f3460';
ctx.beginPath();
ctx.arc(p.x, p.y, nodeRadius, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = '#eee';
ctx.font = '10px sans-serif';
ctx.textAlign = 'center';
ctx.fillText(n.label.substring(0, 12), p.x, p.y + 4);
});
}
canvas.addEventListener('click', e => {
const rect = canvas.getBoundingClientRect();
const mx = e.clientX - rect.left, my = e.clientY - rect.top;
selected = null;
for (const n of GRAPH.nodes) {
const p = positions[n.id];
if (!p) continue;
const dx = mx - p.x, dy = my - p.y;
if (dx * dx + dy * dy < nodeRadius * nodeRadius) {
selected = n;
break;
}
}
if (selected) {
detail.innerHTML = `<strong>${selected.label}</strong><br><em>${selected.module}</em>` +
(selected.fields.length ? '<br><b>Fields:</b><br>' + selected.fields.map(f => `<div class="member">${f}</div>`).join('') : '') +
(selected.methods.length ? '<br><b>Methods:</b><br>' + selected.methods.map(m => `<div class="member">${m}</div>`).join('') : '');
} else {
detail.innerHTML = '<p>Click a node to see details.</p>';
}
draw();
});
window.addEventListener('resize', resize);
resize();
})();
</script>
</body>
</html>