diff --git a/src/main/java/ui/PreviewPanel.java b/src/main/java/ui/PreviewPanel.java index c7321f8..54b0349 100644 --- a/src/main/java/ui/PreviewPanel.java +++ b/src/main/java/ui/PreviewPanel.java @@ -184,6 +184,18 @@ public PreviewPanel() { if (newState == Worker.State.SCHEDULED) { String location = webView.getEngine().getLocation(); if (location != null && !location.isEmpty() && !location.equals("about:blank")) { + // Ancre interne (#fragment) : défiler vers l'élément sans recharger la page + if (location.contains("#")) { + String fragment = location.substring(location.indexOf('#') + 1); + if (!fragment.isEmpty()) { + webView.getEngine().getLoadWorker().cancel(); + webView.getEngine().executeScript( + "var el = document.getElementById('" + fragment.replace("'", "\\'") + "');" + + "if (el) el.scrollIntoView({behavior: 'smooth', block: 'start'});" + ); + } + return; + } // Vérifier si c'est un lien vers un fichier .md if (location.toLowerCase().endsWith(".md") || location.toLowerCase().endsWith(".markdown")) { // Annuler la navigation @@ -267,6 +279,9 @@ private void updatePreview(String markdown, boolean addToHistory) { String html = htmlRenderer.render(markdownParser.parse(body)); + // ── IDs sur les titres : nécessaires pour la navigation par ancres (#fragment) + html = processHeadingIds(html); + // ── Checkboxes : convertir [ ] et [x] en éléments checkbox HTML html = processCheckboxes(html); @@ -527,6 +542,40 @@ private String loadResourceAsString(String resourcePath) { }); } + /** + * Injecte un attribut {@code id} GitHub-compatible sur chaque titre HTML ({@code

}–{@code

}) + * qui n'en possède pas encore. Requis pour que la navigation par ancres (#fragment) fonctionne. + */ + private String processHeadingIds(String html) { + Pattern p = Pattern.compile( + "<(h[1-6])(\\s[^>]*)?>(.+?)", + Pattern.CASE_INSENSITIVE | Pattern.DOTALL); + Matcher m = p.matcher(html); + StringBuffer sb = new StringBuffer(); + java.util.Map seen = new java.util.HashMap<>(); + while (m.find()) { + String tag = m.group(1); + String attrs = m.group(2) != null ? m.group(2) : ""; + String content = m.group(3); + if (attrs.contains("id=")) { + m.appendReplacement(sb, Matcher.quoteReplacement(m.group(0))); + continue; + } + String text = content.replaceAll("<[^>]+>", "").trim(); + String id = text.toLowerCase() + .replaceAll("[^\\w\\s-]", "") + .trim() + .replaceAll("\\s+", "-"); + int count = seen.getOrDefault(id, 0); + seen.put(id, count + 1); + String uid = count == 0 ? id : id + "-" + count; + m.appendReplacement(sb, Matcher.quoteReplacement( + "<" + tag + attrs + " id=\"" + uid + "\">" + content + "")); + } + m.appendTail(sb); + return sb.toString(); + } + /** * Convertit les marqueurs de checkbox Markdown ([ ] et [x]) en éléments HTML checkbox. *