diff --git a/CHANGELOG.md b/CHANGELOG.md
index d87fbb57..4c6639e7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,4 @@
+- Fixed model combo not becoming active when a session starts with a blocking dialog (e.g. "Resuming session" prompt on --continue/--resume startup)
- Improved keyboard navigation in choice menus: arrow keys, Tab, Shift+Tab
- Added terminal font and font size settings (Tools → Options → Claude Code → General); defaults to auto-detection preferring Adwaita Mono for full Unicode coverage of Claude Code TUI symbols (https://github.com/nbclaudecodegui/NetbeansClaudeCodeGui/issues/18)
- Reorganized General and Advanced settings tabs: moved technical settings (hang timeout, MCP port, Enable MCP integration, session list limit, debug mode) to Advanced
diff --git a/pom.xml b/pom.xml
index a8aebd31..f70cc2c8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
io.github.nbclaudecodegui
netbeans-claude-code-gui
- 0.22.31-SNAPSHOT
+ 0.22.32-SNAPSHOT
nbm
Netbeans 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/nbclaudecodegui/controller/ClaudeSessionController.java b/src/main/java/io/github/nbclaudecodegui/controller/ClaudeSessionController.java
index 365bee5e..d7397700 100644
--- a/src/main/java/io/github/nbclaudecodegui/controller/ClaudeSessionController.java
+++ b/src/main/java/io/github/nbclaudecodegui/controller/ClaudeSessionController.java
@@ -48,7 +48,7 @@
* direct widget references after {@link #startProcess} returns.
*
*/
-public final class ClaudeSessionController {
+public class ClaudeSessionController {
// -------------------------------------------------------------------------
// Fields
@@ -763,7 +763,7 @@ public void switchModel(int index) {
* Retries up to {@value #MAX_MODEL_DISCOVERY_ATTEMPTS} times if the screen
* returns no models.
*/
- private void discoverModels() {
+ void discoverModels() {
if (connector == null || modelComboPopulated || modelDiscoveryInProgress) return;
if (modelDiscoveryAttempts >= MAX_MODEL_DISCOVERY_ATTEMPTS) return;
modelComboPopulated = true;
@@ -923,9 +923,16 @@ void pollScreenState() {
model.setLifecycle(SessionLifecycle.READY);
}
- // On first READY: discover models and detect initial edit mode
+ // On first READY: discover models and detect initial edit mode.
+ // Defer discovery while any blocking dialog is visible — e.g. the
+ // "Resuming session" prompt shown on --continue/--resume startup.
+ // Sending /model while Claude is blocked by such a dialog causes the
+ // model menu to appear only after the dialog is dismissed, by which
+ // time discovery has already timed out and given up.
if (model.getLifecycle() == SessionLifecycle.READY
- && !modelComboPopulated && !modelDiscoveryInProgress) {
+ && !modelComboPopulated && !modelDiscoveryInProgress
+ && model.getActiveChoiceMenu() == null
+ && screenContentDetector.detectChoiceMenu(lines).isEmpty()) {
discoverModels();
detectAndApplyInitialEditMode(lines);
}
diff --git a/src/test/java/io/github/nbclaudecodegui/controller/ClaudeSessionControllerTest.java b/src/test/java/io/github/nbclaudecodegui/controller/ClaudeSessionControllerTest.java
index ac58407b..b11dd065 100644
--- a/src/test/java/io/github/nbclaudecodegui/controller/ClaudeSessionControllerTest.java
+++ b/src/test/java/io/github/nbclaudecodegui/controller/ClaudeSessionControllerTest.java
@@ -618,6 +618,49 @@ void hangNotDetectedWhenWithinTimeout() {
assertNull(callbackCommand.get(), "Hang callback should NOT fire when elapsed < timeout");
}
+ // -------------------------------------------------------------------------
+ // discoverModels deferred when blocking dialog is visible at READY
+ // -------------------------------------------------------------------------
+
+ /**
+ * Regression: when Claude starts with --continue/--resume, it may show a
+ * "Resuming session" dialog before the input prompt. If discoverModels() is
+ * triggered while this dialog is visible, it sends /model but Claude cannot
+ * process it yet (blocked by the dialog). Discovery times out, the combo
+ * stays unpopulated, and MAX_MODEL_DISCOVERY_ATTEMPTS prevents any retry.
+ *
+ * Fix: pollScreenState must skip discoverModels() while any choice menu is
+ * visible on screen or already shown to the user.
+ */
+ @Test
+ void pollScreenState_doesNotStartDiscoveryWhenBlockingMenuIsPresent() {
+ List blockingMenuScreen = Arrays.asList(
+ "Resuming the full session will consume a substantial portion of your usage limits.",
+ "❯ 1. Resume from full history",
+ " 2. Resume from summary",
+ "Enter to confirm · Esc to cancel"
+ );
+
+ java.util.concurrent.atomic.AtomicInteger discoveryCallCount =
+ new java.util.concurrent.atomic.AtomicInteger(0);
+
+ ClaudeSessionModel m = new ClaudeSessionModel();
+ ClaudeSessionController c = new ClaudeSessionController(m, () -> blockingMenuScreen) {
+ @Override
+ void discoverModels() {
+ discoveryCallCount.incrementAndGet();
+ }
+ };
+
+ m.setLifecycle(SessionLifecycle.READY);
+ c.pollScreenState();
+
+ assertEquals(0, discoveryCallCount.get(),
+ "discoverModels must not be called while a blocking dialog is visible on screen");
+ assertNotNull(m.getActiveChoiceMenu(),
+ "Blocking dialog must be shown to the user while discovery is deferred");
+ }
+
// -------------------------------------------------------------------------
// No-op listener base class
// -------------------------------------------------------------------------