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
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ The plugin follows an MVC structure for session management:
| `settings/` | `ClaudeCodePreferences` — NbPreferences wrapper (keys: `contextMenuSessionMode` default CONTINUE_LAST, `sessionListLimit` default 30); `ClaudeCodeOptionsPanelController` / `ClaudeCodeOptionsPanel` — Tools→Options (General + Profiles tabs); `ClaudeProfile`, `ClaudeProfileStore` — named profiles with isolated `CLAUDE_CONFIG_DIR`, auth, proxy, extra env vars; `ClaudeProjectProperties` — per-project profile assignment |
| `actions/` | `ClaudeCodeAction` — toolbar button; `OpenWithClaudeAction` — project node context menu; `PromptFavoriteAction` — dynamically registered action per favorite for Keymap API |
| `io.github.nbclaudecodegui.mcp` | `MCPSseServer` — Jetty `/sse` `/messages` `/hook`; `NetBeansMCPHandler` — MCP dispatcher + PreToolUse hook; `tools/` — `OpenDiff`, `PermissionPromptTool`, `DiffTabTracker`, `GetDiagnostics`, `GetOpenEditors`, `GetCurrentSelection`, `OpenFile` |
| `org.openbeans.claude.netbeans` | Legacy classes: `ClaudeCodeStatusService`, `ClaudeCodeStatusLineElement`, `EditorUtils`, `NbUtils`; `tools/` — `AsyncHandler`, `AsyncResponse`, `GetWorkspaceFolders`, `CheckDocumentDirty`, `SaveDocument`, `CloseTab`, `CloseAllDiffTabs` |
| `org.openbeans.claude.netbeans` | **Legacy package — do not add new code here and avoid modifying existing code.** Contains: `ClaudeCodeStatusService`, `ClaudeCodeStatusLineElement`, `EditorUtils`, `NbUtils`; `tools/` — `AsyncHandler`, `AsyncResponse`, `GetWorkspaceFolders`, `CheckDocumentDirty`, `SaveDocument`, `CloseTab`, `CloseAllDiffTabs` |

### Session lifecycle
1. User clicks toolbar → `ClaudeSessionTab` opens; user picks a directory and session mode in `ClaudeSessionSelectorPanel` → `ClaudeSessionController.startProcess()` merges `settings.local.json` and launches the PTY.
Expand Down
10 changes: 7 additions & 3 deletions docs/user-manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ After submitting or cancelling, the input area is restored automatically.

When Claude Code is about to edit or create a file, the plugin intercepts the operation and shows a diff panel **before** the change is written to disk.

The diff panel appears either **embedded in the session tab** (replacing the input area) or in a **separate IDE tab**, depending on the setting in **Tools → Options → Claude Code → General → Open diff in a separate tab**.
The diff panel appears either **embedded in the session tab** (replacing the input area) or in a **separate IDE tab**, depending on the setting in **Tools → Options → Claude Code → General → Open file diff in a separate tab**.

![Diff panel with permission bar](screenshots/diff-panel.png)

Expand Down Expand Up @@ -343,10 +343,14 @@ Open **Tools → Options → Claude Code** in NetBeans.
| History TTL (days) | 0 | Number of days after which history entries expire. 0 = keep forever. |
| Send prompt key | Ctrl+Enter | Key combination that sends the prompt from the input area. |
| 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 diff in a separate tab | Off | Opens the diff panel in a new IDE tab instead of embedding it in the session tab. |
| Show markdown preview for .md files in diff | On | Shows a rendered markdown preview alongside the raw diff for `.md` files. |
| Start new session when opening with Claude | Off (Continue last) | Checked → always start a **New session** when opening with the project context menu or when NetBeans restores a closed session tab on restart. Unchecked → **Continue last**. |
| Session list limit | 30 | Maximum number of past sessions shown in the session selector table. |
| Session tab dock position | Editor area | Where the Claude Code session tab is docked when first opened. Choices: Editor area, Right side, Left side top, Left side bottom, Bottom dock. |
| 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 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. |

### Advanced tab

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.22.10-SNAPSHOT</version>
<version>0.22.13-SNAPSHOT</version>
<packaging>nbm</packaging>
<name>Netbeans 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 @@ -49,13 +49,6 @@ public final class ClaudeCodeOptionsPanel extends JPanel {
"Grab focus (default)", "Show without grabbing focus", "Hide menu"
};

