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 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
Binary file modified docs/screenshots/settings-general.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 23 additions & 2 deletions docs/user-manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -317,12 +317,30 @@ Press Escape to click Cancel.

### Markdown preview

For `.md` files, a rendered markdown preview is shown alongside the raw diff. Toggle it via **right-click on the diff → Preview Markdown** (checkbox). The default state is configured in **Tools → Options → Claude Code → General → Show markdown preview for .md files in diff**. Toggling in the context menu does not change the global setting.
For `.md` files, a rendered markdown preview is shown alongside the raw diff. Toggle it via **right-click on the diff → Preview Markdown** (checkbox). The default state is configured in **Tools → Options → Claude Code → General → Markdown preview in diff**, which offers three options:

| Option | Behaviour |
|--------|-----------|
| **Always** | Always show the inline preview for `.md` files in the diff panel (default) |
| **Never** | Never show the inline preview |
| **Except when Plan Preview tab is open** | Show the inline preview, but suppress it when a dedicated live Plan Preview tab is already visible for a file in the `plans/` directory |

Toggling in the context menu does not change the global setting.

**Pin Preview** — opens the rendered markdown (proposed content) in a separate IDE tab that remains open after the diff is closed.

Both features are especially useful in Claude's plan mode: they let you read the formatted plan before deciding whether to accept it.

### Auto Plan Preview

When Claude writes a plan file to a `plans/` directory (e.g. `~/.claude/plans/my-plan.md`), the plugin can automatically open a live **Markdown Preview** tab the moment you click **Accept** in the diff panel.

- The preview opens immediately when you click **Accept**; the file content appears within a second or two as Claude finishes writing it.
- If you click **Accept** again for the same plan file (e.g. after an update), the existing preview tab refreshes in-place instead of opening a new tab.
- The feature works with any `CLAUDE_CONFIG_DIR` location — it detects the `plans/` directory by name, so named profiles with custom config directories are supported automatically.

**Enable / disable:** **Tools → Options → Claude Code → General → Automatically open Plan Preview tab when Claude writes a plan**.

If the file being edited is outside the current project directory, a warning ⚠ is shown in the diff panel.

