diff --git a/CHANGELOG.md b/CHANGELOG.md
index 724856e5..b8c6c4aa 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,5 @@
+- Fixed a Warning dialog appearing on NetBeans exit when a diff tab had been opened during the session
+
# 1.2.8 (2026-05-24)
- Added "Auto Mode" to the edit-mode selector (Claude Code 2.1.83+): file edits in auto mode are auto-approved; when auto mode is selected on an older Claude Code version, the plugin falls back to Bypass Permissions or Accept on Edit
diff --git a/pom.xml b/pom.xml
index 72fe8ecf..951ccbf8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
io.github.nbplugins
netbeans-plugin-claude-code-gui
- 1.2.12-SNAPSHOT
+ 1.2.14-SNAPSHOT
nbm
Netbeans Plugin 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/nbplugins/claudecodegui/ClaudeCodeInstaller.java b/src/main/java/io/github/nbplugins/claudecodegui/ClaudeCodeInstaller.java
index 3246cff3..20ae26b1 100644
--- a/src/main/java/io/github/nbplugins/claudecodegui/ClaudeCodeInstaller.java
+++ b/src/main/java/io/github/nbplugins/claudecodegui/ClaudeCodeInstaller.java
@@ -51,8 +51,12 @@ public void restored() {
// Migrate window settings files (must run before window system reads them)
String userDir = System.getProperty("netbeans.user");
if (userDir != null) {
- V1MigrationHelper.migrateWindowsSettings(
- Paths.get(userDir, "config", "Windows2Local", "Components"));
+ Path componentsDir = Paths.get(userDir, "config", "Windows2Local", "Components");
+ V1MigrationHelper.migrateWindowsSettings(componentsDir);
+ // FileDiffOpener$1 was renamed to FileDiffOpener$DiffTopComponent in 1.2.13;
+ // delete stale .settings files so NetBeans does not warn about the unknown class.
+ V1MigrationHelper.removeStaleComponentSettings(componentsDir,
+ "FileDiffOpener$1");
}
// Migrate preferences from old package paths (one-time, after package rename in 1.0)
diff --git a/src/main/java/io/github/nbplugins/claudecodegui/V1MigrationHelper.java b/src/main/java/io/github/nbplugins/claudecodegui/V1MigrationHelper.java
index 42ca90bb..7bc3b87e 100644
--- a/src/main/java/io/github/nbplugins/claudecodegui/V1MigrationHelper.java
+++ b/src/main/java/io/github/nbplugins/claudecodegui/V1MigrationHelper.java
@@ -41,6 +41,34 @@ static void migrateWindowsSettings(Path componentsDir) {
}
}
+ /**
+ * Deletes stale .settings files in componentsDir that reference class names matching
+ * any of the given substrings. Use this when a TopComponent class is renamed or removed
+ * and its serialized binary data can no longer be deserialized.
+ */
+ static void removeStaleComponentSettings(Path componentsDir, String... classNameSubstrings) {
+ if (!Files.exists(componentsDir)) return;
+ try (var stream = Files.list(componentsDir)) {
+ stream.filter(p -> p.toString().endsWith(".settings"))
+ .forEach(p -> {
+ try {
+ String content = Files.readString(p);
+ for (String substring : classNameSubstrings) {
+ if (content.contains(substring)) {
+ Files.delete(p);
+ LOG.log(Level.FINE, "Removed stale window settings: {0}", p);
+ break;
+ }
+ }
+ } catch (IOException e) {
+ LOG.log(Level.WARNING, "Could not process window settings: " + p, e);
+ }
+ });
+ } catch (IOException e) {
+ LOG.log(Level.WARNING, "Stale settings cleanup failed", e);
+ }
+ }
+
/** Copies keys from oldPath NbPreferences node to newNode, then removes the old node. */
static void migratePrefsNode(String oldPath, Preferences newNode) {
migratePrefsNode(NbPreferences.root(), oldPath, newNode);
diff --git a/src/main/java/io/github/nbplugins/claudecodegui/ui/FileDiffOpener.java b/src/main/java/io/github/nbplugins/claudecodegui/ui/FileDiffOpener.java
index 5d37e02f..ca5592be 100644
--- a/src/main/java/io/github/nbplugins/claudecodegui/ui/FileDiffOpener.java
+++ b/src/main/java/io/github/nbplugins/claudecodegui/ui/FileDiffOpener.java
@@ -341,17 +341,7 @@ public static void open(
}
// --- TOPLEVEL branch ---
- TopComponent diffTC = new TopComponent() {
- @Override
- public void componentClosed() {
- super.componentClosed();
- removeAwtListener[0].run();
- if (!decided.get()) {
- onClose.run();
- activateSessionForFile(filePath);
- }
- }
- };
+ TopComponent diffTC = new DiffTopComponent(removeAwtListener, decided, onClose, filePath);
closeDiffRef[0] = diffTC::close;
diffTC.setDisplayName(tabName);
diffTC.setLayout(new java.awt.BorderLayout());
@@ -596,6 +586,36 @@ public static void cancelCurrentPromptForFile(String filePath) {
}
}
+ static final class DiffTopComponent extends TopComponent {
+ private final Runnable[] removeAwtListener;
+ private final AtomicBoolean decided;
+ private final Runnable onClose;
+ private final String filePath;
+
+ DiffTopComponent(Runnable[] removeAwtListener, AtomicBoolean decided,
+ Runnable onClose, String filePath) {
+ this.removeAwtListener = removeAwtListener;
+ this.decided = decided;
+ this.onClose = onClose;
+ this.filePath = filePath;
+ }
+
+ @Override
+ public int getPersistenceType() {
+ return TopComponent.PERSISTENCE_NEVER;
+ }
+
+ @Override
+ public void componentClosed() {
+ super.componentClosed();
+ removeAwtListener[0].run();
+ if (!decided.get()) {
+ onClose.run();
+ activateSessionForFile(filePath);
+ }
+ }
+ }
+
private static StreamSource textSource(String name, String filePath, String content) {
return new StreamSource() {
@Override public String getName() { return name; }
diff --git a/src/test/java/io/github/nbplugins/claudecodegui/ui/FileDiffOpenerTest.java b/src/test/java/io/github/nbplugins/claudecodegui/ui/FileDiffOpenerTest.java
index 1de48093..3ee82542 100644
--- a/src/test/java/io/github/nbplugins/claudecodegui/ui/FileDiffOpenerTest.java
+++ b/src/test/java/io/github/nbplugins/claudecodegui/ui/FileDiffOpenerTest.java
@@ -3,10 +3,12 @@
import java.awt.Component;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import org.junit.jupiter.api.Test;
+import org.openide.windows.TopComponent;
import static org.junit.jupiter.api.Assertions.*;
@@ -283,4 +285,14 @@ private static long countCheckItems(JPopupMenu menu, String text) {
}
return count;
}
+
+ @Test
+ void diffTopComponentPersistenceTypeIsNever() {
+ FileDiffOpener.DiffTopComponent tc = new FileDiffOpener.DiffTopComponent(
+ new Runnable[]{() -> {}},
+ new AtomicBoolean(false),
+ () -> {},
+ "/tmp/test.java");
+ assertEquals(TopComponent.PERSISTENCE_NEVER, tc.getPersistenceType());
+ }
}