@@ -261,3 +261,119 @@ func getTopSections(sections []*parser.Section, maxDepth int) []*parser.Section
261261 }
262262 return result
263263}
264+
265+ // RefsTree renders document references (links to other .md files)
266+ func RefsTree (docs []* parser.Document , dirName string ) {
267+ // Build reference graph
268+ type RefInfo struct {
269+ From string
270+ To string
271+ Text string
272+ Line int
273+ }
274+
275+ var allRefs []RefInfo
276+ fileRefs := make (map [string ][]string ) // file -> files it references
277+ fileRefBy := make (map [string ][]string ) // file -> files that reference it
278+
279+ for _ , doc := range docs {
280+ for _ , ref := range doc .References {
281+ allRefs = append (allRefs , RefInfo {
282+ From : doc .Filename ,
283+ To : ref .Target ,
284+ Text : ref .Text ,
285+ Line : ref .Line ,
286+ })
287+ fileRefs [doc .Filename ] = append (fileRefs [doc .Filename ], ref .Target )
288+ fileRefBy [ref .Target ] = append (fileRefBy [ref .Target ], doc .Filename )
289+ }
290+ }
291+
292+ if len (allRefs ) == 0 {
293+ fmt .Println ("No markdown cross-references found" )
294+ return
295+ }
296+
297+ // Header
298+ innerWidth := 60
299+ titleLine := fmt .Sprintf (" %s/ " , dirName )
300+ padding := innerWidth - len (titleLine )
301+ leftPad := padding / 2
302+ rightPad := padding - leftPad
303+ fmt .Printf ("╭%s%s%s╮\n " , strings .Repeat ("─" , leftPad ), titleLine , strings .Repeat ("─" , rightPad ))
304+
305+ info := fmt .Sprintf ("References: %d links between docs" , len (allRefs ))
306+ fmt .Printf ("│ %-*s │\n " , innerWidth - 2 , centerText (info , innerWidth - 2 ))
307+ fmt .Printf ("╰%s╯\n " , strings .Repeat ("─" , innerWidth ))
308+ fmt .Println ()
309+
310+ // Find hubs (most referenced files)
311+ type hub struct {
312+ file string
313+ count int
314+ }
315+ var hubs []hub
316+ for file , refs := range fileRefBy {
317+ if len (refs ) >= 2 {
318+ hubs = append (hubs , hub {file , len (refs )})
319+ }
320+ }
321+
322+ if len (hubs ) > 0 {
323+ // Sort by count descending
324+ for i := 0 ; i < len (hubs ); i ++ {
325+ for j := i + 1 ; j < len (hubs ); j ++ {
326+ if hubs [j ].count > hubs [i ].count {
327+ hubs [i ], hubs [j ] = hubs [j ], hubs [i ]
328+ }
329+ }
330+ }
331+
332+ fmt .Printf ("%sHUBS:%s " , bold , reset )
333+ var hubStrs []string
334+ for _ , h := range hubs {
335+ if len (hubStrs ) >= 5 {
336+ break
337+ }
338+ hubStrs = append (hubStrs , fmt .Sprintf ("%s%s%s (%d←)" , green , h .file , reset , h .count ))
339+ }
340+ fmt .Println (strings .Join (hubStrs , ", " ))
341+ fmt .Println ()
342+ }
343+
344+ // Show reference flow by file
345+ fmt .Printf ("%sReference Flow:%s\n " , bold + cyan , reset )
346+ fmt .Println ()
347+
348+ // Group by source file
349+ printed := make (map [string ]bool )
350+ for _ , doc := range docs {
351+ if len (doc .References ) == 0 {
352+ continue
353+ }
354+ if printed [doc .Filename ] {
355+ continue
356+ }
357+ printed [doc .Filename ] = true
358+
359+ // Dedupe targets
360+ seen := make (map [string ]bool )
361+ var targets []string
362+ for _ , ref := range doc .References {
363+ if ! seen [ref .Target ] {
364+ targets = append (targets , ref .Target )
365+ seen [ref .Target ] = true
366+ }
367+ }
368+
369+ fmt .Printf (" %s%s%s\n " , bold , doc .Filename , reset )
370+ for i , target := range targets {
371+ connector := "├──▶ "
372+ if i == len (targets )- 1 {
373+ connector = "└──▶ "
374+ }
375+ fmt .Printf (" %s%s%s%s\n " , dim , connector , reset , target )
376+ }
377+ fmt .Println ()
378+ }
379+ }
0 commit comments