---
Expand All @@ -343,7 +361,8 @@ Open **Tools → Options → Claude Code** in NetBeans.
| Insert newline key | Enter | Key combination that inserts a newline in the input area. The send and newline keys are configured independently but cannot be set to the same value. |
| Open file diff in a separate tab | Off | Opens the diff panel in a new IDE tab instead of embedding it in the session tab. |
| File diff tab dock position | Editor area | Where the File Diff tab is docked when opened as a separate tab. Enabled only when "Open file diff in a separate tab" is checked. |
| Show markdown preview for .md files in diff | On | Shows a rendered markdown preview alongside the raw diff for `.md` files. |
| Markdown preview in diff | Always | Controls when the inline markdown preview is shown for `.md` files in the diff panel. **Always** — always show it. **Never** — never show it. **Except when Plan Preview tab is open** — show it, but suppress it when the diffed file is a plan file and Auto Plan Preview is on (or its tab is already open). |
| Automatically open Plan Preview tab when Claude writes a plan | On | When enabled, a live Markdown Preview tab opens automatically as soon as the user clicks Allow in a diff panel for a file in a `plans/` directory. |
| Markdown preview dock position | Right side | Where the Markdown Preview tab is docked when first opened. Choices: Editor area, Right side, Left side top, Left side bottom, Bottom dock. |
| Choice menu focus | Grab focus | Controls whether the choice menu panel grabs keyboard focus when it appears. Choices: Grab focus, Show without grabbing focus, Hide menu. |
| Terminal font | Auto | Font used in the embedded terminal. Click **Choose…** to open the font picker. The label shows the resolved font name (e.g. `Adwaita Mono, 14 (Auto)` when auto-detection is active, or `Monospaced, 14` when set explicitly). |
Expand Down Expand Up @@ -657,6 +676,8 @@ The plugin exposes a set of IDE tools to Claude via an MCP (Model Context Protoc
| `openDiff` | Shows a git diff for a file in the diff viewer |
| `closeAllDiffTabs` | Closes all open diff viewer tabs |
| `permission_prompt` | Shows proposed file changes in the diff panel and waits for your approval |
| `show_markdown` | Renders markdown text directly in a Markdown Preview tab; accepts the markdown content plus optional title and file path |
| `show_markdown_file` | Opens a live-updating Markdown Preview tab for an existing `.md` file on disk; the tab automatically refreshes when the file changes |

### Prompts that work well with IDE tools

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.1-SNAPSHOT</version>
<version>0.23.6-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
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@
import javax.swing.SwingUtilities;
import org.openide.windows.WindowManager;
import io.github.nbclaudecodegui.ui.ClaudeSessionTab;
import io.github.nbclaudecodegui.settings.ClaudeCodePreferences;
import io.github.nbclaudecodegui.ui.FileDiffOpener;
import io.github.nbclaudecodegui.ui.MarkdownPreviewTab;
import io.github.nbclaudecodegui.mcp.tools.DiffTabTracker;
import io.github.nbclaudecodegui.mcp.tools.GetDiagnostics;
import io.github.nbclaudecodegui.mcp.tools.GetOpenEditors;
Expand Down Expand Up @@ -548,7 +550,10 @@ public CompletableFuture<String> handlePreToolUse(String hookJson) {
CompletableFuture<String> future = DiffTabTracker.registerHookFuture(tabName);

FileDiffOpener.open(filePath, before, after, tabName, cwd,
() -> DiffTabTracker.resolveHook(tabName, hookAllowJson()),
() -> {
DiffTabTracker.resolveHook(tabName, hookAllowJson());
openPlanPreviewIfNeeded(toolInput, filePath);
},
reason -> DiffTabTracker.resolveHook(tabName, hookDenyJson(reason)),
() -> {
DiffTabTracker.resolveHook(tabName, hookDenyJson(""));
Expand All @@ -565,6 +570,35 @@ public CompletableFuture<String> handlePreToolUse(String hookJson) {
}
}

/**
* Opens a live Markdown Preview tab for the written file when it is a plan
* file ({@code <configDir>/plans/*.md}) and the {@code autoPlanPreview}
* setting is enabled. Called after the user approves a Write hook.
*
* <p>The plans directory is identified by the parent directory being named
* {@code "plans"}, making it robust against different {@code CLAUDE_CONFIG_DIR}
* values (default {@code ~/.claude}, isolated profiles, etc.)
*
* <p>The tab is opened before Claude writes the file; {@link MarkdownPreviewTab#openLive}
* schedules a deferred {@code forceReload()} that wires the {@link org.openide.filesystems.FileChangeListener}
* once the file appears on disk.
*/
private void openPlanPreviewIfNeeded(JsonNode toolInput, String filePath) {
if (!ClaudeCodePreferences.isAutoPlanPreview()) return;
if (filePath == null || filePath.isEmpty() || !filePath.endsWith(".md")) return;
java.nio.file.Path p = java.nio.file.Path.of(filePath);
if (p.getParent() == null
|| !"plans".equals(p.getParent().getFileName().toString())) return;
File file = new File(filePath);
FileObject fo = FileUtil.toFileObject(FileUtil.normalizeFile(file));
String currentContent = "";
if (fo != null) {
try { currentContent = fo.asText(); } catch (java.io.IOException ignored) {}
}
final String initialContent = currentContent;
SwingUtilities.invokeLater(() -> MarkdownPreviewTab.openLive(filePath, initialContent, fo));
}

private static boolean isFileEditTool(String toolName) {
return "Edit".equals(toolName) || "Write".equals(toolName) || "MultiEdit".equals(toolName);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

import io.github.nbclaudecodegui.model.FavoriteEntry;
import io.github.nbclaudecodegui.model.PromptFavoritesStore;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JComboBox;
import javax.swing.JList;
import io.github.nbclaudecodegui.ui.FavoritesPanel;
import java.awt.BorderLayout;
import java.awt.GridBagConstraints;
Expand Down Expand Up @@ -72,8 +75,12 @@ public final class ClaudeCodeOptionsPanel extends JPanel {
private javax.swing.JCheckBox debugCheckBox;
/** Checkbox to open diff in a separate tab. */
private javax.swing.JCheckBox diffInSessionCheck;
/** Checkbox to show markdown preview for .md files in diff. */
private javax.swing.JCheckBox mdPreviewInDiffCheck;
/** Label for the markdown preview in diff combobox. */
private JLabel mdPreviewInDiffLabel;
/** Combobox for the markdown preview in diff mode. */
private JComboBox<MdPreviewInDiffMode> mdPreviewInDiffCombo;
/** Checkbox to automatically open Plan Preview when Claude writes a plan file. */
private javax.swing.JCheckBox autoPlanPreviewCheck;
/** Checkbox for context-menu session mode (checked = New, unchecked = Continue last). */
private javax.swing.JCheckBox startNewSessionCheck;
/** Spinner for the maximum number of sessions shown in the session list. */
Expand Down Expand Up @@ -219,13 +226,36 @@ private JPanel buildGeneralPanel() {
row++;

// --- md preview in diff ---
mdPreviewInDiffCheck = new javax.swing.JCheckBox("Show markdown preview for .md files in diff");
GridBagConstraints mdGbc = new GridBagConstraints();
mdGbc.gridx = 0; mdGbc.gridy = row;
mdGbc.gridwidth = 3;
mdGbc.anchor = GridBagConstraints.WEST;
mdGbc.insets = new Insets(4, 8, 4, 8);
form.add(mdPreviewInDiffCheck, mdGbc);
mdPreviewInDiffLabel = new JLabel("Markdown preview in diff:");
form.add(mdPreviewInDiffLabel, gbc(0, row, false));
mdPreviewInDiffCombo = new JComboBox<>(MdPreviewInDiffMode.values());
mdPreviewInDiffCombo.setRenderer(new DefaultListCellRenderer() {
@Override
public java.awt.Component getListCellRendererComponent(
JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
if (value instanceof MdPreviewInDiffMode mode) {
setText(switch (mode) {
case ALWAYS -> "Always";
case NEVER -> "Never";
case EXCEPT_PLAN -> "Except when Plan Preview tab is open";
});
}
return this;
}
});
form.add(mdPreviewInDiffCombo, gbc(1, row, false));
row++;

// --- auto plan preview ---
autoPlanPreviewCheck = new javax.swing.JCheckBox(
"Automatically open Plan Preview tab when Claude writes a plan");
GridBagConstraints appGbc = new GridBagConstraints();
appGbc.gridx = 0; appGbc.gridy = row;
appGbc.gridwidth = 3;
appGbc.anchor = GridBagConstraints.WEST;
appGbc.insets = new Insets(4, 8, 4, 8);
form.add(autoPlanPreviewCheck, appGbc);
row++;

// --- markdown preview dock mode ---
Expand Down Expand Up @@ -444,7 +474,8 @@ void load() {
debugCheckBox.setSelected(ClaudeCodePreferences.isDebugMode());
diffInSessionCheck.setSelected(
ClaudeCodePreferences.isOpenDiffInSeparateTab());
mdPreviewInDiffCheck.setSelected(ClaudeCodePreferences.isMdPreviewInDiff());
mdPreviewInDiffCombo.setSelectedItem(ClaudeCodePreferences.getMdPreviewInDiffMode());
autoPlanPreviewCheck.setSelected(ClaudeCodePreferences.isAutoPlanPreview());
startNewSessionCheck.setSelected(
ClaudeCodePreferences.getContextMenuSessionMode()
== io.github.nbclaudecodegui.model.SessionMode.NEW);
Expand Down Expand Up @@ -489,7 +520,9 @@ void store() {
ClaudeCodePreferences.setHistoryTtlDays((Integer) historyTtlDaysSpinner.getValue());
ClaudeCodePreferences.setDebugMode(debugCheckBox.isSelected());
ClaudeCodePreferences.setOpenDiffInSeparateTab(diffInSessionCheck.isSelected());
ClaudeCodePreferences.setMdPreviewInDiff(mdPreviewInDiffCheck.isSelected());
MdPreviewInDiffMode selMode = (MdPreviewInDiffMode) mdPreviewInDiffCombo.getSelectedItem();
if (selMode != null) ClaudeCodePreferences.setMdPreviewInDiffMode(selMode);
ClaudeCodePreferences.setAutoPlanPreview(autoPlanPreviewCheck.isSelected());
ClaudeCodePreferences.setContextMenuSessionMode(
startNewSessionCheck.isSelected()
? io.github.nbclaudecodegui.model.SessionMode.NEW
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -380,31 +380,78 @@ public static void setOpenDiffInSeparateTab(boolean v) {
}

// -------------------------------------------------------------------------
// mdPreviewInDiff
// mdPreviewInDiffMode
// -------------------------------------------------------------------------

/** Preference key: show markdown preview for .md files in diff. */
public static final String KEY_MD_PREVIEW_IN_DIFF = "mdPreviewInDiff";
/** Default: markdown preview enabled. */
public static final boolean DEFAULT_MD_PREVIEW_IN_DIFF = true;
/** Preference key: when to show markdown preview for .md files in diff. */
public static final String KEY_MD_PREVIEW_IN_DIFF_MODE = "mdPreviewInDiffMode";
/** Default: always show the inline preview. */
public static final MdPreviewInDiffMode DEFAULT_MD_PREVIEW_IN_DIFF_MODE =
MdPreviewInDiffMode.ALWAYS;

/**
* Returns whether the markdown preview is shown for .md files in diffs.
* Returns the current mode for showing the inline Markdown preview in the diff panel.
*
* @return {@code true} if the preview is enabled
* @return the {@link MdPreviewInDiffMode}, never {@code null}
*/
public static MdPreviewInDiffMode getMdPreviewInDiffMode() {
String raw = NbPreferences.forModule(ClaudeCodePreferences.class)
.get(KEY_MD_PREVIEW_IN_DIFF_MODE, DEFAULT_MD_PREVIEW_IN_DIFF_MODE.name());
try {
return MdPreviewInDiffMode.valueOf(raw);
} catch (IllegalArgumentException e) {
return DEFAULT_MD_PREVIEW_IN_DIFF_MODE;
}
}

/**
* Persists the markdown-preview-in-diff mode setting.
*
* @param v the mode to store, must not be {@code null}
*/
public static void setMdPreviewInDiffMode(MdPreviewInDiffMode v) {
NbPreferences.forModule(ClaudeCodePreferences.class)
.put(KEY_MD_PREVIEW_IN_DIFF_MODE, v.name());
}

/**
* Returns {@code true} if the inline Markdown preview is enabled in any form.
*
* @deprecated Use {@link #getMdPreviewInDiffMode()} for full control.
*/
@Deprecated
public static boolean isMdPreviewInDiff() {
return getMdPreviewInDiffMode() != MdPreviewInDiffMode.NEVER;
}

// -------------------------------------------------------------------------
// autoPlanPreview
// -------------------------------------------------------------------------

/** Preference key: automatically open Markdown Preview when Claude writes a plan file. */
public static final String KEY_AUTO_PLAN_PREVIEW = "autoPlanPreview";
/** Default: auto plan preview enabled. */
public static final boolean DEFAULT_AUTO_PLAN_PREVIEW = true;

/**
* Returns {@code true} if the plugin should automatically open a live Markdown
* Preview tab when Claude writes a plan file to any {@code plans/} directory.
*
* @return {@code true} if auto plan preview is enabled
*/
public static boolean isAutoPlanPreview() {
return NbPreferences.forModule(ClaudeCodePreferences.class)
.getBoolean(KEY_MD_PREVIEW_IN_DIFF, DEFAULT_MD_PREVIEW_IN_DIFF);
.getBoolean(KEY_AUTO_PLAN_PREVIEW, DEFAULT_AUTO_PLAN_PREVIEW);
}

/**
* Persists the markdown-preview-in-diff setting.
* Persists the auto-plan-preview setting.
*
* @param v {@code true} to enable the preview
* @param v {@code true} to enable automatic plan preview
*/
public static void setMdPreviewInDiff(boolean v) {
NbPreferences.forModule(ClaudeCodePreferences.class).putBoolean(KEY_MD_PREVIEW_IN_DIFF, v);
public static void setAutoPlanPreview(boolean v) {
NbPreferences.forModule(ClaudeCodePreferences.class)
.putBoolean(KEY_AUTO_PLAN_PREVIEW, v);
}

// -------------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package io.github.nbclaudecodegui.settings;

/**
* Controls when the inline Markdown preview is shown inside the file diff panel.
*/
public enum MdPreviewInDiffMode {
/** Always show the inline preview for .md files in the diff panel. */
ALWAYS,
/** Never show the inline preview. */
NEVER,
/**
* Show the inline preview, except when a live Plan Preview tab is already
* open for a file in the {@code plans/} directory — in that case the
* dedicated preview tab makes the inline one redundant.
*/
EXCEPT_PLAN
}
Loading
Loading