Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
- Added Find bar (Ctrl+F / F3 / Shift+F3) to Markdown Preview
- Added automatic Markdown Preview tab when Claude writes a plan file (configurable in Settings → General)
- Added `show_markdown` MCP tool: displays markdown text in a NetBeans Markdown Preview tab
- Added `show_markdown_file` MCP tool: opens a live-updating Markdown Preview tab for a .md file on disk
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>io.github.nbclaudecodegui</groupId>
<artifactId>netbeans-claude-code-gui</artifactId>
<version>0.23.10-SNAPSHOT</version>
<version>0.23.12-SNAPSHOT</version>
<packaging>nbm</packaging>
<name>Netbeans Plugin Claude Code GUI</name>
<description>NetBeans plugin that provides a GUI for Claude Code CLI — run claude code sessions directly inside the IDE.</description>
Expand Down
54 changes: 51 additions & 3 deletions src/main/java/io/github/nbclaudecodegui/ui/MarkdownDiffPanel.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
package io.github.nbclaudecodegui.ui;

import io.github.nbclaudecodegui.ui.common.MarkdownRenderer;
import io.github.nbclaudecodegui.ui.common.markdown.MarkdownFindBar;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.JComponent;
import javax.swing.JEditorPane;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import org.openide.util.NbPreferences;

Expand Down Expand Up @@ -66,7 +73,18 @@ public MarkdownDiffPanel(String before, String after) {
beforeScroll = new JScrollPane(mdBeforePane);
afterScroll = new JScrollPane(mdAfterPane);

splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, beforeScroll, afterScroll);
MarkdownFindBar beforeFindBar = new MarkdownFindBar(mdBeforePane);
MarkdownFindBar afterFindBar = new MarkdownFindBar(mdAfterPane);

JPanel beforePanel = new JPanel(new BorderLayout());
beforePanel.add(beforeScroll, BorderLayout.CENTER);
beforePanel.add(beforeFindBar, BorderLayout.SOUTH);

JPanel afterPanel = new JPanel(new BorderLayout());
afterPanel.add(afterScroll, BorderLayout.CENTER);
afterPanel.add(afterFindBar, BorderLayout.SOUTH);

splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, beforePanel, afterPanel);
splitPane.setResizeWeight(0.5);

// restore divider
Expand All @@ -86,22 +104,52 @@ public MarkdownDiffPanel(String before, String after) {
add(splitPane, BorderLayout.CENTER);
wireScrollSync();

// Add "Hide" and "Pin Preview" to the existing popup menus on both editor panes
for (JEditorPane pane : new JEditorPane[]{mdBeforePane, mdAfterPane}) {
// Add "Find…", "Hide", and "Pin Preview" to the existing popup menus on both panes
for (int i = 0; i < 2; i++) {
JEditorPane pane = i == 0 ? mdBeforePane : mdAfterPane;
MarkdownFindBar fb = i == 0 ? beforeFindBar : afterFindBar;
JPopupMenu menu = pane.getComponentPopupMenu();
if (menu == null) { menu = new JPopupMenu(); pane.setComponentPopupMenu(menu); }
JMenuItem findItem = new JMenuItem("Find…");
findItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F, InputEvent.CTRL_DOWN_MASK));
findItem.addActionListener(e -> fb.gainFocus());
menu.add(findItem);
menu.addSeparator();
JMenuItem hideItem = new JMenuItem("Hide");
hideItem.addActionListener(e -> { if (onHide != null) onHide.run(); });
JMenuItem pinItem = new JMenuItem("Pin Preview");
pinItem.addActionListener(e -> { if (onPinPreview != null) onPinPreview.run(); });
menu.add(hideItem);
menu.add(pinItem);
bindFindBarKeys(pane, fb);
}
// Let right-click on the divider also show the menu
splitPane.setComponentPopupMenu(mdBeforePane.getComponentPopupMenu());
}

private static void bindFindBarKeys(JEditorPane pane, MarkdownFindBar fb) {
KeyStroke ctrlF = KeyStroke.getKeyStroke(KeyEvent.VK_F, InputEvent.CTRL_DOWN_MASK);
KeyStroke f3 = KeyStroke.getKeyStroke(KeyEvent.VK_F3, 0);
KeyStroke shiftF3 = KeyStroke.getKeyStroke(KeyEvent.VK_F3, InputEvent.SHIFT_DOWN_MASK);
pane.getInputMap(JComponent.WHEN_FOCUSED).put(ctrlF, "mddiff-find-show");
pane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(ctrlF, "mddiff-find-show");
pane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(f3, "mddiff-find-next");
pane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(shiftF3, "mddiff-find-prev");
pane.getActionMap().put("mddiff-find-show", new AbstractAction() {
@Override public void actionPerformed(ActionEvent e) { fb.gainFocus(); }
});
pane.getActionMap().put("mddiff-find-next", new AbstractAction() {
@Override public void actionPerformed(ActionEvent e) {
if (fb.isVisible()) fb.findNext(); else fb.searchAndFindNext();
}
});
pane.getActionMap().put("mddiff-find-prev", new AbstractAction() {
@Override public void actionPerformed(ActionEvent e) {
if (fb.isVisible()) fb.findPrev(); else fb.searchAndFindPrev();
}
});
}

