Changement des ilink vers markdown pur
This commit is contained in:
@ -114,6 +114,10 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
h.handleFavorites(w, r)
|
||||
return
|
||||
}
|
||||
if strings.HasPrefix(path, "/api/folder/") {
|
||||
h.handleFolderView(w, r)
|
||||
return
|
||||
}
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
|
||||
@ -278,15 +282,17 @@ func (h *Handler) handleHome(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// Utiliser le template editor.html pour afficher la page d'accueil
|
||||
data := struct {
|
||||
Filename string
|
||||
Content string
|
||||
IsHome bool
|
||||
Backlinks []BacklinkInfo
|
||||
Filename string
|
||||
Content string
|
||||
IsHome bool
|
||||
Backlinks []BacklinkInfo
|
||||
Breadcrumb template.HTML
|
||||
}{
|
||||
Filename: "🏠 Accueil - Index",
|
||||
Content: content,
|
||||
IsHome: true,
|
||||
Backlinks: nil, // Pas de backlinks pour la page d'accueil
|
||||
Filename: "🏠 Accueil - Index",
|
||||
Content: content,
|
||||
IsHome: true,
|
||||
Backlinks: nil, // Pas de backlinks pour la page d'accueil
|
||||
Breadcrumb: h.generateBreadcrumb(""),
|
||||
}
|
||||
|
||||
err := h.templates.ExecuteTemplate(w, "editor.html", data)
|
||||
@ -338,12 +344,19 @@ func (h *Handler) generateHomeMarkdown() string {
|
||||
// Section des notes récemment modifiées (après les favoris)
|
||||
h.generateRecentNotesSection(&sb)
|
||||
|
||||
// Titre de l'arborescence avec le nombre de notes
|
||||
sb.WriteString(fmt.Sprintf("## 📂 Toutes les notes (%d)\n\n", noteCount))
|
||||
// Section de toutes les notes avec accordéon
|
||||
sb.WriteString("<div class=\"home-section\">\n")
|
||||
sb.WriteString(" <div class=\"home-section-header\" onclick=\"toggleFolder('all-notes')\">\n")
|
||||
sb.WriteString(fmt.Sprintf(" <h2 class=\"home-section-title\">📂 Toutes les notes (%d)</h2>\n", noteCount))
|
||||
sb.WriteString(" </div>\n")
|
||||
sb.WriteString(" <div class=\"home-section-content\" id=\"folder-all-notes\">\n")
|
||||
|
||||
// Générer l'arborescence en Markdown
|
||||
h.generateMarkdownTree(&sb, tree, 0)
|
||||
|
||||
sb.WriteString(" </div>\n")
|
||||
sb.WriteString("</div>\n")
|
||||
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
@ -355,18 +368,24 @@ func (h *Handler) generateTagsSection(sb *strings.Builder) {
|
||||
return
|
||||
}
|
||||
|
||||
sb.WriteString("## 🏷️ Tags\n\n")
|
||||
sb.WriteString("<div class=\"tags-cloud\">\n")
|
||||
sb.WriteString("<div class=\"home-section\">\n")
|
||||
sb.WriteString(" <div class=\"home-section-header\" onclick=\"toggleFolder('tags')\">\n")
|
||||
sb.WriteString(" <h2 class=\"home-section-title\">🏷️ Tags</h2>\n")
|
||||
sb.WriteString(" </div>\n")
|
||||
sb.WriteString(" <div class=\"home-section-content\" id=\"folder-tags\">\n")
|
||||
sb.WriteString(" <div class=\"tags-cloud\">\n")
|
||||
|
||||
for _, tc := range tags {
|
||||
// Créer un lien HTML discret et fonctionnel
|
||||
sb.WriteString(fmt.Sprintf(
|
||||
`<a href="#" class="tag-item" hx-get="/api/search?query=tag:%s" hx-target="#search-results" hx-swap="innerHTML"><kbd class="tag-badge">#%s</kbd> <mark class="tag-count">%d</mark></a>`,
|
||||
` <a href="#" class="tag-item" hx-get="/api/search?query=tag:%s" hx-target="#search-results" hx-swap="innerHTML"><kbd class="tag-badge">#%s</kbd> <mark class="tag-count">%d</mark></a>`,
|
||||
tc.Tag, tc.Tag, tc.Count,
|
||||
))
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
|
||||
sb.WriteString(" </div>\n")
|
||||
sb.WriteString(" </div>\n")
|
||||
sb.WriteString("</div>\n\n")
|
||||
}
|
||||
|
||||
@ -377,36 +396,42 @@ func (h *Handler) generateFavoritesSection(sb *strings.Builder) {
|
||||
return
|
||||
}
|
||||
|
||||
sb.WriteString("## ⭐ Favoris\n\n")
|
||||
sb.WriteString("<div class=\"note-tree favorites-tree\">\n")
|
||||
sb.WriteString("<div class=\"home-section\">\n")
|
||||
sb.WriteString(" <div class=\"home-section-header\" onclick=\"toggleFolder('favorites')\">\n")
|
||||
sb.WriteString(" <h2 class=\"home-section-title\">⭐ Favoris</h2>\n")
|
||||
sb.WriteString(" </div>\n")
|
||||
sb.WriteString(" <div class=\"home-section-content\" id=\"folder-favorites\">\n")
|
||||
sb.WriteString(" <div class=\"note-tree favorites-tree\">\n")
|
||||
|
||||
for _, fav := range favorites.Items {
|
||||
safeID := "fav-" + strings.ReplaceAll(strings.ReplaceAll(fav.Path, "/", "-"), "\\", "-")
|
||||
|
||||
|
||||
if fav.IsDir {
|
||||
// Dossier - avec accordéon
|
||||
sb.WriteString(fmt.Sprintf(" <div class=\"folder indent-level-1\">\n"))
|
||||
sb.WriteString(fmt.Sprintf(" <div class=\"folder-header\" onclick=\"toggleFolder('%s')\">\n", safeID))
|
||||
sb.WriteString(fmt.Sprintf(" <span class=\"folder-icon\" id=\"icon-%s\">📁</span>\n", safeID))
|
||||
sb.WriteString(fmt.Sprintf(" <strong>%s</strong>\n", fav.Title))
|
||||
sb.WriteString(fmt.Sprintf(" </div>\n"))
|
||||
sb.WriteString(fmt.Sprintf(" <div class=\"folder-content\" id=\"folder-%s\">\n", safeID))
|
||||
|
||||
sb.WriteString(fmt.Sprintf(" <div class=\"folder indent-level-1\">\n"))
|
||||
sb.WriteString(fmt.Sprintf(" <div class=\"folder-header\" onclick=\"toggleFolder('%s')\">\n", safeID))
|
||||
sb.WriteString(fmt.Sprintf(" <span class=\"folder-icon\" id=\"icon-%s\">📁</span>\n", safeID))
|
||||
sb.WriteString(fmt.Sprintf(" <strong>%s</strong>\n", fav.Title))
|
||||
sb.WriteString(fmt.Sprintf(" </div>\n"))
|
||||
sb.WriteString(fmt.Sprintf(" <div class=\"folder-content\" id=\"folder-%s\">\n", safeID))
|
||||
|
||||
// Lister le contenu du dossier
|
||||
h.generateFavoriteFolderContent(sb, fav.Path, 2)
|
||||
|
||||
sb.WriteString(fmt.Sprintf(" </div>\n"))
|
||||
sb.WriteString(fmt.Sprintf(" </div>\n"))
|
||||
h.generateFavoriteFolderContent(sb, fav.Path, 3)
|
||||
|
||||
sb.WriteString(fmt.Sprintf(" </div>\n"))
|
||||
sb.WriteString(fmt.Sprintf(" </div>\n"))
|
||||
} else {
|
||||
// Fichier
|
||||
sb.WriteString(fmt.Sprintf(" <div class=\"file indent-level-1\">\n"))
|
||||
sb.WriteString(fmt.Sprintf(" <a href=\"#\" hx-get=\"/api/notes/%s\" hx-target=\"#editor-container\" hx-swap=\"innerHTML\">", fav.Path))
|
||||
sb.WriteString(fmt.Sprintf(" <div class=\"file indent-level-1\">\n"))
|
||||
sb.WriteString(fmt.Sprintf(" <a href=\"#\" hx-get=\"/api/notes/%s\" hx-target=\"#editor-container\" hx-swap=\"innerHTML\">", fav.Path))
|
||||
sb.WriteString(fmt.Sprintf("📄 %s", fav.Title))
|
||||
sb.WriteString("</a>\n")
|
||||
sb.WriteString(fmt.Sprintf(" </div>\n"))
|
||||
sb.WriteString(fmt.Sprintf(" </div>\n"))
|
||||
}
|
||||
}
|
||||
|
||||
sb.WriteString(" </div>\n")
|
||||
sb.WriteString(" </div>\n")
|
||||
sb.WriteString("</div>\n\n")
|
||||
}
|
||||
|
||||
@ -418,8 +443,12 @@ func (h *Handler) generateRecentNotesSection(sb *strings.Builder) {
|
||||
return
|
||||
}
|
||||
|
||||
sb.WriteString("## 🕒 Récemment modifiés\n\n")
|
||||
sb.WriteString("<div class=\"recent-notes-container\">\n")
|
||||
sb.WriteString("<div class=\"home-section\">\n")
|
||||
sb.WriteString(" <div class=\"home-section-header\" onclick=\"toggleFolder('recent')\">\n")
|
||||
sb.WriteString(" <h2 class=\"home-section-title\">🕒 Récemment modifiés</h2>\n")
|
||||
sb.WriteString(" </div>\n")
|
||||
sb.WriteString(" <div class=\"home-section-content\" id=\"folder-recent\">\n")
|
||||
sb.WriteString(" <div class=\"recent-notes-container\">\n")
|
||||
|
||||
for _, doc := range recentDocs {
|
||||
// Extraire les premières lignes du corps (max 150 caractères)
|
||||
@ -434,13 +463,13 @@ func (h *Handler) generateRecentNotesSection(sb *strings.Builder) {
|
||||
dateStr = doc.Date
|
||||
}
|
||||
|
||||
sb.WriteString(" <div class=\"recent-note-card\">\n")
|
||||
sb.WriteString(fmt.Sprintf(" <a href=\"#\" class=\"recent-note-link\" hx-get=\"/api/notes/%s\" hx-target=\"#editor-container\" hx-swap=\"innerHTML\">\n", doc.Path))
|
||||
sb.WriteString(fmt.Sprintf(" <div class=\"recent-note-title\">%s</div>\n", doc.Title))
|
||||
sb.WriteString(fmt.Sprintf(" <div class=\"recent-note-meta\">\n"))
|
||||
sb.WriteString(fmt.Sprintf(" <span class=\"recent-note-date\">📅 %s</span>\n", dateStr))
|
||||
sb.WriteString(" <div class=\"recent-note-card\">\n")
|
||||
sb.WriteString(fmt.Sprintf(" <a href=\"#\" class=\"recent-note-link\" hx-get=\"/api/notes/%s\" hx-target=\"#editor-container\" hx-swap=\"innerHTML\">\n", doc.Path))
|
||||
sb.WriteString(fmt.Sprintf(" <div class=\"recent-note-title\">%s</div>\n", doc.Title))
|
||||
sb.WriteString(fmt.Sprintf(" <div class=\"recent-note-meta\">\n"))
|
||||
sb.WriteString(fmt.Sprintf(" <span class=\"recent-note-date\">📅 %s</span>\n", dateStr))
|
||||
if len(doc.Tags) > 0 {
|
||||
sb.WriteString(fmt.Sprintf(" <span class=\"recent-note-tags\">"))
|
||||
sb.WriteString(fmt.Sprintf(" <span class=\"recent-note-tags\">"))
|
||||
for i, tag := range doc.Tags {
|
||||
if i > 0 {
|
||||
sb.WriteString(" ")
|
||||
@ -449,14 +478,16 @@ func (h *Handler) generateRecentNotesSection(sb *strings.Builder) {
|
||||
}
|
||||
sb.WriteString("</span>\n")
|
||||
}
|
||||
sb.WriteString(" </div>\n")
|
||||
sb.WriteString(" </div>\n")
|
||||
if preview != "" {
|
||||
sb.WriteString(fmt.Sprintf(" <div class=\"recent-note-preview\">%s</div>\n", preview))
|
||||
sb.WriteString(fmt.Sprintf(" <div class=\"recent-note-preview\">%s</div>\n", preview))
|
||||
}
|
||||
sb.WriteString(" </a>\n")
|
||||
sb.WriteString(" </div>\n")
|
||||
sb.WriteString(" </a>\n")
|
||||
sb.WriteString(" </div>\n")
|
||||
}
|
||||
|
||||
sb.WriteString(" </div>\n")
|
||||
sb.WriteString(" </div>\n")
|
||||
sb.WriteString("</div>\n\n")
|
||||
}
|
||||
|
||||
@ -788,15 +819,17 @@ func (h *Handler) handleGetNote(w http.ResponseWriter, r *http.Request, filename
|
||||
backlinkData := h.buildBacklinkData(backlinks)
|
||||
|
||||
data := struct {
|
||||
Filename string
|
||||
Content string
|
||||
IsHome bool
|
||||
Backlinks []BacklinkInfo
|
||||
Filename string
|
||||
Content string
|
||||
IsHome bool
|
||||
Backlinks []BacklinkInfo
|
||||
Breadcrumb template.HTML
|
||||
}{
|
||||
Filename: filename,
|
||||
Content: string(content),
|
||||
IsHome: false,
|
||||
Backlinks: backlinkData,
|
||||
Filename: filename,
|
||||
Content: string(content),
|
||||
IsHome: false,
|
||||
Backlinks: backlinkData,
|
||||
Breadcrumb: h.generateBreadcrumb(filename),
|
||||
}
|
||||
|
||||
err = h.templates.ExecuteTemplate(w, "editor.html", data)
|
||||
@ -1264,3 +1297,190 @@ func (h *Handler) buildBacklinkData(paths []string) []BacklinkInfo {
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// handleFolderView affiche le contenu d'un dossier
|
||||
func (h *Handler) handleFolderView(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
http.Error(w, "Méthode non autorisée", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
// Si ce n'est pas une requête HTMX, rediriger vers la page principale
|
||||
if r.Header.Get("HX-Request") == "" {
|
||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
|
||||
// Extraire le chemin du dossier depuis l'URL
|
||||
folderPath := strings.TrimPrefix(r.URL.Path, "/api/folder/")
|
||||
folderPath = strings.TrimPrefix(folderPath, "/")
|
||||
|
||||
// Sécurité : vérifier le chemin
|
||||
cleanPath := filepath.Clean(folderPath)
|
||||
if strings.HasPrefix(cleanPath, "..") || filepath.IsAbs(cleanPath) {
|
||||
http.Error(w, "Chemin invalide", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Construire le chemin absolu
|
||||
absPath := filepath.Join(h.notesDir, cleanPath)
|
||||
|
||||
// Vérifier que c'est bien un dossier
|
||||
info, err := os.Stat(absPath)
|
||||
if err != nil || !info.IsDir() {
|
||||
http.Error(w, "Dossier non trouvé", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
// Générer le contenu de la page
|
||||
content := h.generateFolderViewMarkdown(cleanPath)
|
||||
|
||||
// Utiliser le template editor.html
|
||||
data := struct {
|
||||
Filename string
|
||||
Content string
|
||||
IsHome bool
|
||||
Backlinks []BacklinkInfo
|
||||
Breadcrumb template.HTML
|
||||
}{
|
||||
Filename: cleanPath,
|
||||
Content: content,
|
||||
IsHome: true, // Pas d'édition pour une vue de dossier
|
||||
Backlinks: nil,
|
||||
Breadcrumb: h.generateBreadcrumb(cleanPath),
|
||||
}
|
||||
|
||||
err = h.templates.ExecuteTemplate(w, "editor.html", data)
|
||||
if err != nil {
|
||||
h.logger.Printf("Erreur d'exécution du template folder view: %v", err)
|
||||
http.Error(w, "Erreur interne", http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
// generateBreadcrumb génère un fil d'Ariane HTML cliquable
|
||||
func (h *Handler) generateBreadcrumb(path string) template.HTML {
|
||||
if path == "" {
|
||||
return template.HTML(`<strong>📁 Racine</strong>`)
|
||||
}
|
||||
|
||||
parts := strings.Split(filepath.ToSlash(path), "/")
|
||||
var sb strings.Builder
|
||||
|
||||
sb.WriteString(`<span class="breadcrumb">`)
|
||||
|
||||
// Lien racine
|
||||
sb.WriteString(`<a href="#" hx-get="/api/home" hx-target="#editor-container" hx-swap="innerHTML" hx-push-url="true" class="breadcrumb-link">📁 Racine</a>`)
|
||||
|
||||
// Construire les liens pour chaque partie
|
||||
currentPath := ""
|
||||
for i, part := range parts {
|
||||
sb.WriteString(` <span class="breadcrumb-separator">›</span> `)
|
||||
|
||||
if currentPath == "" {
|
||||
currentPath = part
|
||||
} else {
|
||||
currentPath = currentPath + "/" + part
|
||||
}
|
||||
|
||||
// Le dernier élément (fichier) n'est pas cliquable
|
||||
if i == len(parts)-1 && strings.HasSuffix(part, ".md") {
|
||||
// C'est un fichier, pas cliquable
|
||||
displayName := strings.TrimSuffix(part, ".md")
|
||||
sb.WriteString(fmt.Sprintf(`<strong>%s</strong>`, displayName))
|
||||
} else {
|
||||
// C'est un dossier, cliquable
|
||||
sb.WriteString(fmt.Sprintf(
|
||||
`<a href="#" hx-get="/api/folder/%s" hx-target="#editor-container" hx-swap="innerHTML" hx-push-url="true" class="breadcrumb-link">📂 %s</a>`,
|
||||
currentPath, part,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
sb.WriteString(`</span>`)
|
||||
return template.HTML(sb.String())
|
||||
}
|
||||
|
||||
// generateFolderViewMarkdown génère le contenu Markdown pour l'affichage d'un dossier
|
||||
func (h *Handler) generateFolderViewMarkdown(folderPath string) string {
|
||||
var sb strings.Builder
|
||||
|
||||
// En-tête
|
||||
if folderPath == "" {
|
||||
sb.WriteString("# 📁 Racine\n\n")
|
||||
} else {
|
||||
folderName := filepath.Base(folderPath)
|
||||
sb.WriteString(fmt.Sprintf("# 📂 %s\n\n", folderName))
|
||||
}
|
||||
|
||||
sb.WriteString("_Contenu du dossier_\n\n")
|
||||
|
||||
// Lister le contenu
|
||||
absPath := filepath.Join(h.notesDir, folderPath)
|
||||
entries, err := os.ReadDir(absPath)
|
||||
if err != nil {
|
||||
sb.WriteString("❌ Erreur lors de la lecture du dossier\n")
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// Séparer dossiers et fichiers
|
||||
var folders []os.DirEntry
|
||||
var files []os.DirEntry
|
||||
|
||||
for _, entry := range entries {
|
||||
// Ignorer les fichiers cachés
|
||||
if strings.HasPrefix(entry.Name(), ".") {
|
||||
continue
|
||||
}
|
||||
|
||||
if entry.IsDir() {
|
||||
folders = append(folders, entry)
|
||||
} else if strings.HasSuffix(entry.Name(), ".md") {
|
||||
files = append(files, entry)
|
||||
}
|
||||
}
|
||||
|
||||
// Afficher les dossiers
|
||||
if len(folders) > 0 {
|
||||
sb.WriteString("## 📁 Dossiers\n\n")
|
||||
sb.WriteString("<div class=\"folder-list\">\n")
|
||||
for _, folder := range folders {
|
||||
subPath := filepath.Join(folderPath, folder.Name())
|
||||
sb.WriteString(fmt.Sprintf(
|
||||
`<div class="folder-item"><a href="#" hx-get="/api/folder/%s" hx-target="#editor-container" hx-swap="innerHTML" hx-push-url="true">📂 %s</a></div>`,
|
||||
filepath.ToSlash(subPath), folder.Name(),
|
||||
))
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
sb.WriteString("</div>\n\n")
|
||||
}
|
||||
|
||||
// Afficher les fichiers
|
||||
if len(files) > 0 {
|
||||
sb.WriteString(fmt.Sprintf("## 📄 Notes (%d)\n\n", len(files)))
|
||||
sb.WriteString("<div class=\"file-list\">\n")
|
||||
for _, file := range files {
|
||||
filePath := filepath.Join(folderPath, file.Name())
|
||||
displayName := strings.TrimSuffix(file.Name(), ".md")
|
||||
|
||||
// Lire le titre du front matter si possible
|
||||
fullPath := filepath.Join(h.notesDir, filePath)
|
||||
fm, _, err := indexer.ExtractFrontMatterAndBody(fullPath)
|
||||
if err == nil && fm.Title != "" {
|
||||
displayName = fm.Title
|
||||
}
|
||||
|
||||
sb.WriteString(fmt.Sprintf(
|
||||
`<div class="file-item"><a href="#" hx-get="/api/notes/%s" hx-target="#editor-container" hx-swap="innerHTML" hx-push-url="true">📄 %s</a></div>`,
|
||||
filepath.ToSlash(filePath), displayName,
|
||||
))
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
sb.WriteString("</div>\n\n")
|
||||
}
|
||||
|
||||
if len(folders) == 0 && len(files) == 0 {
|
||||
sb.WriteString("_Ce dossier est vide_\n")
|
||||
}
|
||||
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
@ -33,6 +33,7 @@ type Document struct {
|
||||
LastModified string
|
||||
Body string
|
||||
Summary string
|
||||
Links []string // Liens Markdown vers d'autres notes
|
||||
|
||||
lowerTitle string
|
||||
lowerBody string
|
||||
@ -115,11 +116,11 @@ func (i *Indexer) Load(root string) error {
|
||||
indexed[tag] = list
|
||||
}
|
||||
|
||||
// Build backlinks index
|
||||
// Build backlinks index from Markdown links
|
||||
backlinksMap := make(map[string][]string)
|
||||
for sourcePath, doc := range documents {
|
||||
links := extractInternalLinks(doc.Body)
|
||||
for _, targetPath := range links {
|
||||
// Use the Links field which contains extracted Markdown links
|
||||
for _, targetPath := range doc.Links {
|
||||
// Add sourcePath to the backlinks of targetPath
|
||||
if _, ok := backlinksMap[targetPath]; !ok {
|
||||
backlinksMap[targetPath] = make([]string, 0)
|
||||
@ -169,6 +170,45 @@ func normalizeTags(tags []string) []string {
|
||||
return result
|
||||
}
|
||||
|
||||
// extractMarkdownLinks extrait tous les liens Markdown du body
|
||||
// Format : [texte](chemin/vers/note.md)
|
||||
// Retourne une liste de chemins vers d'autres notes
|
||||
func extractMarkdownLinks(body string) []string {
|
||||
// Regex pour capturer [texte](chemin.md)
|
||||
// Groupe 1 : texte du lien, Groupe 2 : chemin
|
||||
re := regexp.MustCompile(`\[([^\]]+)\]\(([^)]+\.md)\)`)
|
||||
matches := re.FindAllStringSubmatch(body, -1)
|
||||
|
||||
links := make([]string, 0, len(matches))
|
||||
seen := make(map[string]bool) // Éviter les doublons
|
||||
|
||||
for _, match := range matches {
|
||||
if len(match) < 3 {
|
||||
continue
|
||||
}
|
||||
|
||||
linkPath := strings.TrimSpace(match[2])
|
||||
|
||||
// Ignorer les URLs absolues (http://, https://, //)
|
||||
if strings.HasPrefix(linkPath, "http://") ||
|
||||
strings.HasPrefix(linkPath, "https://") ||
|
||||
strings.HasPrefix(linkPath, "//") {
|
||||
continue
|
||||
}
|
||||
|
||||
// Normaliser le chemin (convertir \ en / pour Windows)
|
||||
linkPath = filepath.ToSlash(linkPath)
|
||||
|
||||
// Éviter les doublons
|
||||
if !seen[linkPath] {
|
||||
seen[linkPath] = true
|
||||
links = append(links, linkPath)
|
||||
}
|
||||
}
|
||||
|
||||
return links
|
||||
}
|
||||
|
||||
func buildDocument(path string, fm FullFrontMatter, body string, tags []string) *Document {
|
||||
title := strings.TrimSpace(fm.Title)
|
||||
if title == "" {
|
||||
@ -176,6 +216,7 @@ func buildDocument(path string, fm FullFrontMatter, body string, tags []string)
|
||||
}
|
||||
|
||||
summary := buildSummary(body)
|
||||
links := extractMarkdownLinks(body)
|
||||
|
||||
lowerTags := make([]string, len(tags))
|
||||
for idx, tag := range tags {
|
||||
@ -190,6 +231,7 @@ func buildDocument(path string, fm FullFrontMatter, body string, tags []string)
|
||||
LastModified: strings.TrimSpace(fm.LastModified),
|
||||
Body: body,
|
||||
Summary: summary,
|
||||
Links: links,
|
||||
lowerTitle: strings.ToLower(title),
|
||||
lowerBody: strings.ToLower(body),
|
||||
lowerTags: lowerTags,
|
||||
|
||||
Reference in New Issue
Block a user