@girardelli/architect 2.1.0 → 4.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 +105 -116
- package/__test_agent_output__/INDEX.md +1 -0
- package/__test_agent_output__/agents/AGENT-ORCHESTRATOR.md +1 -0
- package/__test_agent_output__/agents/DATABASE-ENGINEER.md +174 -0
- package/__test_agent_output__/agents/QA-TEST-ENGINEER.md +138 -0
- package/__test_agent_output__/agents/SECURITY-AUDITOR.md +106 -0
- package/__test_agent_output__/agents/TECH-DEBT-CONTROLLER.md +104 -0
- package/__test_agent_output__/agents/TYPESCRIPT-BACKEND-DEVELOPER.md +135 -0
- package/__test_agent_output__/guards/CODE-REVIEW-CHECKLIST.md +95 -0
- package/__test_agent_output__/guards/PREFLIGHT.md +200 -0
- package/__test_agent_output__/guards/QUALITY-GATES.md +1 -0
- package/__test_agent_output__/rules/00-general.md +229 -0
- package/__test_agent_output__/rules/01-architecture.md +191 -0
- package/__test_agent_output__/rules/02-security.md +402 -0
- package/__test_agent_output__/rules/03-nestjs.md +124 -0
- package/__test_agent_output__/templates/ADR.md +95 -0
- package/__test_agent_output__/templates/BDD.md +58 -0
- package/__test_agent_output__/templates/C4.md +68 -0
- package/__test_agent_output__/templates/TDD.md +86 -0
- package/__test_agent_output__/templates/THREAT-MODEL.md +82 -0
- package/__test_agent_output__/workflows/fix-bug.md +228 -0
- package/__test_agent_output__/workflows/new-feature.md +311 -0
- package/__test_agent_output__/workflows/review.md +95 -0
- package/__test_context_7RvUrO/src/modules/empty/empty.ts +0 -0
- package/__test_context_Rf5fNJ/src/modules/mixed/mixed.ts +5 -0
- package/__test_context_WRCnYH/src/modules/test/test.ts +10 -0
- package/__test_context_YsnVS3/src/modules/test/test.ts +10 -0
- package/__test_context_w7XZeH/src/modules/mixed/mixed.ts +5 -0
- package/__test_context_y5noh6/src/modules/empty/empty.ts +0 -0
- package/__test_framework__24OjAu/package.json +1 -0
- package/__test_framework__3ZDZsx/pyproject.toml +8 -0
- package/__test_framework__4T54Jn/package.json +1 -0
- package/__test_framework__4tlXu9/pyproject.toml +8 -0
- package/__test_framework__6boWqQ/Pipfile +6 -0
- package/__test_framework__6gygMU/pom.xml +10 -0
- package/__test_framework__6kxj0N/go.mod +8 -0
- package/__test_framework__7CEoXw/pom.xml +10 -0
- package/__test_framework__85DDz0/Pipfile +6 -0
- package/__test_framework__9WrRIr/pom.xml +7 -0
- package/__test_framework__ANqGKl/Gemfile +5 -0
- package/__test_framework__BCXTEM/go.mod +3 -0
- package/__test_framework__BHiPNq/setup.py +2 -0
- package/__test_framework__BqkiKv/package.json +1 -0
- package/__test_framework__C5yd8X/Pipfile.lock +1 -0
- package/__test_framework__C5yd8X/requirements.txt +1 -0
- package/__test_framework__C87d3a/manage.py +1 -0
- package/__test_framework__C87d3a/requirements.txt +2 -0
- package/__test_framework__DXNwc5/build.gradle +7 -0
- package/__test_framework__GhHSt3/build.gradle.kts +4 -0
- package/__test_framework__GzklJP/Cargo.toml +7 -0
- package/__test_framework__H4hd13/go.mod +8 -0
- package/__test_framework__HKjOXO/composer.json +1 -0
- package/__test_framework__HaDN45/Gemfile +3 -0
- package/__test_framework__IBO7YG/pyproject.toml +9 -0
- package/__test_framework__JwSOyF/pyproject.toml +6 -0
- package/__test_framework__K6HrCr/build.gradle +2 -0
- package/__test_framework__KzRPlh/pubspec.yaml +9 -0
- package/__test_framework__L6uIym/pyproject.toml +6 -0
- package/__test_framework__LOdoGK/requirements.txt +4 -0
- package/__test_framework__LgHzss/package.json +1 -0
- package/__test_framework__M76M6q/Gemfile +5 -0
- package/__test_framework__Mr9vWW/composer.json +1 -0
- package/__test_framework__N03Gnv/package.json +1 -0
- package/__test_framework__Num4UE/requirements +1 -0
- package/__test_framework__OAGw3Y/build.gradle +7 -0
- package/__test_framework__OQc8yG/pubspec.yaml +9 -0
- package/__test_framework__OwKZcd/requirements.txt +3 -0
- package/__test_framework__P0gFv7/requirements +1 -0
- package/__test_framework__PN55Rq/package.json +1 -0
- package/__test_framework__PQiqX8/pubspec.yaml +3 -0
- package/__test_framework__RBHsg7/composer.json +1 -0
- package/__test_framework__RHxif4/Cargo.toml +7 -0
- package/__test_framework__T0v0p1/Cargo.toml +4 -0
- package/__test_framework__Tu0clt/Pipfile.lock +1 -0
- package/__test_framework__Tu0clt/requirements.txt +1 -0
- package/__test_framework__TwDj9P/Cargo.toml +4 -0
- package/__test_framework__VQJNC4/pom.xml +7 -0
- package/__test_framework__W6sm05/package.json +1 -0
- package/__test_framework__W7vBLy/pyproject.toml +4 -0
- package/__test_framework__WNJOWT/setup.py +2 -0
- package/__test_framework__WSJs7U/package.json +1 -0
- package/__test_framework__YQ5VpA/build.gradle.kts +4 -0
- package/__test_framework__ZNEUEs/package.json +1 -0
- package/__test_framework__Znt922/pom.xml +7 -0
- package/__test_framework__azyg0h/pom.xml +7 -0
- package/__test_framework__c6otLr/package.json +1 -0
- package/__test_framework__cl9S9G/build.gradle +2 -0
- package/__test_framework__eilvV4/composer.json +1 -0
- package/__test_framework__gQZxXO/manage.py +1 -0
- package/__test_framework__gQZxXO/requirements.txt +2 -0
- package/__test_framework__ghvl26/poetry.lock +1 -0
- package/__test_framework__ghvl26/pyproject.toml +2 -0
- package/__test_framework__hR7b9U/Makefile +11 -0
- package/__test_framework__iESVsi/composer.json +1 -0
- package/__test_framework__jm6TJy/package.json +1 -0
- package/__test_framework__kBUpjs/pyproject.toml +9 -0
- package/__test_framework__kqoZrw/requirements.txt +4 -0
- package/__test_framework__lWkoyO/pyproject.toml +4 -0
- package/__test_framework__mTKnUO/package.json +1 -0
- package/__test_framework__nCeZwe/Makefile +11 -0
- package/__test_framework__oljsU0/package.json +1 -0
- package/__test_framework__osRG4q/go.mod +3 -0
- package/__test_framework__pCHH4F/package.json +1 -0
- package/__test_framework__pExx6E/Gemfile +3 -0
- package/__test_framework__pyBoGd/pyproject.toml +5 -0
- package/__test_framework__qw16VQ/package.json +1 -0
- package/__test_framework__rRayrG/package.json +1 -0
- package/__test_framework__s82zO5/package.json +1 -0
- package/__test_framework__tp8MFK/pyproject.toml +5 -0
- package/__test_framework__w44k4w/composer.json +1 -0
- package/__test_framework__yefPZY/poetry.lock +1 -0
- package/__test_framework__yefPZY/pyproject.toml +2 -0
- package/__test_framework__zCiyDT/requirements.txt +3 -0
- package/__test_framework__zGZN3j/pubspec.yaml +3 -0
- package/__test_framework__zXpnxL/package.json +1 -0
- package/architect-run.sh +431 -0
- package/assets/banner-v3.html +561 -0
- package/dist/agent-generator/context-enricher.d.ts +58 -0
- package/dist/agent-generator/context-enricher.d.ts.map +1 -0
- package/dist/agent-generator/context-enricher.js +581 -0
- package/dist/agent-generator/context-enricher.js.map +1 -0
- package/dist/agent-generator/domain-inferrer.d.ts +52 -0
- package/dist/agent-generator/domain-inferrer.d.ts.map +1 -0
- package/dist/agent-generator/domain-inferrer.js +575 -0
- package/dist/agent-generator/domain-inferrer.js.map +1 -0
- package/dist/agent-generator/framework-detector.d.ts +40 -0
- package/dist/agent-generator/framework-detector.d.ts.map +1 -0
- package/dist/agent-generator/framework-detector.js +611 -0
- package/dist/agent-generator/framework-detector.js.map +1 -0
- package/dist/agent-generator/index.d.ts +33 -0
- package/dist/agent-generator/index.d.ts.map +1 -0
- package/dist/agent-generator/index.js +477 -0
- package/dist/agent-generator/index.js.map +1 -0
- package/dist/agent-generator/stack-detector.d.ts +12 -0
- package/dist/agent-generator/stack-detector.d.ts.map +1 -0
- package/dist/agent-generator/stack-detector.js +128 -0
- package/dist/agent-generator/stack-detector.js.map +1 -0
- package/dist/agent-generator/templates/core/agents.d.ts +17 -0
- package/dist/agent-generator/templates/core/agents.d.ts.map +1 -0
- package/dist/agent-generator/templates/core/agents.js +1252 -0
- package/dist/agent-generator/templates/core/agents.js.map +1 -0
- package/dist/agent-generator/templates/core/architecture-rules.d.ts +7 -0
- package/dist/agent-generator/templates/core/architecture-rules.d.ts.map +1 -0
- package/dist/agent-generator/templates/core/architecture-rules.js +274 -0
- package/dist/agent-generator/templates/core/architecture-rules.js.map +1 -0
- package/dist/agent-generator/templates/core/general-rules.d.ts +8 -0
- package/dist/agent-generator/templates/core/general-rules.d.ts.map +1 -0
- package/dist/agent-generator/templates/core/general-rules.js +301 -0
- package/dist/agent-generator/templates/core/general-rules.js.map +1 -0
- package/dist/agent-generator/templates/core/index-md.d.ts +7 -0
- package/dist/agent-generator/templates/core/index-md.d.ts.map +1 -0
- package/dist/agent-generator/templates/core/index-md.js +246 -0
- package/dist/agent-generator/templates/core/index-md.js.map +1 -0
- package/dist/agent-generator/templates/core/orchestrator.d.ts +8 -0
- package/dist/agent-generator/templates/core/orchestrator.d.ts.map +1 -0
- package/dist/agent-generator/templates/core/orchestrator.js +422 -0
- package/dist/agent-generator/templates/core/orchestrator.js.map +1 -0
- package/dist/agent-generator/templates/core/preflight.d.ts +8 -0
- package/dist/agent-generator/templates/core/preflight.d.ts.map +1 -0
- package/dist/agent-generator/templates/core/preflight.js +213 -0
- package/dist/agent-generator/templates/core/preflight.js.map +1 -0
- package/dist/agent-generator/templates/core/quality-gates.d.ts +11 -0
- package/dist/agent-generator/templates/core/quality-gates.d.ts.map +1 -0
- package/dist/agent-generator/templates/core/quality-gates.js +254 -0
- package/dist/agent-generator/templates/core/quality-gates.js.map +1 -0
- package/dist/agent-generator/templates/core/security-rules.d.ts +7 -0
- package/dist/agent-generator/templates/core/security-rules.d.ts.map +1 -0
- package/dist/agent-generator/templates/core/security-rules.js +528 -0
- package/dist/agent-generator/templates/core/security-rules.js.map +1 -0
- package/dist/agent-generator/templates/core/skills-generator.d.ts +6 -0
- package/dist/agent-generator/templates/core/skills-generator.d.ts.map +1 -0
- package/dist/agent-generator/templates/core/skills-generator.js +207 -0
- package/dist/agent-generator/templates/core/skills-generator.js.map +1 -0
- package/dist/agent-generator/templates/core/workflow-fix-bug.d.ts +7 -0
- package/dist/agent-generator/templates/core/workflow-fix-bug.d.ts.map +1 -0
- package/dist/agent-generator/templates/core/workflow-fix-bug.js +237 -0
- package/dist/agent-generator/templates/core/workflow-fix-bug.js.map +1 -0
- package/dist/agent-generator/templates/core/workflow-new-feature.d.ts +8 -0
- package/dist/agent-generator/templates/core/workflow-new-feature.d.ts.map +1 -0
- package/dist/agent-generator/templates/core/workflow-new-feature.js +321 -0
- package/dist/agent-generator/templates/core/workflow-new-feature.js.map +1 -0
- package/dist/agent-generator/templates/core/workflow-review.d.ts +7 -0
- package/dist/agent-generator/templates/core/workflow-review.d.ts.map +1 -0
- package/dist/agent-generator/templates/core/workflow-review.js +104 -0
- package/dist/agent-generator/templates/core/workflow-review.js.map +1 -0
- package/dist/agent-generator/templates/domain/index.d.ts +22 -0
- package/dist/agent-generator/templates/domain/index.d.ts.map +1 -0
- package/dist/agent-generator/templates/domain/index.js +1176 -0
- package/dist/agent-generator/templates/domain/index.js.map +1 -0
- package/dist/agent-generator/templates/stack/index.d.ts +8 -0
- package/dist/agent-generator/templates/stack/index.d.ts.map +1 -0
- package/dist/agent-generator/templates/stack/index.js +695 -0
- package/dist/agent-generator/templates/stack/index.js.map +1 -0
- package/dist/agent-generator/templates/template-helpers.d.ts +75 -0
- package/dist/agent-generator/templates/template-helpers.d.ts.map +1 -0
- package/dist/agent-generator/templates/template-helpers.js +726 -0
- package/dist/agent-generator/templates/template-helpers.js.map +1 -0
- package/dist/agent-generator/types.d.ts +196 -0
- package/dist/agent-generator/types.d.ts.map +1 -0
- package/dist/agent-generator/types.js +27 -0
- package/dist/agent-generator/types.js.map +1 -0
- package/dist/analyzer.d.ts +5 -0
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +35 -4
- package/dist/analyzer.js.map +1 -1
- package/dist/analyzers/forecast.d.ts +85 -0
- package/dist/analyzers/forecast.d.ts.map +1 -0
- package/dist/analyzers/forecast.js +337 -0
- package/dist/analyzers/forecast.js.map +1 -0
- package/dist/analyzers/git-cache.d.ts +7 -0
- package/dist/analyzers/git-cache.d.ts.map +1 -0
- package/dist/analyzers/git-cache.js +41 -0
- package/dist/analyzers/git-cache.js.map +1 -0
- package/dist/analyzers/git-history.d.ts +113 -0
- package/dist/analyzers/git-history.d.ts.map +1 -0
- package/dist/analyzers/git-history.js +333 -0
- package/dist/analyzers/git-history.js.map +1 -0
- package/dist/analyzers/index.d.ts +10 -0
- package/dist/analyzers/index.d.ts.map +1 -0
- package/dist/analyzers/index.js +7 -0
- package/dist/analyzers/index.js.map +1 -0
- package/dist/analyzers/temporal-scorer.d.ts +72 -0
- package/dist/analyzers/temporal-scorer.d.ts.map +1 -0
- package/dist/analyzers/temporal-scorer.js +140 -0
- package/dist/analyzers/temporal-scorer.js.map +1 -0
- package/dist/cli.d.ts +2 -3
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +287 -82
- package/dist/cli.js.map +1 -1
- package/dist/html-reporter.d.ts +3 -1
- package/dist/html-reporter.d.ts.map +1 -1
- package/dist/html-reporter.js +594 -100
- package/dist/html-reporter.js.map +1 -1
- package/dist/index.d.ts +16 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +63 -4
- package/dist/index.js.map +1 -1
- package/dist/project-summarizer.d.ts +18 -0
- package/dist/project-summarizer.d.ts.map +1 -0
- package/dist/project-summarizer.js +306 -0
- package/dist/project-summarizer.js.map +1 -0
- package/dist/refactor-reporter.js +1 -1
- package/dist/types.d.ts +13 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +12 -3
- package/src/agent-generator/context-enricher.ts +643 -0
- package/src/agent-generator/domain-inferrer.ts +625 -0
- package/src/agent-generator/framework-detector.ts +669 -0
- package/src/agent-generator/index.ts +555 -0
- package/src/agent-generator/stack-detector.ts +103 -0
- package/src/agent-generator/templates/core/agents.ts +1293 -0
- package/src/agent-generator/templates/core/architecture-rules.ts +287 -0
- package/src/agent-generator/templates/core/general-rules.ts +306 -0
- package/src/agent-generator/templates/core/index-md.ts +260 -0
- package/src/agent-generator/templates/core/orchestrator.ts +459 -0
- package/src/agent-generator/templates/core/preflight.ts +215 -0
- package/src/agent-generator/templates/core/quality-gates.ts +256 -0
- package/src/agent-generator/templates/core/security-rules.ts +543 -0
- package/src/agent-generator/templates/core/skills-generator.ts +236 -0
- package/src/agent-generator/templates/core/workflow-fix-bug.ts +239 -0
- package/src/agent-generator/templates/core/workflow-new-feature.ts +323 -0
- package/src/agent-generator/templates/core/workflow-review.ts +106 -0
- package/src/agent-generator/templates/domain/index.ts +1201 -0
- package/src/agent-generator/templates/stack/index.ts +705 -0
- package/src/agent-generator/templates/template-helpers.ts +776 -0
- package/src/agent-generator/types.ts +232 -0
- package/src/analyzer.ts +38 -4
- package/src/analyzers/forecast.ts +496 -0
- package/src/analyzers/git-cache.ts +52 -0
- package/src/analyzers/git-history.ts +488 -0
- package/src/analyzers/index.ts +33 -0
- package/src/analyzers/temporal-scorer.ts +227 -0
- package/src/cli.ts +336 -83
- package/src/html-reporter.ts +616 -108
- package/src/index.ts +92 -9
- package/src/project-summarizer.ts +347 -0
- package/src/refactor-reporter.ts +1 -1
- package/src/types.ts +10 -0
- package/tests/agent-generator.test.ts +411 -0
- package/tests/analyzers-integration.test.ts +174 -0
- package/tests/architect-adapter-enrichment.test.ts +9 -0
- package/tests/context-enricher.test.ts +971 -0
- package/tests/forecast.test.ts +509 -0
- package/tests/framework-detector.test.ts +1172 -0
- package/tests/git-history.test.ts +254 -0
- package/tests/scanner.test.ts +7 -8
- package/tests/scorer.test.ts +588 -0
- package/tests/stack-detector.test.ts +241 -0
- package/tests/template-generation.test.ts +706 -0
- package/tests/template-helpers.test.ts +1152 -0
- package/tests/temporal-scorer.test.ts +307 -0
- package/dist/agent-generator.d.ts +0 -95
- package/dist/agent-generator.d.ts.map +0 -1
- package/dist/agent-generator.js +0 -1295
- package/dist/agent-generator.js.map +0 -1
- package/src/agent-generator.ts +0 -1401
package/dist/html-reporter.js
CHANGED
|
@@ -17,17 +17,65 @@ ${this.getStyles()}
|
|
|
17
17
|
</head>
|
|
18
18
|
<body>
|
|
19
19
|
${this.renderHeader(report)}
|
|
20
|
-
<div class="
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
20
|
+
<div class="report-layout">
|
|
21
|
+
<nav class="sidebar" id="reportSidebar">
|
|
22
|
+
<div class="sidebar-title">Navigation</div>
|
|
23
|
+
<a href="#score" class="sidebar-link active" data-section="score">📊 Score</a>
|
|
24
|
+
${report.projectSummary ? `<a href="#overview" class="sidebar-link" data-section="overview">📋 Overview</a>` : ''}
|
|
25
|
+
<a href="#layers" class="sidebar-link" data-section="layers">📐 Layers & Graph</a>
|
|
26
|
+
<a href="#anti-patterns" class="sidebar-link" data-section="anti-patterns">⚠️ Anti-Patterns (${report.antiPatterns.length})</a>
|
|
27
|
+
<a href="#suggestions" class="sidebar-link" data-section="suggestions">💡 Suggestions (${report.suggestions.length})</a>
|
|
28
|
+
${plan ? `<a href="#refactoring" class="sidebar-link" data-section="refactoring">🔧 Refactoring (${plan.steps.length})</a>` : ''}
|
|
29
|
+
${agentSuggestion ? `<a href="#agents" class="sidebar-link" data-section="agents">🤖 Agents</a>` : ''}
|
|
30
|
+
</nav>
|
|
31
|
+
<button class="sidebar-toggle" onclick="document.getElementById('reportSidebar').classList.toggle('sidebar-open')">☰</button>
|
|
32
|
+
|
|
33
|
+
<div class="container">
|
|
34
|
+
<div id="score">
|
|
35
|
+
${this.renderScoreHero(report)}
|
|
36
|
+
${this.renderRadarChart(report)}
|
|
37
|
+
${this.renderStats(report)}
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
${this.renderProjectOverview(report)}
|
|
41
|
+
|
|
42
|
+
<details class="section-accordion" id="layers" open>
|
|
43
|
+
<summary class="section-accordion-header">📐 Layer Analysis & Dependencies</summary>
|
|
44
|
+
<div class="section-accordion-body">
|
|
45
|
+
${this.renderLayers(report)}
|
|
46
|
+
${this.renderDependencyGraph(report)}
|
|
47
|
+
</div>
|
|
48
|
+
</details>
|
|
49
|
+
|
|
50
|
+
<details class="section-accordion" id="anti-patterns" open>
|
|
51
|
+
<summary class="section-accordion-header">⚠️ Anti-Patterns (${report.antiPatterns.length})</summary>
|
|
52
|
+
<div class="section-accordion-body">
|
|
53
|
+
${this.renderAntiPatternBubbles(report, grouped)}
|
|
54
|
+
${this.renderAntiPatterns(report, grouped)}
|
|
55
|
+
</div>
|
|
56
|
+
</details>
|
|
57
|
+
|
|
58
|
+
<details class="section-accordion" id="suggestions">
|
|
59
|
+
<summary class="section-accordion-header">💡 Suggestions (${report.suggestions.length})</summary>
|
|
60
|
+
<div class="section-accordion-body">
|
|
61
|
+
${this.renderSuggestions(sugGrouped)}
|
|
62
|
+
</div>
|
|
63
|
+
</details>
|
|
64
|
+
|
|
65
|
+
${plan ? `<details class="section-accordion" id="refactoring" open>
|
|
66
|
+
<summary class="section-accordion-header">🔧 Refactoring Plan (${plan.steps.length} steps, ${plan.totalOperations} operations)</summary>
|
|
67
|
+
<div class="section-accordion-body">
|
|
68
|
+
${this.renderRefactoringPlan(plan)}
|
|
69
|
+
</div>
|
|
70
|
+
</details>` : ''}
|
|
71
|
+
|
|
72
|
+
${agentSuggestion ? `<details class="section-accordion" id="agents" open>
|
|
73
|
+
<summary class="section-accordion-header">🤖 Agent System</summary>
|
|
74
|
+
<div class="section-accordion-body">
|
|
75
|
+
${this.renderAgentSuggestions(agentSuggestion)}
|
|
76
|
+
</div>
|
|
77
|
+
</details>` : ''}
|
|
78
|
+
</div>
|
|
31
79
|
</div>
|
|
32
80
|
${this.renderFooter()}
|
|
33
81
|
${this.getScripts(report)}
|
|
@@ -110,6 +158,69 @@ ${this.getScripts(report)}
|
|
|
110
158
|
</div>
|
|
111
159
|
</div>`;
|
|
112
160
|
}
|
|
161
|
+
renderProjectOverview(report) {
|
|
162
|
+
const summary = report.projectSummary;
|
|
163
|
+
if (!summary)
|
|
164
|
+
return '';
|
|
165
|
+
const modulesHtml = summary.modules.length > 0
|
|
166
|
+
? summary.modules.map(m => `
|
|
167
|
+
<div class="overview-module">
|
|
168
|
+
<div class="overview-module-name">${this.esc(m.name)}</div>
|
|
169
|
+
<div class="overview-module-desc">${this.esc(m.description)}</div>
|
|
170
|
+
<div class="overview-module-files">${m.files} file${m.files > 1 ? 's' : ''}</div>
|
|
171
|
+
</div>`).join('')
|
|
172
|
+
: '<div class="overview-empty">Nenhum módulo detectado</div>';
|
|
173
|
+
const techHtml = summary.techStack
|
|
174
|
+
.map(t => `<span class="overview-tag tech-tag">${this.esc(t)}</span>`)
|
|
175
|
+
.join('');
|
|
176
|
+
const keywordsHtml = summary.keywords
|
|
177
|
+
.map(k => `<span class="overview-tag keyword-tag">${this.esc(k)}</span>`)
|
|
178
|
+
.join('');
|
|
179
|
+
const entryHtml = summary.entryPoints.length > 0
|
|
180
|
+
? summary.entryPoints.map(e => `<code class="overview-entry">${this.esc(e)}</code>`).join(' ')
|
|
181
|
+
: '<span class="overview-empty">—</span>';
|
|
182
|
+
return `
|
|
183
|
+
<details class="section-accordion" id="overview" open>
|
|
184
|
+
<summary class="section-accordion-header">📋 Project Overview</summary>
|
|
185
|
+
<div class="section-accordion-body">
|
|
186
|
+
<div class="overview-grid">
|
|
187
|
+
<div class="overview-card overview-main">
|
|
188
|
+
<div class="overview-label">O que é</div>
|
|
189
|
+
<div class="overview-description">${this.esc(summary.description)}</div>
|
|
190
|
+
<div class="overview-purpose-row">
|
|
191
|
+
<span class="overview-purpose-label">Tipo:</span>
|
|
192
|
+
<span class="overview-purpose-value">${this.esc(summary.purpose)}</span>
|
|
193
|
+
</div>
|
|
194
|
+
</div>
|
|
195
|
+
<div class="overview-card">
|
|
196
|
+
<div class="overview-label">Tech Stack</div>
|
|
197
|
+
<div class="overview-tags">${techHtml || '<span class="overview-empty">—</span>'}</div>
|
|
198
|
+
</div>
|
|
199
|
+
<div class="overview-card">
|
|
200
|
+
<div class="overview-label">Keywords</div>
|
|
201
|
+
<div class="overview-tags">${keywordsHtml || '<span class="overview-empty">—</span>'}</div>
|
|
202
|
+
</div>
|
|
203
|
+
<div class="overview-card">
|
|
204
|
+
<div class="overview-label">Entry Points</div>
|
|
205
|
+
<div class="overview-entries">${entryHtml}</div>
|
|
206
|
+
</div>
|
|
207
|
+
</div>
|
|
208
|
+
<div class="overview-modules-section">
|
|
209
|
+
<div class="overview-label">Módulos Detectados (${summary.modules.length})</div>
|
|
210
|
+
<div class="overview-modules-grid">
|
|
211
|
+
${modulesHtml}
|
|
212
|
+
</div>
|
|
213
|
+
</div>
|
|
214
|
+
</div>
|
|
215
|
+
</details>`;
|
|
216
|
+
}
|
|
217
|
+
esc(text) {
|
|
218
|
+
return text
|
|
219
|
+
.replace(/&/g, '&')
|
|
220
|
+
.replace(/</g, '<')
|
|
221
|
+
.replace(/>/g, '>')
|
|
222
|
+
.replace(/"/g, '"');
|
|
223
|
+
}
|
|
113
224
|
renderScoreHero(report) {
|
|
114
225
|
const overall = report.score.overall;
|
|
115
226
|
const circumference = 2 * Math.PI * 85;
|
|
@@ -208,44 +319,112 @@ ${this.getScripts(report)}
|
|
|
208
319
|
renderDependencyGraph(report) {
|
|
209
320
|
if (report.dependencyGraph.edges.length === 0)
|
|
210
321
|
return '';
|
|
211
|
-
// Build
|
|
322
|
+
// Build real file set — only files that appear as SOURCE in edges (these are real scanned files)
|
|
323
|
+
const realFiles = new Set(report.dependencyGraph.edges.map(e => e.from));
|
|
324
|
+
// Count connections only for real files
|
|
212
325
|
const connectionCount = {};
|
|
213
326
|
for (const edge of report.dependencyGraph.edges) {
|
|
214
|
-
|
|
215
|
-
|
|
327
|
+
if (realFiles.has(edge.from)) {
|
|
328
|
+
connectionCount[edge.from] = (connectionCount[edge.from] || 0) + 1;
|
|
329
|
+
}
|
|
330
|
+
if (realFiles.has(edge.to)) {
|
|
331
|
+
connectionCount[edge.to] = (connectionCount[edge.to] || 0) + 1;
|
|
332
|
+
}
|
|
216
333
|
}
|
|
334
|
+
// Build layer map from report layers
|
|
217
335
|
const layerMap = {};
|
|
218
336
|
for (const layer of report.layers) {
|
|
219
337
|
for (const file of layer.files) {
|
|
220
338
|
layerMap[file] = layer.name;
|
|
221
339
|
}
|
|
222
340
|
}
|
|
223
|
-
|
|
341
|
+
// Create nodes only from real files
|
|
342
|
+
const allNodes = [...realFiles].map(n => ({
|
|
224
343
|
id: n,
|
|
225
344
|
name: n.split('/').pop() || n,
|
|
226
345
|
connections: connectionCount[n] || 0,
|
|
227
346
|
layer: layerMap[n] || 'Other',
|
|
228
347
|
}));
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
348
|
+
// ── Fallback: color by module/directory when layer detection is weak ──
|
|
349
|
+
const otherCount = allNodes.filter(n => n.layer === 'Other').length;
|
|
350
|
+
const useModuleColoring = allNodes.length > 0 && (otherCount / allNodes.length) > 0.7;
|
|
351
|
+
// Palette for module-based coloring (10 distinct, vibrant colors)
|
|
352
|
+
const modulePalette = [
|
|
353
|
+
'#3b82f6', '#ec4899', '#10b981', '#f59e0b', '#8b5cf6',
|
|
354
|
+
'#06b6d4', '#ef4444', '#84cc16', '#f97316', '#6366f1',
|
|
355
|
+
];
|
|
356
|
+
let moduleColorMap = {};
|
|
357
|
+
if (useModuleColoring) {
|
|
358
|
+
// Extract module (first meaningful directory) from each node path
|
|
359
|
+
const getModule = (filePath) => {
|
|
360
|
+
const parts = filePath.split('/');
|
|
361
|
+
if (parts.length < 2)
|
|
362
|
+
return 'root';
|
|
363
|
+
const first = parts[0];
|
|
364
|
+
// If first dir is common source dir, use second level
|
|
365
|
+
if (['src', 'lib', 'app', 'packages', 'modules', 'features', 'apps'].includes(first)) {
|
|
366
|
+
return parts.length > 2 ? parts[1] : first;
|
|
367
|
+
}
|
|
368
|
+
return first;
|
|
369
|
+
};
|
|
370
|
+
// Assign colors to modules
|
|
371
|
+
const moduleNames = [...new Set(allNodes.map(n => getModule(n.id)))];
|
|
372
|
+
moduleNames.forEach((mod, i) => {
|
|
373
|
+
moduleColorMap[mod] = modulePalette[i % modulePalette.length];
|
|
374
|
+
});
|
|
375
|
+
// Reassign layer field to module name for coloring
|
|
376
|
+
for (const node of allNodes) {
|
|
377
|
+
node.layer = getModule(node.id);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
// Build links only between real files
|
|
381
|
+
const allLinks = report.dependencyGraph.edges
|
|
382
|
+
.filter(e => realFiles.has(e.from) && realFiles.has(e.to))
|
|
383
|
+
.map(e => ({ source: e.from, target: e.to }));
|
|
384
|
+
// Limit to top N most-connected nodes for large projects
|
|
385
|
+
const maxNodes = 60;
|
|
386
|
+
const sortedNodes = [...allNodes].sort((a, b) => b.connections - a.connections);
|
|
387
|
+
const limitedNodes = sortedNodes.slice(0, maxNodes);
|
|
388
|
+
const limitedNodeIds = new Set(limitedNodes.map(n => n.id));
|
|
389
|
+
const limitedLinks = allLinks.filter(l => limitedNodeIds.has(l.source) && limitedNodeIds.has(l.target));
|
|
390
|
+
const isLimited = allNodes.length > maxNodes;
|
|
391
|
+
// Collect unique layers/modules from limited nodes
|
|
392
|
+
const uniqueLayers = [...new Set(limitedNodes.map(n => n.layer))];
|
|
393
|
+
// Build dynamic color map for legend and D3
|
|
394
|
+
const colorMap = useModuleColoring
|
|
395
|
+
? moduleColorMap
|
|
396
|
+
: { API: '#ec4899', Service: '#3b82f6', Data: '#10b981', UI: '#f59e0b', Infrastructure: '#8b5cf6', Other: '#64748b' };
|
|
397
|
+
const legendLabel = useModuleColoring ? 'Colored by module' : 'Colored by layer';
|
|
398
|
+
const legendHtml = uniqueLayers.map(l => {
|
|
399
|
+
const color = colorMap[l] || '#64748b';
|
|
400
|
+
return `<span class="legend-item"><span class="legend-dot" style="background: ${color}"></span> ${l}</span>`;
|
|
401
|
+
}).join('');
|
|
402
|
+
const filterHtml = uniqueLayers.map(l => {
|
|
403
|
+
const color = colorMap[l] || '#64748b';
|
|
404
|
+
return `<label class="graph-filter-check"><input type="checkbox" checked data-layer="${l}" onchange="toggleGraphLayer('${l}', this.checked)"><span class="legend-dot" style="background: ${color}"></span> ${l}</label>`;
|
|
405
|
+
}).join('');
|
|
233
406
|
return `
|
|
234
407
|
<h2 class="section-title">🔗 Dependency Graph</h2>
|
|
235
408
|
<div class="card graph-card">
|
|
236
|
-
<div class="graph-
|
|
237
|
-
<
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
<
|
|
242
|
-
|
|
409
|
+
<div class="graph-controls">
|
|
410
|
+
<div class="graph-legend">
|
|
411
|
+
<span class="legend-label" style="color:#94a3b8;font-size:11px;margin-right:8px;">${legendLabel}:</span>
|
|
412
|
+
${legendHtml}
|
|
413
|
+
</div>
|
|
414
|
+
<div class="graph-filters">
|
|
415
|
+
<input type="text" id="graphSearch" class="graph-search" placeholder="🔍 Search node..." oninput="filterGraphNodes(this.value)">
|
|
416
|
+
<div class="graph-layer-filters">
|
|
417
|
+
${filterHtml}
|
|
418
|
+
</div>
|
|
419
|
+
</div>
|
|
420
|
+
${isLimited ? `<div class="graph-limit-notice">Showing top ${maxNodes} of ${allNodes.length} source files (most connected) · ${limitedLinks.length} links</div>` : ''}
|
|
243
421
|
</div>
|
|
244
|
-
<div id="dep-graph" style="width:100%; min-height:
|
|
245
|
-
<div class="graph-hint">🖱️ Drag nodes to
|
|
422
|
+
<div id="dep-graph" style="width:100%; min-height:500px;"></div>
|
|
423
|
+
<div class="graph-hint">🖱️ Drag nodes • Scroll to zoom • Double-click to reset • Node size = connections</div>
|
|
246
424
|
</div>
|
|
247
|
-
<script type="application/json" id="graph-nodes">${JSON.stringify(
|
|
248
|
-
<script type="application/json" id="graph-links">${JSON.stringify(
|
|
425
|
+
<script type="application/json" id="graph-nodes">${JSON.stringify(limitedNodes)}${'</' + 'script>'}
|
|
426
|
+
<script type="application/json" id="graph-links">${JSON.stringify(limitedLinks)}${'</' + 'script>'}
|
|
427
|
+
<script type="application/json" id="graph-colors">${JSON.stringify(colorMap)}${'</' + 'script>'}`;
|
|
249
428
|
}
|
|
250
429
|
/**
|
|
251
430
|
* Bubble chart for anti-patterns — bigger = more severe
|
|
@@ -347,7 +526,7 @@ ${this.getScripts(report)}
|
|
|
347
526
|
renderFooter() {
|
|
348
527
|
return `
|
|
349
528
|
<div class="footer">
|
|
350
|
-
<p>Generated by <a href="https://github.com/camilooscargbaptista/architect"
|
|
529
|
+
<p>Generated by <a href="https://github.com/camilooscargbaptista/architect">⚡ Architect v3.1</a> — Enterprise Architecture Intelligence</p>
|
|
351
530
|
<p>By <strong>Camilo Girardelli</strong> · <a href="https://www.girardellitecnologia.com">Girardelli Tecnologia</a></p>
|
|
352
531
|
</div>`;
|
|
353
532
|
}
|
|
@@ -476,10 +655,12 @@ ${this.getScripts(report)}
|
|
|
476
655
|
</details>
|
|
477
656
|
</div>
|
|
478
657
|
</div>
|
|
479
|
-
<
|
|
480
|
-
<
|
|
481
|
-
|
|
482
|
-
|
|
658
|
+
<details class="rstep-ops-accordion">
|
|
659
|
+
<summary class="rstep-ops-toggle">📋 Operations (${step.operations.length})</summary>
|
|
660
|
+
<div class="rstep-ops">
|
|
661
|
+
${operationsHtml}
|
|
662
|
+
</div>
|
|
663
|
+
</details>
|
|
483
664
|
<div class="rstep-impact">
|
|
484
665
|
<h4>📈 Score Impact</h4>
|
|
485
666
|
<div class="rimpact-tags">${impactHtml}</div>
|
|
@@ -507,6 +688,23 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
507
688
|
}, { threshold: 0.5 });
|
|
508
689
|
|
|
509
690
|
counters.forEach(c => observer.observe(c));
|
|
691
|
+
|
|
692
|
+
// ── Sidebar Active Section Tracking ──
|
|
693
|
+
const sectionIds = ['score', 'layers', 'anti-patterns', 'suggestions', 'refactoring', 'agents'];
|
|
694
|
+
const sectionObserver = new IntersectionObserver((entries) => {
|
|
695
|
+
entries.forEach(entry => {
|
|
696
|
+
if (entry.isIntersecting) {
|
|
697
|
+
document.querySelectorAll('.sidebar-link').forEach(l => l.classList.remove('active'));
|
|
698
|
+
const link = document.querySelector('.sidebar-link[data-section="' + entry.target.id + '"]');
|
|
699
|
+
if (link) link.classList.add('active');
|
|
700
|
+
}
|
|
701
|
+
});
|
|
702
|
+
}, { threshold: 0.15, rootMargin: '-80px 0px -60% 0px' });
|
|
703
|
+
|
|
704
|
+
sectionIds.forEach(id => {
|
|
705
|
+
const el = document.getElementById(id);
|
|
706
|
+
if (el) sectionObserver.observe(el);
|
|
707
|
+
});
|
|
510
708
|
});
|
|
511
709
|
|
|
512
710
|
function animateCounter(el, target) {
|
|
@@ -609,10 +807,12 @@ function animateCounter(el, target) {
|
|
|
609
807
|
|
|
610
808
|
const container = document.getElementById('dep-graph');
|
|
611
809
|
const width = container.clientWidth || 800;
|
|
612
|
-
const height =
|
|
810
|
+
const height = 500;
|
|
613
811
|
container.style.height = height + 'px';
|
|
614
812
|
|
|
615
|
-
|
|
813
|
+
// Dynamic color map — loaded from JSON (supports both layer and module coloring)
|
|
814
|
+
const colorsEl = document.getElementById('graph-colors');
|
|
815
|
+
const layerColors = colorsEl ? JSON.parse(colorsEl.textContent || '{}') : {
|
|
616
816
|
API: '#ec4899', Service: '#3b82f6', Data: '#10b981',
|
|
617
817
|
UI: '#f59e0b', Infrastructure: '#8b5cf6', Other: '#64748b',
|
|
618
818
|
};
|
|
@@ -621,28 +821,43 @@ function animateCounter(el, target) {
|
|
|
621
821
|
.attr('width', width).attr('height', height)
|
|
622
822
|
.attr('viewBox', [0, 0, width, height]);
|
|
623
823
|
|
|
824
|
+
// Zoom container
|
|
825
|
+
const g = svg.append('g');
|
|
826
|
+
|
|
827
|
+
// Zoom behavior
|
|
828
|
+
const zoom = d3.zoom()
|
|
829
|
+
.scaleExtent([0.2, 5])
|
|
830
|
+
.on('zoom', (event) => { g.attr('transform', event.transform); });
|
|
831
|
+
svg.call(zoom);
|
|
832
|
+
|
|
833
|
+
// Double-click to reset zoom
|
|
834
|
+
svg.on('dblclick.zoom', () => {
|
|
835
|
+
svg.transition().duration(500).call(zoom.transform, d3.zoomIdentity);
|
|
836
|
+
});
|
|
837
|
+
|
|
624
838
|
// Arrow marker
|
|
625
|
-
|
|
839
|
+
g.append('defs').append('marker')
|
|
626
840
|
.attr('id', 'arrowhead').attr('viewBox', '-0 -5 10 10')
|
|
627
841
|
.attr('refX', 20).attr('refY', 0).attr('orient', 'auto')
|
|
628
842
|
.attr('markerWidth', 6).attr('markerHeight', 6)
|
|
629
843
|
.append('path').attr('d', 'M 0,-5 L 10,0 L 0,5')
|
|
630
844
|
.attr('fill', '#475569');
|
|
631
845
|
|
|
846
|
+
// Tuned simulation for better spread
|
|
632
847
|
const simulation = d3.forceSimulation(nodes)
|
|
633
|
-
.force('link', d3.forceLink(links).id(d => d.id).distance(
|
|
634
|
-
.force('charge', d3.forceManyBody().strength(-
|
|
848
|
+
.force('link', d3.forceLink(links).id(d => d.id).distance(80))
|
|
849
|
+
.force('charge', d3.forceManyBody().strength(-250))
|
|
635
850
|
.force('center', d3.forceCenter(width / 2, height / 2))
|
|
636
|
-
.force('x', d3.forceX(width / 2).strength(0.
|
|
637
|
-
.force('y', d3.forceY(height / 2).strength(0.
|
|
638
|
-
.force('collision', d3.forceCollide().radius(d => Math.max(d.connections *
|
|
851
|
+
.force('x', d3.forceX(width / 2).strength(0.05))
|
|
852
|
+
.force('y', d3.forceY(height / 2).strength(0.05))
|
|
853
|
+
.force('collision', d3.forceCollide().radius(d => Math.max(d.connections * 2 + 16, 20)));
|
|
639
854
|
|
|
640
|
-
const link =
|
|
855
|
+
const link = g.append('g')
|
|
641
856
|
.selectAll('line').data(links).join('line')
|
|
642
|
-
.attr('stroke', '#334155').attr('stroke-width', 1
|
|
643
|
-
.attr('stroke-opacity', 0.
|
|
857
|
+
.attr('stroke', '#334155').attr('stroke-width', 1)
|
|
858
|
+
.attr('stroke-opacity', 0.4).attr('marker-end', 'url(#arrowhead)');
|
|
644
859
|
|
|
645
|
-
const node =
|
|
860
|
+
const node = g.append('g')
|
|
646
861
|
.selectAll('g').data(nodes).join('g')
|
|
647
862
|
.call(d3.drag()
|
|
648
863
|
.on('start', (e, d) => { if (!e.active) simulation.alphaTarget(0.3).restart(); d.fx = d.x; d.fy = d.y; })
|
|
@@ -650,37 +865,55 @@ function animateCounter(el, target) {
|
|
|
650
865
|
.on('end', (e, d) => { if (!e.active) simulation.alphaTarget(0); d.fx = null; d.fy = null; })
|
|
651
866
|
);
|
|
652
867
|
|
|
653
|
-
// Node circles —
|
|
868
|
+
// Node circles — color by layer
|
|
654
869
|
node.append('circle')
|
|
655
|
-
.attr('r', d => Math.max(d.connections *
|
|
870
|
+
.attr('r', d => Math.max(d.connections * 2.5 + 5, 6))
|
|
656
871
|
.attr('fill', d => layerColors[d.layer] || '#64748b')
|
|
657
|
-
.attr('stroke', '#0f172a').attr('stroke-width',
|
|
658
|
-
.attr('opacity', 0.
|
|
872
|
+
.attr('stroke', '#0f172a').attr('stroke-width', 1.5)
|
|
873
|
+
.attr('opacity', 0.9);
|
|
659
874
|
|
|
660
|
-
// Node labels
|
|
661
|
-
node.append('text')
|
|
875
|
+
// Node labels — only show for nodes with enough connections
|
|
876
|
+
node.filter(d => d.connections >= 2).append('text')
|
|
662
877
|
.text(d => d.name.replace(/\\.[^.]+$/, ''))
|
|
663
|
-
.attr('x', 0).attr('y', d => -(Math.max(d.connections *
|
|
878
|
+
.attr('x', 0).attr('y', d => -(Math.max(d.connections * 2.5 + 5, 6) + 4))
|
|
664
879
|
.attr('text-anchor', 'middle')
|
|
665
|
-
.attr('fill', '#
|
|
880
|
+
.attr('fill', '#e2e8f0').attr('font-size', '9px').attr('font-weight', '500');
|
|
666
881
|
|
|
667
|
-
// Tooltip
|
|
882
|
+
// Tooltip
|
|
668
883
|
node.append('title')
|
|
669
884
|
.text(d => d.id + '\\nConnections: ' + d.connections + '\\nLayer: ' + d.layer);
|
|
670
885
|
|
|
671
886
|
simulation.on('tick', () => {
|
|
672
|
-
// Clamp nodes to stay within SVG bounds
|
|
673
|
-
nodes.forEach(d => {
|
|
674
|
-
const r = Math.max(d.connections * 3 + 6, 8) + 10;
|
|
675
|
-
d.x = Math.max(r, Math.min(width - r, d.x));
|
|
676
|
-
d.y = Math.max(r, Math.min(height - r, d.y));
|
|
677
|
-
});
|
|
678
|
-
|
|
679
887
|
link
|
|
680
888
|
.attr('x1', d => d.source.x).attr('y1', d => d.source.y)
|
|
681
889
|
.attr('x2', d => d.target.x).attr('y2', d => d.target.y);
|
|
682
890
|
node.attr('transform', d => 'translate(' + d.x + ',' + d.y + ')');
|
|
683
891
|
});
|
|
892
|
+
|
|
893
|
+
// Expose search and filter functions
|
|
894
|
+
window.filterGraphNodes = function(query) {
|
|
895
|
+
if (!query) {
|
|
896
|
+
node.attr('opacity', 1);
|
|
897
|
+
link.attr('opacity', 0.4);
|
|
898
|
+
return;
|
|
899
|
+
}
|
|
900
|
+
query = query.toLowerCase();
|
|
901
|
+
node.attr('opacity', d => d.id.toLowerCase().includes(query) || d.name.toLowerCase().includes(query) ? 1 : 0.1);
|
|
902
|
+
link.attr('opacity', d => {
|
|
903
|
+
const srcMatch = d.source.id.toLowerCase().includes(query);
|
|
904
|
+
const tgtMatch = d.target.id.toLowerCase().includes(query);
|
|
905
|
+
return (srcMatch || tgtMatch) ? 0.6 : 0.05;
|
|
906
|
+
});
|
|
907
|
+
};
|
|
908
|
+
|
|
909
|
+
window.toggleGraphLayer = function(layer, visible) {
|
|
910
|
+
node.filter(d => d.layer === layer)
|
|
911
|
+
.transition().duration(300)
|
|
912
|
+
.attr('opacity', visible ? 1 : 0.05);
|
|
913
|
+
link.filter(d => d.source.layer === layer || d.target.layer === layer)
|
|
914
|
+
.transition().duration(300)
|
|
915
|
+
.attr('opacity', visible ? 0.4 : 0.02);
|
|
916
|
+
};
|
|
684
917
|
})();
|
|
685
918
|
|
|
686
919
|
// ── Bubble Chart ──
|
|
@@ -785,49 +1018,60 @@ function animateCounter(el, target) {
|
|
|
785
1018
|
return '#fbbf24';
|
|
786
1019
|
return '#60a5fa';
|
|
787
1020
|
};
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
1021
|
+
// Status helpers
|
|
1022
|
+
const statusBadge = (status) => {
|
|
1023
|
+
const map = {
|
|
1024
|
+
'KEEP': { icon: '✅', label: 'KEEP', color: '#22c55e' },
|
|
1025
|
+
'MODIFY': { icon: '🔵', label: 'MODIFY', color: '#3b82f6' },
|
|
1026
|
+
'CREATE': { icon: '🟡', label: 'NEW', color: '#f59e0b' },
|
|
1027
|
+
'DELETE': { icon: '🔴', label: 'REMOVE', color: '#ef4444' },
|
|
1028
|
+
};
|
|
1029
|
+
const s = map[status] || map['CREATE'];
|
|
1030
|
+
return `<span class="agent-status-badge" style="background:${s.color}20;color:${s.color};border:1px solid ${s.color}40">${s.icon} ${s.label}</span>`;
|
|
1031
|
+
};
|
|
1032
|
+
const statusBorder = (status) => {
|
|
1033
|
+
const map = {
|
|
1034
|
+
'KEEP': '#22c55e', 'MODIFY': '#3b82f6', 'CREATE': '#f59e0b', 'DELETE': '#ef4444',
|
|
1035
|
+
};
|
|
1036
|
+
return map[status] || '#334155';
|
|
1037
|
+
};
|
|
1038
|
+
const agentCards = s.suggestedAgents.map(a => `<label class="agent-toggle-card" data-category="agents" data-name="${a.name}">
|
|
1039
|
+
<input type="checkbox" class="agent-check" ${a.status !== 'DELETE' ? 'checked' : ''} data-type="agents" data-item="${a.name}">
|
|
1040
|
+
<div class="agent-toggle-inner" style="border-color:${statusBorder(a.status)}">
|
|
1041
|
+
<div class="agent-toggle-icon">${roleIcon(a.name)}</div>
|
|
792
1042
|
<div class="agent-toggle-info">
|
|
793
|
-
<span class="agent-toggle-name">${a}</span>
|
|
794
|
-
<span class="agent-toggle-role" style="color:${roleColor(a)}">${roleLabel(a)}</span>
|
|
1043
|
+
<span class="agent-toggle-name">${a.name}</span>
|
|
1044
|
+
<span class="agent-toggle-role" style="color:${roleColor(a.name)}">${roleLabel(a.name)}</span>
|
|
1045
|
+
${a.description ? `<span class="agent-toggle-desc">${a.description}</span>` : ''}
|
|
795
1046
|
</div>
|
|
1047
|
+
${statusBadge(a.status)}
|
|
796
1048
|
<div class="agent-toggle-check">\u2713</div>
|
|
797
1049
|
</div>
|
|
798
1050
|
</label>`).join('\n');
|
|
799
|
-
const
|
|
800
|
-
<input type="checkbox" class="agent-check" checked data-type="
|
|
801
|
-
<div class="agent-toggle-inner">
|
|
802
|
-
<span class="agent-toggle-icon"
|
|
803
|
-
<
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
<input type="checkbox" class="agent-check" checked data-type="guards" data-item="${g}">
|
|
809
|
-
<div class="agent-toggle-inner">
|
|
810
|
-
<span class="agent-toggle-icon">\u{1F6E1}\uFE0F</span>
|
|
811
|
-
<span class="agent-toggle-name">${g}.md</span>
|
|
812
|
-
<div class="agent-toggle-check">\u2713</div>
|
|
813
|
-
</div>
|
|
814
|
-
</label>`).join('\n');
|
|
815
|
-
const workflowCards = s.suggestedWorkflows.map(w => `<label class="agent-toggle-card mini" data-category="workflows">
|
|
816
|
-
<input type="checkbox" class="agent-check" checked data-type="workflows" data-item="${w}">
|
|
817
|
-
<div class="agent-toggle-inner">
|
|
818
|
-
<span class="agent-toggle-icon">\u26A1</span>
|
|
819
|
-
<span class="agent-toggle-name">${w}.md</span>
|
|
1051
|
+
const miniCard = (item, icon, type) => `<label class="agent-toggle-card mini" data-category="${type}">
|
|
1052
|
+
<input type="checkbox" class="agent-check" ${item.status !== 'DELETE' ? 'checked' : ''} data-type="${type}" data-item="${item.name}">
|
|
1053
|
+
<div class="agent-toggle-inner" style="border-color:${statusBorder(item.status)}">
|
|
1054
|
+
<span class="agent-toggle-icon">${icon}</span>
|
|
1055
|
+
<div class="agent-toggle-info">
|
|
1056
|
+
<span class="agent-toggle-name">${item.name}.md</span>
|
|
1057
|
+
${item.description ? `<span class="agent-toggle-desc">${item.description}</span>` : ''}
|
|
1058
|
+
</div>
|
|
1059
|
+
${statusBadge(item.status)}
|
|
820
1060
|
<div class="agent-toggle-check">\u2713</div>
|
|
821
1061
|
</div>
|
|
822
|
-
</label
|
|
1062
|
+
</label>`;
|
|
1063
|
+
const ruleCards = s.suggestedRules.map(r => miniCard(r, '\u{1F4CF}', 'rules')).join('\n');
|
|
1064
|
+
const guardCards = s.suggestedGuards.map(g => miniCard(g, '\u{1F6E1}\uFE0F', 'guards')).join('\n');
|
|
1065
|
+
const workflowCards = s.suggestedWorkflows.map(w => miniCard(w, '\u26A1', 'workflows')).join('\n');
|
|
823
1066
|
const skillCards = s.suggestedSkills.map(sk => `<label class="agent-toggle-card" data-category="skills">
|
|
824
1067
|
<input type="checkbox" class="agent-check" checked data-type="skills" data-item="${sk.source}">
|
|
825
|
-
<div class="agent-toggle-inner">
|
|
1068
|
+
<div class="agent-toggle-inner" style="border-color:${statusBorder(sk.status)}">
|
|
826
1069
|
<span class="agent-toggle-icon">\u{1F9E0}</span>
|
|
827
1070
|
<div class="agent-toggle-info">
|
|
828
1071
|
<span class="agent-toggle-name">${sk.name}</span>
|
|
829
1072
|
<span class="agent-toggle-role" style="color:#34d399">${sk.description}</span>
|
|
830
1073
|
</div>
|
|
1074
|
+
${statusBadge(sk.status)}
|
|
831
1075
|
<div class="agent-toggle-check">\u2713</div>
|
|
832
1076
|
</div>
|
|
833
1077
|
</label>`).join('\n');
|
|
@@ -852,17 +1096,31 @@ function animateCounter(el, target) {
|
|
|
852
1096
|
`\u{1F527} ${s.stack.primary}`,
|
|
853
1097
|
`\u{1F4E6} ${s.stack.frameworks.length > 0 ? s.stack.frameworks.join(', ') : 'No framework'}`,
|
|
854
1098
|
s.hasExistingAgents ? '\u{1F4C1} Existing .agent/' : '\u{1F4C1} New .agent/',
|
|
855
|
-
|
|
1099
|
+
...(s.stack.hasBackend ? ['\u{1F519} Backend'] : []),
|
|
1100
|
+
...(s.stack.hasFrontend ? ['\u{1F5A5}\uFE0F Frontend'] : []),
|
|
1101
|
+
...(s.stack.hasMobile ? ['\u{1F4F1} Mobile'] : []),
|
|
1102
|
+
...(s.stack.hasDatabase ? ['\u{1F5C4}\uFE0F Database'] : []),
|
|
856
1103
|
];
|
|
857
1104
|
const totalItems = s.suggestedAgents.length + s.suggestedRules.length + s.suggestedGuards.length + s.suggestedWorkflows.length + s.suggestedSkills.length;
|
|
1105
|
+
// Status summary counts
|
|
1106
|
+
const allItems = [...s.suggestedAgents, ...s.suggestedRules, ...s.suggestedGuards, ...s.suggestedWorkflows];
|
|
1107
|
+
const keepCount = allItems.filter(i => i.status === 'KEEP').length;
|
|
1108
|
+
const modifyCount = allItems.filter(i => i.status === 'MODIFY').length;
|
|
1109
|
+
const createCount = allItems.filter(i => i.status === 'CREATE').length;
|
|
858
1110
|
return `
|
|
859
|
-
<h2 class="section-title">\u{1F916} Agent System
|
|
1111
|
+
<h2 class="section-title">\u{1F916} Agent System</h2>
|
|
860
1112
|
|
|
861
1113
|
<div class="card agent-system-card">
|
|
862
1114
|
<div class="agent-stack-banner">
|
|
863
1115
|
${stackPills.map(p => `<div class="stack-pill">${p}</div>`).join('\n ')}
|
|
864
1116
|
</div>
|
|
865
1117
|
|
|
1118
|
+
<div class="agent-status-legend">
|
|
1119
|
+
<span class="status-legend-item"><span class="legend-dot" style="background:#22c55e"></span> KEEP (${keepCount})</span>
|
|
1120
|
+
<span class="status-legend-item"><span class="legend-dot" style="background:#3b82f6"></span> MODIFY (${modifyCount})</span>
|
|
1121
|
+
<span class="status-legend-item"><span class="legend-dot" style="background:#f59e0b"></span> NEW (${createCount})</span>
|
|
1122
|
+
</div>
|
|
1123
|
+
|
|
866
1124
|
<div class="agent-controls">
|
|
867
1125
|
<button class="agent-ctrl-btn" onclick="toggleAll(true)">\u2705 Select All</button>
|
|
868
1126
|
<button class="agent-ctrl-btn" onclick="toggleAll(false)">\u2B1C Select None</button>
|
|
@@ -910,18 +1168,22 @@ function animateCounter(el, target) {
|
|
|
910
1168
|
<style>
|
|
911
1169
|
.agent-system-card { padding: 1.5rem; }
|
|
912
1170
|
.agent-stack-banner { display: flex; gap: 0.5rem; flex-wrap: wrap; margin-bottom: 1.5rem; }
|
|
913
|
-
.stack-pill { background: #1e293b; border: 1px solid #334155; border-radius: 99px; padding: 0.4rem 1rem; font-size: 0.8rem; color: #94a3b8; white-space:
|
|
1171
|
+
.stack-pill { background: #1e293b; border: 1px solid #334155; border-radius: 99px; padding: 0.4rem 1rem; font-size: 0.8rem; color: #94a3b8; white-space: nowrap; }
|
|
1172
|
+
.agent-status-legend { display: flex; gap: 1.5rem; margin-bottom: 1rem; padding: 0.5rem 0; border-bottom: 1px solid #1e293b; }
|
|
1173
|
+
.status-legend-item { display: flex; align-items: center; gap: 0.4rem; font-size: 0.8rem; color: #94a3b8; }
|
|
1174
|
+
.agent-status-badge { display: inline-flex; align-items: center; gap: 0.25rem; padding: 0.15rem 0.5rem; border-radius: 99px; font-size: 0.65rem; font-weight: 700; flex-shrink: 0; letter-spacing: 0.03em; }
|
|
1175
|
+
.agent-toggle-desc { display: block; font-size: 0.65rem; color: #64748b; margin-top: 0.15rem; line-height: 1.3; }
|
|
914
1176
|
.agent-controls { display: flex; align-items: center; gap: 0.75rem; margin-bottom: 1.5rem; }
|
|
915
1177
|
.agent-ctrl-btn { background: #1e293b; border: 1px solid #334155; color: #e2e8f0; padding: 0.4rem 1rem; border-radius: 8px; font-size: 0.8rem; cursor: pointer; transition: all 0.2s; }
|
|
916
1178
|
.agent-ctrl-btn:hover { background: #334155; }
|
|
917
1179
|
.agent-count-label { color: #94a3b8; font-size: 0.85rem; margin-left: auto; }
|
|
918
1180
|
#agentSelectedCount { color: #c084fc; font-weight: 700; }
|
|
919
1181
|
.agent-section-subtitle { color: #e2e8f0; font-size: 1.05rem; font-weight: 700; margin: 1.25rem 0 0.75rem; }
|
|
920
|
-
.agent-toggle-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(
|
|
1182
|
+
.agent-toggle-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 0.75rem; }
|
|
921
1183
|
.agent-toggle-card { cursor: pointer; transition: all 0.3s; }
|
|
922
1184
|
.agent-toggle-card input { display: none; }
|
|
923
1185
|
.agent-toggle-inner { display: flex; align-items: center; gap: 0.75rem; background: #1e293b; border: 2px solid #334155; border-radius: 12px; padding: 0.75rem 1rem; transition: all 0.3s; }
|
|
924
|
-
.agent-toggle-card input:checked + .agent-toggle-inner {
|
|
1186
|
+
.agent-toggle-card input:checked + .agent-toggle-inner { background: #1e1b4b; }
|
|
925
1187
|
.agent-toggle-icon { font-size: 1.3rem; flex-shrink: 0; }
|
|
926
1188
|
.agent-toggle-info { flex: 1; min-width: 0; }
|
|
927
1189
|
.agent-toggle-name { display: block; color: #e2e8f0; font-weight: 600; font-size: 0.85rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
@@ -992,7 +1254,50 @@ function animateCounter(el, target) {
|
|
|
992
1254
|
min-height: 100vh;
|
|
993
1255
|
}
|
|
994
1256
|
|
|
995
|
-
|
|
1257
|
+
html { scroll-behavior: smooth; }
|
|
1258
|
+
|
|
1259
|
+
/* ── Layout ── */
|
|
1260
|
+
.report-layout { display: flex; min-height: 100vh; }
|
|
1261
|
+
|
|
1262
|
+
.sidebar {
|
|
1263
|
+
position: sticky; top: 0; height: 100vh; width: 220px; min-width: 220px;
|
|
1264
|
+
background: linear-gradient(180deg, #0f172a 0%, #1e293b 100%);
|
|
1265
|
+
border-right: 1px solid #334155; padding: 1.5rem 0;
|
|
1266
|
+
display: flex; flex-direction: column; gap: 0.25rem;
|
|
1267
|
+
overflow-y: auto; z-index: 100;
|
|
1268
|
+
}
|
|
1269
|
+
.sidebar-title {
|
|
1270
|
+
font-size: 0.7rem; font-weight: 700; text-transform: uppercase;
|
|
1271
|
+
letter-spacing: 0.15em; color: #475569; padding: 0 1.25rem; margin-bottom: 0.75rem;
|
|
1272
|
+
}
|
|
1273
|
+
.sidebar-link {
|
|
1274
|
+
display: flex; align-items: center; gap: 0.5rem; padding: 0.6rem 1.25rem;
|
|
1275
|
+
color: #94a3b8; text-decoration: none; font-size: 0.8rem; font-weight: 500;
|
|
1276
|
+
border-left: 3px solid transparent; transition: all 0.2s;
|
|
1277
|
+
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
|
1278
|
+
}
|
|
1279
|
+
.sidebar-link:hover { color: #e2e8f0; background: #1e293b; border-left-color: #475569; }
|
|
1280
|
+
.sidebar-link.active { color: #c084fc; background: #c084fc10; border-left-color: #c084fc; font-weight: 700; }
|
|
1281
|
+
|
|
1282
|
+
.sidebar-toggle {
|
|
1283
|
+
display: none; position: fixed; bottom: 1.5rem; right: 1.5rem; z-index: 200;
|
|
1284
|
+
width: 48px; height: 48px; border-radius: 50%; border: none;
|
|
1285
|
+
background: #c084fc; color: #0f172a; font-size: 1.2rem; cursor: pointer;
|
|
1286
|
+
box-shadow: 0 4px 16px rgba(192,132,252,0.4); transition: all 0.2s;
|
|
1287
|
+
}
|
|
1288
|
+
.sidebar-toggle:hover { transform: scale(1.1); }
|
|
1289
|
+
|
|
1290
|
+
@media (max-width: 1024px) {
|
|
1291
|
+
.sidebar {
|
|
1292
|
+
position: fixed; left: -240px; top: 0; width: 240px; min-width: 240px;
|
|
1293
|
+
transition: left 0.3s ease; box-shadow: none;
|
|
1294
|
+
}
|
|
1295
|
+
.sidebar.sidebar-open { left: 0; box-shadow: 4px 0 24px rgba(0,0,0,0.5); }
|
|
1296
|
+
.sidebar-toggle { display: flex; align-items: center; justify-content: center; }
|
|
1297
|
+
.report-layout { flex-direction: column; }
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
.container { max-width: 1200px; margin: 0 auto; padding: 2rem; flex: 1; min-width: 0; }
|
|
996
1301
|
|
|
997
1302
|
/* ── Header ── */
|
|
998
1303
|
.header {
|
|
@@ -1071,6 +1376,170 @@ function animateCounter(el, target) {
|
|
|
1071
1376
|
display: flex; align-items: center; gap: 0.5rem;
|
|
1072
1377
|
}
|
|
1073
1378
|
|
|
1379
|
+
/* ── Section Accordion ── */
|
|
1380
|
+
.section-accordion {
|
|
1381
|
+
margin: 1.5rem 0; border: 1px solid #334155; border-radius: 16px;
|
|
1382
|
+
background: transparent; overflow: hidden;
|
|
1383
|
+
}
|
|
1384
|
+
.section-accordion-header {
|
|
1385
|
+
cursor: pointer; list-style: none; display: flex; align-items: center; gap: 0.75rem;
|
|
1386
|
+
font-size: 1.3rem; font-weight: 700; color: #e2e8f0;
|
|
1387
|
+
padding: 1.25rem 1.5rem; background: linear-gradient(135deg, #1e293b, #0f172a);
|
|
1388
|
+
border-bottom: 1px solid transparent; transition: all 0.3s; user-select: none;
|
|
1389
|
+
}
|
|
1390
|
+
.section-accordion-header:hover { background: linear-gradient(135deg, #334155, #1e293b); }
|
|
1391
|
+
.section-accordion[open] > .section-accordion-header { border-bottom-color: #334155; }
|
|
1392
|
+
.section-accordion-header::after {
|
|
1393
|
+
content: '\\25B6'; margin-left: auto; font-size: 0.8rem; color: #818cf8;
|
|
1394
|
+
transition: transform 0.3s;
|
|
1395
|
+
}
|
|
1396
|
+
.section-accordion[open] > .section-accordion-header::after { transform: rotate(90deg); }
|
|
1397
|
+
.section-accordion-header::-webkit-details-marker { display: none; }
|
|
1398
|
+
.section-accordion-body { padding: 0.5rem 0; }
|
|
1399
|
+
|
|
1400
|
+
/* ── Project Overview ── */
|
|
1401
|
+
.overview-grid {
|
|
1402
|
+
display: grid;
|
|
1403
|
+
grid-template-columns: 1fr 1fr;
|
|
1404
|
+
gap: 1rem;
|
|
1405
|
+
margin-bottom: 1.5rem;
|
|
1406
|
+
}
|
|
1407
|
+
.overview-card {
|
|
1408
|
+
background: rgba(255,255,255,0.03);
|
|
1409
|
+
border: 1px solid #334155;
|
|
1410
|
+
border-radius: 12px;
|
|
1411
|
+
padding: 1.25rem;
|
|
1412
|
+
}
|
|
1413
|
+
.overview-main {
|
|
1414
|
+
grid-column: 1 / -1;
|
|
1415
|
+
background: linear-gradient(135deg, rgba(59,130,246,0.08), rgba(139,92,246,0.08));
|
|
1416
|
+
border-color: #3b82f6;
|
|
1417
|
+
}
|
|
1418
|
+
.overview-label {
|
|
1419
|
+
font-size: 0.75rem;
|
|
1420
|
+
font-weight: 600;
|
|
1421
|
+
text-transform: uppercase;
|
|
1422
|
+
letter-spacing: 0.05em;
|
|
1423
|
+
color: #94a3b8;
|
|
1424
|
+
margin-bottom: 0.75rem;
|
|
1425
|
+
}
|
|
1426
|
+
.overview-description {
|
|
1427
|
+
font-size: 1.1rem;
|
|
1428
|
+
color: #e2e8f0;
|
|
1429
|
+
line-height: 1.6;
|
|
1430
|
+
margin-bottom: 0.75rem;
|
|
1431
|
+
}
|
|
1432
|
+
.overview-purpose-row {
|
|
1433
|
+
display: flex;
|
|
1434
|
+
align-items: center;
|
|
1435
|
+
gap: 0.5rem;
|
|
1436
|
+
}
|
|
1437
|
+
.overview-purpose-label {
|
|
1438
|
+
font-size: 0.8rem;
|
|
1439
|
+
color: #64748b;
|
|
1440
|
+
}
|
|
1441
|
+
.overview-purpose-value {
|
|
1442
|
+
font-size: 0.85rem;
|
|
1443
|
+
color: #a78bfa;
|
|
1444
|
+
font-weight: 600;
|
|
1445
|
+
background: rgba(139,92,246,0.1);
|
|
1446
|
+
padding: 0.2rem 0.6rem;
|
|
1447
|
+
border-radius: 6px;
|
|
1448
|
+
}
|
|
1449
|
+
.overview-tags {
|
|
1450
|
+
display: flex;
|
|
1451
|
+
flex-wrap: wrap;
|
|
1452
|
+
gap: 0.4rem;
|
|
1453
|
+
}
|
|
1454
|
+
.overview-tag {
|
|
1455
|
+
font-size: 0.75rem;
|
|
1456
|
+
padding: 0.25rem 0.6rem;
|
|
1457
|
+
border-radius: 6px;
|
|
1458
|
+
font-weight: 500;
|
|
1459
|
+
}
|
|
1460
|
+
.tech-tag {
|
|
1461
|
+
background: rgba(59,130,246,0.15);
|
|
1462
|
+
color: #60a5fa;
|
|
1463
|
+
border: 1px solid rgba(59,130,246,0.3);
|
|
1464
|
+
}
|
|
1465
|
+
.keyword-tag {
|
|
1466
|
+
background: rgba(16,185,129,0.1);
|
|
1467
|
+
color: #34d399;
|
|
1468
|
+
border: 1px solid rgba(16,185,129,0.2);
|
|
1469
|
+
}
|
|
1470
|
+
.overview-entry {
|
|
1471
|
+
font-size: 0.8rem;
|
|
1472
|
+
background: rgba(255,255,255,0.05);
|
|
1473
|
+
padding: 0.25rem 0.5rem;
|
|
1474
|
+
border-radius: 4px;
|
|
1475
|
+
color: #e2e8f0;
|
|
1476
|
+
font-family: 'SF Mono', monospace;
|
|
1477
|
+
}
|
|
1478
|
+
.overview-entries {
|
|
1479
|
+
display: flex;
|
|
1480
|
+
flex-wrap: wrap;
|
|
1481
|
+
gap: 0.4rem;
|
|
1482
|
+
}
|
|
1483
|
+
.overview-modules-section {
|
|
1484
|
+
margin-top: 0.5rem;
|
|
1485
|
+
}
|
|
1486
|
+
.overview-modules-grid {
|
|
1487
|
+
display: grid;
|
|
1488
|
+
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
|
1489
|
+
gap: 0.75rem;
|
|
1490
|
+
margin-top: 0.5rem;
|
|
1491
|
+
}
|
|
1492
|
+
.overview-module {
|
|
1493
|
+
background: rgba(255,255,255,0.03);
|
|
1494
|
+
border: 1px solid #1e293b;
|
|
1495
|
+
border-radius: 8px;
|
|
1496
|
+
padding: 0.75rem 1rem;
|
|
1497
|
+
transition: border-color 0.2s;
|
|
1498
|
+
}
|
|
1499
|
+
.overview-module:hover {
|
|
1500
|
+
border-color: #3b82f6;
|
|
1501
|
+
}
|
|
1502
|
+
.overview-module-name {
|
|
1503
|
+
font-weight: 600;
|
|
1504
|
+
color: #e2e8f0;
|
|
1505
|
+
font-size: 0.9rem;
|
|
1506
|
+
margin-bottom: 0.25rem;
|
|
1507
|
+
}
|
|
1508
|
+
.overview-module-desc {
|
|
1509
|
+
color: #94a3b8;
|
|
1510
|
+
font-size: 0.75rem;
|
|
1511
|
+
margin-bottom: 0.25rem;
|
|
1512
|
+
}
|
|
1513
|
+
.overview-module-files {
|
|
1514
|
+
color: #64748b;
|
|
1515
|
+
font-size: 0.7rem;
|
|
1516
|
+
}
|
|
1517
|
+
.overview-empty {
|
|
1518
|
+
color: #475569;
|
|
1519
|
+
font-size: 0.85rem;
|
|
1520
|
+
font-style: italic;
|
|
1521
|
+
}
|
|
1522
|
+
@media (max-width: 768px) {
|
|
1523
|
+
.overview-grid { grid-template-columns: 1fr; }
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
/* ── Operations Accordion (inside refactoring steps) ── */
|
|
1527
|
+
.rstep-ops-accordion {
|
|
1528
|
+
margin: 0.75rem 0; border: 1px solid #1e293b; border-radius: 10px; overflow: hidden;
|
|
1529
|
+
}
|
|
1530
|
+
.rstep-ops-toggle {
|
|
1531
|
+
cursor: pointer; list-style: none; display: flex; align-items: center; gap: 0.5rem;
|
|
1532
|
+
font-size: 0.9rem; font-weight: 600; color: #94a3b8;
|
|
1533
|
+
padding: 0.75rem 1rem; background: #0f172a; transition: all 0.2s;
|
|
1534
|
+
}
|
|
1535
|
+
.rstep-ops-toggle:hover { background: #1e293b; color: #e2e8f0; }
|
|
1536
|
+
.rstep-ops-toggle::after {
|
|
1537
|
+
content: '\\25B6'; margin-left: auto; font-size: 0.65rem; color: #818cf8;
|
|
1538
|
+
transition: transform 0.3s;
|
|
1539
|
+
}
|
|
1540
|
+
.rstep-ops-accordion[open] > .rstep-ops-toggle::after { transform: rotate(90deg); }
|
|
1541
|
+
.rstep-ops-toggle::-webkit-details-marker { display: none; }
|
|
1542
|
+
|
|
1074
1543
|
/* ── Cards ── */
|
|
1075
1544
|
.card {
|
|
1076
1545
|
background: #1e293b; border-radius: 16px; border: 1px solid #334155;
|
|
@@ -1080,17 +1549,42 @@ function animateCounter(el, target) {
|
|
|
1080
1549
|
|
|
1081
1550
|
/* ── Graph ── */
|
|
1082
1551
|
.graph-card { padding: 1rem; }
|
|
1552
|
+
.graph-controls { margin-bottom: 0.75rem; }
|
|
1083
1553
|
.graph-legend {
|
|
1084
1554
|
display: flex; gap: 1rem; flex-wrap: wrap; margin-bottom: 0.5rem;
|
|
1085
1555
|
justify-content: center;
|
|
1086
1556
|
}
|
|
1087
1557
|
.legend-item { display: flex; align-items: center; gap: 4px; font-size: 0.75rem; color: #94a3b8; }
|
|
1088
|
-
.legend-dot { width: 10px; height: 10px; border-radius: 50%; display: inline-block; }
|
|
1558
|
+
.legend-dot { width: 10px; height: 10px; border-radius: 50%; display: inline-block; flex-shrink: 0; }
|
|
1559
|
+
.graph-filters {
|
|
1560
|
+
display: flex; gap: 0.75rem; align-items: center; flex-wrap: wrap;
|
|
1561
|
+
justify-content: center; margin-top: 0.5rem;
|
|
1562
|
+
}
|
|
1563
|
+
.graph-search {
|
|
1564
|
+
background: #0f172a; border: 1px solid #334155; border-radius: 8px;
|
|
1565
|
+
padding: 0.4rem 0.75rem; color: #e2e8f0; font-size: 0.8rem;
|
|
1566
|
+
outline: none; width: 180px; transition: border-color 0.2s;
|
|
1567
|
+
}
|
|
1568
|
+
.graph-search:focus { border-color: #818cf8; }
|
|
1569
|
+
.graph-layer-filters {
|
|
1570
|
+
display: flex; gap: 0.5rem; flex-wrap: wrap; align-items: center;
|
|
1571
|
+
}
|
|
1572
|
+
.graph-filter-check {
|
|
1573
|
+
display: flex; align-items: center; gap: 4px;
|
|
1574
|
+
font-size: 0.75rem; color: #94a3b8; cursor: pointer;
|
|
1575
|
+
}
|
|
1576
|
+
.graph-filter-check input { width: 14px; height: 14px; accent-color: #818cf8; }
|
|
1577
|
+
.graph-limit-notice {
|
|
1578
|
+
text-align: center; font-size: 0.75rem; color: #f59e0b;
|
|
1579
|
+
background: #f59e0b15; padding: 0.3rem 0.75rem; border-radius: 6px;
|
|
1580
|
+
margin-top: 0.5rem;
|
|
1581
|
+
}
|
|
1089
1582
|
.graph-hint {
|
|
1090
1583
|
text-align: center; font-size: 0.75rem; color: #475569; margin-top: 0.5rem;
|
|
1091
1584
|
font-style: italic;
|
|
1092
1585
|
}
|
|
1093
|
-
#dep-graph svg { background: rgba(0,0,0,0.2); border-radius: 12px; }
|
|
1586
|
+
#dep-graph svg { background: rgba(0,0,0,0.2); border-radius: 12px; cursor: grab; }
|
|
1587
|
+
#dep-graph svg:active { cursor: grabbing; }
|
|
1094
1588
|
|
|
1095
1589
|
/* ── Layers Grid ── */
|
|
1096
1590
|
.layers-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 1rem; }
|