private void wireScrollSync() {
beforeScroll.getViewport().addChangeListener(e -> {
if (syncActive) return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import io.github.nbclaudecodegui.settings.ClaudeCodePreferences;
import io.github.nbclaudecodegui.ui.common.MarkdownRenderer;
import io.github.nbclaudecodegui.ui.common.UiUtils;
import io.github.nbclaudecodegui.ui.common.markdown.MarkdownFindBar;
import java.awt.BorderLayout;
import java.awt.Toolkit;
import java.awt.datatransfer.StringSelection;
Expand Down Expand Up @@ -103,6 +104,9 @@ public class MarkdownPreviewTab extends TopComponent {
/** Snapshot of the href captured when the context menu opens; read by action listeners. */
String menuHref = null;

/** Find bar shown at the bottom of the preview. */
private MarkdownFindBar findBar;

/** File path saved by writeExternal; applied in componentOpened() after IDE restart. */
String savedFilePath = null;

Expand Down Expand Up @@ -156,10 +160,11 @@ public static void openLive(String filePath, String content, FileObject fo) {
tab.scrollPane = new JScrollPane(tab.pane);
tab.add(tab.scrollPane, BorderLayout.CENTER);

// Attach hyperlink listener and context menu
// Attach hyperlink listener, context menu, and find bar
tab.attachHyperlinkListener();
tab.bindRefreshKey(tab.pane);
tab.pane.setComponentPopupMenu(tab.buildContextMenu());
tab.initFindBar(tab.pane);

if (fo != null) {
tab.fileListener = makeFileListener(tab, fo);
Expand Down Expand Up @@ -207,6 +212,43 @@ public void updateContent(String markdown) {
});
}

private void initFindBar(JEditorPane p) {
findBar = new MarkdownFindBar(p);
add(findBar, BorderLayout.SOUTH);
bindFindBarKeys(p);
}

private void bindFindBarKeys(JEditorPane p) {
KeyStroke ctrlF = KeyStroke.getKeyStroke(KeyEvent.VK_F, InputEvent.CTRL_DOWN_MASK);
KeyStroke f3 = KeyStroke.getKeyStroke(KeyEvent.VK_F3, 0);
KeyStroke shiftF3 = KeyStroke.getKeyStroke(KeyEvent.VK_F3, InputEvent.SHIFT_DOWN_MASK);
// WHEN_FOCUSED overrides built-in JEditorPane Ctrl+F before NetBeans global action
p.getInputMap(JComponent.WHEN_FOCUSED).put(ctrlF, "md-find-show");
// WHEN_IN_FOCUSED_WINDOW covers F3/Shift+F3 from anywhere in the tab
p.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(ctrlF, "md-find-show");
p.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(f3, "md-find-next");
p.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(shiftF3, "md-find-prev");
p.getActionMap().put("md-find-show", new AbstractAction() {
@Override public void actionPerformed(ActionEvent e) { showFindBar(); }
});
p.getActionMap().put("md-find-next", new AbstractAction() {
@Override public void actionPerformed(ActionEvent e) {
if (findBar != null && findBar.isVisible()) findBar.findNext();
else if (findBar != null) findBar.searchAndFindNext();
}
});
p.getActionMap().put("md-find-prev", new AbstractAction() {
@Override public void actionPerformed(ActionEvent e) {
if (findBar != null && findBar.isVisible()) findBar.findPrev();
else if (findBar != null) findBar.searchAndFindPrev();
}
});
}

void showFindBar() {
if (findBar != null) findBar.gainFocus();
}

@Override
public void componentClosed() {
super.componentClosed();
Expand Down Expand Up @@ -483,6 +525,10 @@ JPopupMenu buildContextMenu() {
refresh.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F5, 0));
refresh.addActionListener(e -> forceReload());

JMenuItem find = new JMenuItem("Find…");
find.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F, InputEvent.CTRL_DOWN_MASK));
find.addActionListener(e -> showFindBar());

JMenuItem selectAll = new JMenuItem("Select All");
selectAll.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A, InputEvent.CTRL_DOWN_MASK));
selectAll.addActionListener(e -> { if (pane != null) pane.selectAll(); });
Expand All @@ -508,6 +554,8 @@ JPopupMenu buildContextMenu() {
menu.add(forward);
menu.add(refresh);
menu.addSeparator();
menu.add(find);
menu.addSeparator();
menu.add(selectAll);
menu.add(copy);

Expand Down Expand Up @@ -604,6 +652,7 @@ protected void componentOpened() {
attachHyperlinkListener();
bindRefreshKey(pane);
pane.setComponentPopupMenu(buildContextMenu());
initFindBar(pane);
}

filePath = path;
Expand Down
Loading
Loading