diff --git a/CHANGELOG.md b/CHANGELOG.md index 1af0b76..e10fd43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +- Fixed a "Warning" dialog showing raw XML content sometimes appearing at IDE startup after using a diff view + # 1.2.25 (2026-06-29) - Fixed choice menu not appearing when option descriptions span more than 3 terminal lines diff --git a/pom.xml b/pom.xml index 4dbf77a..10714a4 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.github.nbplugins netbeans-plugin-claude-code-gui - 1.2.25-SNAPSHOT + 1.2.27-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/mcp/tools/OpenDiff.java b/src/main/java/io/github/nbplugins/claudecodegui/mcp/tools/OpenDiff.java index f7f9de5..fea7461 100644 --- a/src/main/java/io/github/nbplugins/claudecodegui/mcp/tools/OpenDiff.java +++ b/src/main/java/io/github/nbplugins/claudecodegui/mcp/tools/OpenDiff.java @@ -221,13 +221,7 @@ public Writer createWriter(Difference[] conflicts) throws IOException { // Override componentClosed() so that closing the tab without clicking // Approve or Reject sends FILE_REJECTED — preventing Claude from hanging. final String finalDiffTabName = diffTabName; - TopComponent diffTC = new TopComponent() { - @Override - public void componentClosed() { - super.componentClosed(); - DiffTabTracker.setRejected(finalDiffTabName); - } - }; + TopComponent diffTC = new DiffTopComponent(finalDiffTabName); diffTC.setDisplayName(diffTabName); diffTC.setLayout(new java.awt.BorderLayout()); @@ -305,4 +299,28 @@ public void setHandler(AsyncHandler handler) { return createAsyncResponse(createErrorResult("Error opening diff: " + e.getMessage())); } } + + /** + * Ephemeral diff-viewer tab. Must never be persisted across IDE restarts — its toolbar + * button listeners close over per-request state (file contents, async response handler) + * that is not meaningfully serializable. + */ + static final class DiffTopComponent extends TopComponent { + private final String diffTabName; + + DiffTopComponent(String diffTabName) { + this.diffTabName = diffTabName; + } + + @Override + public int getPersistenceType() { + return TopComponent.PERSISTENCE_NEVER; + } + + @Override + public void componentClosed() { + super.componentClosed(); + DiffTabTracker.setRejected(diffTabName); + } + } } diff --git a/src/test/java/io/github/nbplugins/claudecodegui/mcp/tools/OpenDiffTest.java b/src/test/java/io/github/nbplugins/claudecodegui/mcp/tools/OpenDiffTest.java new file mode 100644 index 0000000..aa3ff34 --- /dev/null +++ b/src/test/java/io/github/nbplugins/claudecodegui/mcp/tools/OpenDiffTest.java @@ -0,0 +1,22 @@ +package io.github.nbplugins.claudecodegui.mcp.tools; + +import org.junit.jupiter.api.Test; +import org.openide.windows.TopComponent; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * Unit tests for {@link OpenDiff}. + */ +class OpenDiffTest { + + @Test + void diffTopComponentPersistenceTypeIsNever() { + // Reproduces the bug where the diff tab's TopComponent defaulted to PERSISTENCE_ALWAYS, + // causing the NetBeans window system to try to serialize it (and the non-serializable + // async-handler state it closes over) into a .settings file on IDE exit — producing a + // corrupted-settings "Warning" dialog with raw XML on the next startup. + OpenDiff.DiffTopComponent tc = new OpenDiff.DiffTopComponent("Diff: test"); + assertEquals(TopComponent.PERSISTENCE_NEVER, tc.getPersistenceType()); + } +}