private static final String[] DOCK_MODE_VALUES = {
"editor", "commonpalette", "explorer", "navigator", "output"
};
private static final String[] DOCK_MODE_LABELS = {
"Editor area (default)", "Right side", "Left side top", "Left side bottom", "Bottom dock"
};

private static final String[] KEY_VALUES = {
ClaudeCodePreferences.ENTER,
ClaudeCodePreferences.SHIFT_ENTER,
Expand Down Expand Up @@ -86,6 +79,14 @@ public final class ClaudeCodeOptionsPanel extends JPanel {
private JSpinner sessionListLimitSpinner;
/** Dropdown for the dock position of the Claude Code session tab. */
private javax.swing.JComboBox<String> dockModeCombo;
/** Dropdown for the dock position of the Markdown Preview tab. */
private javax.swing.JComboBox<String> mdPreviewDockModeCombo;
/** Label for the Markdown Preview dock position combo. */
private javax.swing.JLabel mdPreviewDockModeLabel;
/** Dropdown for the dock position of the File Diff tab. */
private javax.swing.JComboBox<String> fileDiffDockModeCombo;
/** Label for the File Diff dock position combo. */
private javax.swing.JLabel fileDiffDockModeLabel;
/** Dropdown for the choice menu focus behavior. */
private javax.swing.JComboBox<String> choiceMenuFocusCombo;
/** Spinner for the hang-detection timeout in seconds (0 = disabled). */
Expand Down Expand Up @@ -204,26 +205,6 @@ private JPanel buildGeneralPanel() {
newlineRadios.get(val).addActionListener(e -> syncExclusion(val, false));
}

// --- diff in session ---
diffInSessionCheck = new javax.swing.JCheckBox("Open diff in a separate tab");
GridBagConstraints diffGbc = new GridBagConstraints();
diffGbc.gridx = 0; diffGbc.gridy = row;
diffGbc.gridwidth = 3;
diffGbc.anchor = GridBagConstraints.WEST;
diffGbc.insets = new Insets(4, 8, 4, 8);
form.add(diffInSessionCheck, diffGbc);
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);
row++;

// --- context-menu session mode ---
startNewSessionCheck = new javax.swing.JCheckBox("Start new session when opening with Claude");
GridBagConstraints cmGbc = new GridBagConstraints();
Expand All @@ -244,11 +225,53 @@ private JPanel buildGeneralPanel() {

// --- session tab dock position ---
form.add(new JLabel("Session tab dock position:"), gbc(0, row, false));
dockModeCombo = new javax.swing.JComboBox<>(DOCK_MODE_LABELS);
dockModeCombo = new javax.swing.JComboBox<>(
DockMode.labels(ClaudeCodePreferences.DEFAULT_SESSION_DOCK_MODE));
dockModeCombo.setToolTipText("Where to dock the Claude Code session tab when opening it");
form.add(dockModeCombo, gbc(1, row, false));
row++;

// --- diff in session ---
diffInSessionCheck = new javax.swing.JCheckBox("Open file diff in a separate tab");
GridBagConstraints diffGbc = new GridBagConstraints();
diffGbc.gridx = 0; diffGbc.gridy = row;
diffGbc.gridwidth = 3;
diffGbc.anchor = GridBagConstraints.WEST;
diffGbc.insets = new Insets(4, 8, 4, 8);
form.add(diffInSessionCheck, diffGbc);
diffInSessionCheck.addActionListener(e -> updateFileDiffDockEnabled());
row++;

// --- file diff dock mode (enabled only when diff is in separate tab) ---
fileDiffDockModeLabel = new javax.swing.JLabel("File diff tab dock position:");
form.add(fileDiffDockModeLabel, gbc(0, row, false));
fileDiffDockModeCombo = new javax.swing.JComboBox<>(
DockMode.labels(ClaudeCodePreferences.DEFAULT_FILE_DIFF_DOCK_MODE));
fileDiffDockModeCombo.setToolTipText(
"Where to dock the File Diff tab when opening it in a separate tab");
form.add(fileDiffDockModeCombo, gbc(1, row, false));
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);
row++;

// --- markdown preview dock mode ---
mdPreviewDockModeLabel = new javax.swing.JLabel("Markdown preview dock position:");
form.add(mdPreviewDockModeLabel, gbc(0, row, false));
mdPreviewDockModeCombo = new javax.swing.JComboBox<>(
DockMode.labels(ClaudeCodePreferences.DEFAULT_MARKDOWN_PREVIEW_DOCK_MODE));
mdPreviewDockModeCombo.setToolTipText(
"Where to dock the Markdown Preview tab when first opened");
form.add(mdPreviewDockModeCombo, gbc(1, row, false));
row++;

// --- choice menu focus mode ---
form.add(new JLabel("Choice menu focus:"), gbc(0, row, false));
choiceMenuFocusCombo = new javax.swing.JComboBox<>(CHOICE_MENU_FOCUS_LABELS);
Expand Down Expand Up @@ -391,9 +414,12 @@ void load() {
ClaudeCodePreferences.getContextMenuSessionMode()
== io.github.nbclaudecodegui.model.SessionMode.NEW);
sessionListLimitSpinner.setValue(ClaudeCodePreferences.getSessionListLimit());
String dockMode = ClaudeCodePreferences.getSessionDockMode();
int dockIdx = java.util.Arrays.asList(DOCK_MODE_VALUES).indexOf(dockMode);
dockModeCombo.setSelectedIndex(dockIdx >= 0 ? dockIdx : 0);
dockModeCombo.setSelectedIndex(ClaudeCodePreferences.getSessionDockMode().ordinal());
mdPreviewDockModeCombo.setSelectedIndex(
ClaudeCodePreferences.getMarkdownPreviewDockMode().ordinal());
fileDiffDockModeCombo.setSelectedIndex(
ClaudeCodePreferences.getFileDiffDockMode().ordinal());
updateFileDiffDockEnabled();
String focusMode = ClaudeCodePreferences.getChoiceMenuFocusMode();
int focusIdx = java.util.Arrays.asList(CHOICE_MENU_FOCUS_VALUES).indexOf(focusMode);
choiceMenuFocusCombo.setSelectedIndex(focusIdx >= 0 ? focusIdx : 0);
Expand Down Expand Up @@ -431,11 +457,9 @@ void store() {
? io.github.nbclaudecodegui.model.SessionMode.NEW
: io.github.nbclaudecodegui.model.SessionMode.CONTINUE_LAST);
ClaudeCodePreferences.setSessionListLimit((Integer) sessionListLimitSpinner.getValue());
int dockSel = dockModeCombo.getSelectedIndex();
ClaudeCodePreferences.setSessionDockMode(
dockSel >= 0 && dockSel < DOCK_MODE_VALUES.length
? DOCK_MODE_VALUES[dockSel]
: ClaudeCodePreferences.DEFAULT_SESSION_DOCK_MODE);
ClaudeCodePreferences.setSessionDockMode(dockModeFromCombo(dockModeCombo));
ClaudeCodePreferences.setMarkdownPreviewDockMode(dockModeFromCombo(mdPreviewDockModeCombo));
ClaudeCodePreferences.setFileDiffDockMode(dockModeFromCombo(fileDiffDockModeCombo));
int focusSel = choiceMenuFocusCombo.getSelectedIndex();
ClaudeCodePreferences.setChoiceMenuFocusMode(
focusSel >= 0 && focusSel < CHOICE_MENU_FOCUS_VALUES.length
Expand Down Expand Up @@ -478,6 +502,18 @@ private static String selectedValue(Map<String, JRadioButton> radios) {
return ClaudeCodePreferences.DEFAULT_SEND_KEY;
}

private static DockMode dockModeFromCombo(javax.swing.JComboBox<?> combo) {
int sel = combo.getSelectedIndex();
DockMode[] vals = DockMode.values();
return (sel >= 0 && sel < vals.length) ? vals[sel] : DockMode.EDITOR;
}

private void updateFileDiffDockEnabled() {
boolean sep = diffInSessionCheck.isSelected();
fileDiffDockModeLabel.setEnabled(sep);
fileDiffDockModeCombo.setEnabled(sep);
}

private static GridBagConstraints gbc(int x, int y, boolean fill) {
GridBagConstraints c = new GridBagConstraints();
c.gridx = x; c.gridy = y;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -557,17 +557,97 @@ public static void setMcpEnabled(boolean v) {

/** Preference key: dock mode for the Claude Code session tab. */
public static final String KEY_SESSION_DOCK_MODE = "sessionDockMode";
/** Default: editor area. */
public static final String DEFAULT_SESSION_DOCK_MODE = "editor";
/** Default dock position for the session tab: editor area. */
public static final DockMode DEFAULT_SESSION_DOCK_MODE = DockMode.EDITOR;

public static String getSessionDockMode() {
return NbPreferences.forModule(ClaudeCodePreferences.class)
.get(KEY_SESSION_DOCK_MODE, DEFAULT_SESSION_DOCK_MODE);
/**
* Returns the configured dock position for the Claude Code session tab.
*
* @return configured {@link DockMode}, or {@link DockMode#EDITOR} as fallback
*/
public static DockMode getSessionDockMode() {
String stored = NbPreferences.forModule(ClaudeCodePreferences.class)
.get(KEY_SESSION_DOCK_MODE, DEFAULT_SESSION_DOCK_MODE.getModeName());
return DockMode.fromModeName(stored);
}

/**
* Persists the dock position for the Claude Code session tab.
*
* @param mode desired dock position; {@code null} falls back to {@link DockMode#EDITOR}
*/
public static void setSessionDockMode(DockMode mode) {
NbPreferences.forModule(ClaudeCodePreferences.class)
.put(KEY_SESSION_DOCK_MODE,
mode != null ? mode.getModeName() : DEFAULT_SESSION_DOCK_MODE.getModeName());
}

// -------------------------------------------------------------------------
// markdownPreviewDockMode
// -------------------------------------------------------------------------

/** Preference key: dock mode for the Markdown Preview tab. */
public static final String KEY_MARKDOWN_PREVIEW_DOCK_MODE = "markdownPreviewDockMode";
/** Default dock position for the Markdown Preview tab: right side. */
public static final DockMode DEFAULT_MARKDOWN_PREVIEW_DOCK_MODE = DockMode.RIGHT;

/**
* Returns the configured dock position for the Markdown Preview tab.
*
* @return configured {@link DockMode}, or {@link DockMode#RIGHT} as fallback
*/
public static DockMode getMarkdownPreviewDockMode() {
String stored = NbPreferences.forModule(ClaudeCodePreferences.class)
.get(KEY_MARKDOWN_PREVIEW_DOCK_MODE,
DEFAULT_MARKDOWN_PREVIEW_DOCK_MODE.getModeName());
return DockMode.fromModeName(stored);
}

public static void setSessionDockMode(String mode) {
/**
* Persists the dock position for the Markdown Preview tab.
*
* @param mode desired dock position; {@code null} falls back to {@link DockMode#RIGHT}
*/
public static void setMarkdownPreviewDockMode(DockMode mode) {
NbPreferences.forModule(ClaudeCodePreferences.class)
.put(KEY_MARKDOWN_PREVIEW_DOCK_MODE,
mode != null ? mode.getModeName()
: DEFAULT_MARKDOWN_PREVIEW_DOCK_MODE.getModeName());
}

// -------------------------------------------------------------------------
// fileDiffDockMode
// -------------------------------------------------------------------------

/** Preference key: dock mode for the File Diff tab (separate-tab mode only). */
public static final String KEY_FILE_DIFF_DOCK_MODE = "fileDiffDockMode";
/** Default dock position for the File Diff tab: editor area. */
public static final DockMode DEFAULT_FILE_DIFF_DOCK_MODE = DockMode.EDITOR;

/**
* Returns the configured dock position for the File Diff tab.
*
* <p>Only used when "Open diff in a separate tab" is enabled.
*
* @return configured {@link DockMode}, or {@link DockMode#EDITOR} as fallback
*/
public static DockMode getFileDiffDockMode() {
String stored = NbPreferences.forModule(ClaudeCodePreferences.class)
.get(KEY_FILE_DIFF_DOCK_MODE,
DEFAULT_FILE_DIFF_DOCK_MODE.getModeName());
return DockMode.fromModeName(stored);
}

/**
* Persists the dock position for the File Diff tab.
*
* @param mode desired dock position; {@code null} falls back to {@link DockMode#EDITOR}
*/
public static void setFileDiffDockMode(DockMode mode) {
NbPreferences.forModule(ClaudeCodePreferences.class)
.put(KEY_SESSION_DOCK_MODE, mode != null ? mode : DEFAULT_SESSION_DOCK_MODE);
.put(KEY_FILE_DIFF_DOCK_MODE,
mode != null ? mode.getModeName()
: DEFAULT_FILE_DIFF_DOCK_MODE.getModeName());
}

// -------------------------------------------------------------------------
Expand Down
Loading
Loading