From 0ffc05c7f68c0b0e191aa267efa4da2c22492840 Mon Sep 17 00:00:00 2001 From: Oleg Samarin Date: Wed, 15 Apr 2026 22:55:15 +0300 Subject: [PATCH] Fixed markdown preview not rendering images Added support for ![alt](src) image syntax in MarkdownRenderer: added image group to the INLINE regex (before links, so ![ is not consumed by the link branch), emitted tags, and added a createOutputPane(html, baseDirPath) overload that sets the HTMLDocument base URL so relative image paths resolve correctly. openLive() now passes the file's parent directory as the base. Resolves: #48 Co-Authored-By: Claude Sonnet 4.6 --- CHANGELOG.md | 1 + docs/to-do.md | 4 +-- pom.xml | 2 +- .../ui/MarkdownPreviewTab.java | 2 +- .../ui/common/MarkdownRenderer.java | 32 +++++++++++++++++-- .../ui/common/MarkdownRendererTest.java | 29 +++++++++++++++++ 6 files changed, 63 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fdd16d9..82edf5cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- Fixed markdown preview not rendering images (`![alt](path)` syntax) - Fixed session tab blocking resize below ~600px when docked as a side panel (https://github.com/nbclaudecodegui/NetbeansClaudeCodeGui/issues/19) - Fixed terminal appearing blank after dismissing the model selection menu - Fixed MCP connection to localhost being routed through an active HTTP_PROXY/HTTPS_PROXY; NO_PROXY is now automatically extended with localhost and 127.0.0.1 when a proxy is configured diff --git a/docs/to-do.md b/docs/to-do.md index eadc7e77..c23e8a7e 100644 --- a/docs/to-do.md +++ b/docs/to-do.md @@ -2,11 +2,11 @@ - [x] **Tooltip "Stop session" → "Close session"** — `ClaudeSessionTab.java:229`: переименовать tooltip кнопки `stopButton` с "Stop session" на "Close session" (кнопка закрывает сессию, а не останавливает процесс). - [x] **IDE restore не сохраняет режим Resume specific** — `ClaudeSessionTab.writeExternal()` сохраняет значение настройки `contextMenuSessionMode` вместо фактического режима/resumeId, с которым была открыта сессия. Если сессия была открыта через "Resume specific", после рестарта IDE она восстановится в режиме Continue last (или New), а не возобновит ту же конкретную сессию. -- [ ] Drag and drop in a text field: вставлять в текущую позицию курсора, а не +- [x] Drag and drop in a text field: вставлять в текущую позицию курсора, а не сдвигать позицию курсора. Рднако, возможность перемещения позиции курсора кликом должна быть созранена - [x] Невозможно выйти из /usage по кнопке cancel. Отладить - [x] Не учитываются цвета Look and Feel для кнопок -- [ ] Previe markdown: не показываются изображения +- [x] Previe markdown: не показываются изображения - [ ] Previe markdown: не работает навигация по линкам внутри файла # Features diff --git a/pom.xml b/pom.xml index 2b676dee..2797a16a 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.github.nbclaudecodegui netbeans-claude-code-gui - 0.21.49-SNAPSHOT + 0.21.50-SNAPSHOT nbm Netbeans Claude Code GUI NetBeans plugin that provides a GUI for Claude Code CLI — run claude code sessions directly inside the IDE. diff --git a/src/main/java/io/github/nbclaudecodegui/ui/MarkdownPreviewTab.java b/src/main/java/io/github/nbclaudecodegui/ui/MarkdownPreviewTab.java index 1e946dea..777f8fb5 100644 --- a/src/main/java/io/github/nbclaudecodegui/ui/MarkdownPreviewTab.java +++ b/src/main/java/io/github/nbclaudecodegui/ui/MarkdownPreviewTab.java @@ -134,7 +134,7 @@ public static void openLive(String filePath, String content, FileObject fo) { tab.fileObject = fo; String html = MarkdownRenderer.toHtml(content != null ? content : ""); - tab.pane = MarkdownRenderer.createOutputPane(html); + tab.pane = MarkdownRenderer.createOutputPane(html, new java.io.File(filePath).getParent()); String name = new java.io.File(filePath).getName(); tab.setDisplayName("Preview: " + name); diff --git a/src/main/java/io/github/nbclaudecodegui/ui/common/MarkdownRenderer.java b/src/main/java/io/github/nbclaudecodegui/ui/common/MarkdownRenderer.java index 246b6f09..90d16086 100644 --- a/src/main/java/io/github/nbclaudecodegui/ui/common/MarkdownRenderer.java +++ b/src/main/java/io/github/nbclaudecodegui/ui/common/MarkdownRenderer.java @@ -42,7 +42,8 @@ public final class MarkdownRenderer { + "|\\*\\*(.+?)\\*\\*" // group 2: bold + "|\\*([^*\\n]+)\\*" // group 3: italic + "|`([^`]+)`" // group 4: inline code - + "|\\[([^\\]]+)\\]\\(([^)]+)\\)", // group 5: link text, group 6: link url + + "|!\\[([^\\]]*)\\]\\(([^)]+)\\)" // group 5: image alt, group 6: image src + + "|\\[([^\\]]+)\\]\\(([^)]+)\\)", // group 7: link text, group 8: link url Pattern.DOTALL); // ------------------------------------------------------------------------- @@ -64,6 +65,28 @@ public static JEditorPane createOutputPane(String html) { return pane; } + /** + * Creates and configures a {@link JEditorPane} with HTML content and a base directory for + * resolving relative image paths. + * + * @param html initial HTML content to display + * @param baseDirPath absolute path of the directory containing the rendered file, or null + * @return a ready-to-use output pane + */ + public static JEditorPane createOutputPane(String html, String baseDirPath) { + JEditorPane pane = createOutputPane(); + if (baseDirPath != null) { + try { + ((HTMLDocument) pane.getDocument()).setBase( + new java.net.URL("file://" + baseDirPath + "/")); + } catch (java.net.MalformedURLException ignored) { + // fall through — relative images won't resolve but nothing breaks + } + } + pane.setText(html); + return pane; + } + /** * Creates and configures a {@link JEditorPane} for rendering chat output with no initial content. * @@ -398,8 +421,11 @@ static String inlineToHtml(String text) { } else if (m.group(4) != null) { sb.append("").append(esc(m.group(4))).append(""); } else if (m.group(5) != null) { - sb.append("") - .append(esc(m.group(5))).append(""); + sb.append("\"").append(esc(m.group(5))).append("\""); + } else if (m.group(7) != null) { + sb.append("") + .append(esc(m.group(7))).append(""); } lastEnd = m.end(); } diff --git a/src/test/java/io/github/nbclaudecodegui/ui/common/MarkdownRendererTest.java b/src/test/java/io/github/nbclaudecodegui/ui/common/MarkdownRendererTest.java index 12870322..fe383eb4 100644 --- a/src/test/java/io/github/nbclaudecodegui/ui/common/MarkdownRendererTest.java +++ b/src/test/java/io/github/nbclaudecodegui/ui/common/MarkdownRendererTest.java @@ -208,4 +208,33 @@ void testTableFollowedByParagraph() { assertTrue(html.contains(""), "table present"); assertTrue(html.contains("Some text after"), "paragraph after table"); } + + // ------------------------------------------------------------------------- + // Bug 2: Images + // ------------------------------------------------------------------------- + + @Test + void testInlineImage() { + String result = MarkdownRenderer.inlineToHtml("![alt text](image.png)"); + assertTrue(result.contains("