feat: 优化依赖树生成并默认使用树形视图

This commit is contained in:
hailaz
2026-01-08 14:54:39 +08:00
parent df857a1dd6
commit a7b2c21974
4 changed files with 54 additions and 14 deletions

View File

@ -34,11 +34,15 @@ func (a *analyzer) generate(in Input) string {
// generateTree generates ASCII tree output.
func (a *analyzer) generateTree(in Input) string {
var sb strings.Builder
pkgs := a.getSortedPackages()
for _, pkgPath := range pkgs {
// Find root packages (packages that are not imported by any other package)
rootPkgs := a.findRootPackages()
// Use a single visited map across all root packages to avoid duplicates
a.visited = make(map[string]bool)
for _, pkgPath := range rootPkgs {
pkg := a.packages[pkgPath]
a.visited = make(map[string]bool)
shortName := a.shortName(pkg.ImportPath, in.Group)
sb.WriteString(shortName + "\n")
a.printTreeNode(&sb, pkg, "", in, 0)
@ -46,6 +50,33 @@ func (a *analyzer) generateTree(in Input) string {
return sb.String()
}
// findRootPackages finds packages that are not imported by any other internal package.
func (a *analyzer) findRootPackages() []string {
// Build a set of all imported packages
imported := make(map[string]bool)
for _, pkg := range a.packages {
for _, dep := range pkg.Imports {
imported[dep] = true
}
}
// Find packages that are not imported by others
roots := make([]string, 0)
for pkgPath := range a.packages {
if !imported[pkgPath] {
roots = append(roots, pkgPath)
}
}
// If no roots found (circular dependencies), use all packages
if len(roots) == 0 {
roots = a.getSortedPackages()
}
sort.Strings(roots)
return roots
}
func (a *analyzer) printTreeNode(sb *strings.Builder, pkg *goPackage, prefix string, in Input, depth int) {
if in.Depth > 0 && depth >= in.Depth {
return

View File

@ -6,7 +6,7 @@ let selectedPackage = null;
let allPackages = [];
let isRemoteMode = false;
let currentRemoteModule = '';
let packageListMode = 'flat'; // 'flat' or 'tree'
let packageListMode = 'tree'; // 'flat' or 'tree'
let expandedNodes = new Set(); // Track expanded tree nodes
// Pan and Zoom state
@ -94,9 +94,11 @@ async function init() {
theme.init();
initPanZoom();
initRemoteModuleInput();
await loadModuleName();
await loadPackages();
await refresh();
const hasLocalModule = await loadModuleName();
if (hasLocalModule) {
await loadPackages();
await refresh();
}
}
// Initialize remote module input
@ -376,21 +378,28 @@ function showZoomIndicator() {
}
// Load module name from server
// Returns true if local module exists, false otherwise
async function loadModuleName() {
try {
const response = await fetch('/api/module');
const data = await response.json();
document.getElementById('moduleName').textContent = data.name || '';
// If no local module, set default value for remote module input
// If no local module, set default value and auto-analyze
if (!data.name) {
const input = document.getElementById('remoteModuleInput');
if (input && !input.value) {
input.value = 'github.com/gogf/gf/v2';
// Fetch versions first, then auto-analyze
await fetchVersions();
analyzeRemoteModule();
}
return false;
}
return true;
} catch (e) {
console.error('Failed to load module name:', e);
return false;
}
}
@ -566,14 +575,14 @@ function toggleTreeNode(path) {
// Re-render with current filter
const query = document.getElementById('searchInput').value.toLowerCase();
const filtered = query ? allPackages.filter(pkg => pkg.toLowerCase().includes(query)) : allPackages;
const filtered = query ? allPackages.filter(pkg => getPkgName(pkg).toLowerCase().includes(query)) : allPackages;
renderPackageList(filtered);
}
// Filter packages by search query
function filterPackages() {
const query = document.getElementById('searchInput').value.toLowerCase();
const filtered = allPackages.filter(pkg => pkg.toLowerCase().includes(query));
const filtered = allPackages.filter(pkg => getPkgName(pkg).toLowerCase().includes(query));
renderPackageList(filtered);
}

View File

@ -87,8 +87,8 @@
<span class="count" id="packageCount">0</span>
</div>
<div class="sidebar-mode">
<button class="mode-btn active" id="modeFlat" onclick="setPackageListMode('flat')" title="Flat list"></button>
<button class="mode-btn" id="modeTree" onclick="setPackageListMode('tree')" title="Tree view">🌲</button>
<button class="mode-btn" id="modeFlat" onclick="setPackageListMode('flat')" title="Flat list"></button>
<button class="mode-btn active" id="modeTree" onclick="setPackageListMode('tree')" title="Tree view">🌲</button>
</div>
</div>
<div class="search-box">

View File

@ -112,7 +112,7 @@ body {
}
.remote-input-group input[type="text"] {
width: 260px;
width: 320px;
background: var(--input-bg);
border: 1px solid var(--input-border);
color: var(--text-primary);
@ -128,7 +128,7 @@ body {
}
.remote-input-group select {
width: 120px;
width: 160px;
background: var(--input-bg);
border: 1px solid var(--input-border);
color: var(--text-primary);