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()); + } }