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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
- Fixed the choice dialog (e.g. /model) endlessly flickering and resizing when not all menu items fit on screen
# 1.3.20 (2026-10-04)

- Fixed switching the edit mode (e.g. to Auto Mode) while a prompt was still running sometimes cycling through several modes and landing on the wrong one instead of the one selected
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.nbplugins</groupId>
<artifactId>netbeans-plugin-claude-code-gui</artifactId>
<version>1.3.37-SNAPSHOT</version>
<version>1.3.38-SNAPSHOT</version>
<packaging>nbm</packaging>
<name>Netbeans Plugin 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 @@ -1438,7 +1438,7 @@ private void applyDetectedChoiceMenu(ChoiceMenuModel newMenu, String logTag) {
model.setActiveChoiceMenu(newMenu);
return;
}
if (sameMenu(newMenu, current)) {
if (sameMenu(newMenu, current) || isShrunkViewOf(newMenu, current)) {
pendingChoiceMenuCandidate = null;
return;
}
Expand All @@ -1452,6 +1452,28 @@ private void applyDetectedChoiceMenu(ChoiceMenuModel newMenu, String logTag) {
}
}

/**
* Returns {@code true} when {@code newMenu} is just a shorter window onto the same
* scrolling list as {@code current} (same question, options are a strict prefix).
*
* <p>Scrolling pickers such as {@code /model} draw as many items as fit the terminal.
* The choice panel shrinks the terminal, which makes Claude draw fewer items, which
* shrinks the panel, which grows the terminal again — an endless flicker. Keeping
* the larger option list breaks that loop.
*/
static boolean isShrunkViewOf(ChoiceMenuModel newMenu, ChoiceMenuModel current) {
List<ChoiceMenuModel.Option> n = newMenu.options();
List<ChoiceMenuModel.Option> c = current.options();
if (!newMenu.text().equals(current.text()) || n.size() >= c.size()) return false;
for (int i = 0; i < n.size(); i++) {
if (!n.get(i).display().equals(c.get(i).display())
|| !n.get(i).response().equals(c.get(i).response())) {
return false;
}
}
return true;
}

private static boolean sameMenu(ChoiceMenuModel a, ChoiceMenuModel b) {
return a.text().equals(b.text()) && optionsEqual(a.options(), b.options());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,36 @@ void setUp() {
// onClaudeIdle
// -------------------------------------------------------------------------

private static ChoiceMenuModel menu(String text, String... displays) {
List<ChoiceMenuModel.Option> opts = new java.util.ArrayList<>();
for (int i = 0; i < displays.length; i++) {
opts.add(new ChoiceMenuModel.Option(displays[i], String.valueOf(i + 1)));
}
return new ChoiceMenuModel(text, opts, 0);
}

@Test
void isShrunkViewOf_prefixOfScrollingList_true() {
ChoiceMenuModel full = menu("Select model", "a", "b", "c", "d");
ChoiceMenuModel shrunk = menu("Select model", "a", "b", "c");
assertTrue(ClaudeSessionController.isShrunkViewOf(shrunk, full));
}

@Test
void isShrunkViewOf_sameSizeOrGrown_false() {
ChoiceMenuModel three = menu("Select model", "a", "b", "c");
ChoiceMenuModel four = menu("Select model", "a", "b", "c", "d");
assertFalse(ClaudeSessionController.isShrunkViewOf(three, three));
assertFalse(ClaudeSessionController.isShrunkViewOf(four, three));
}

@Test
void isShrunkViewOf_differentTextOrOptions_false() {
ChoiceMenuModel full = menu("Select model", "a", "b", "c", "d");
assertFalse(ClaudeSessionController.isShrunkViewOf(menu("Other", "a", "b", "c"), full));
assertFalse(ClaudeSessionController.isShrunkViewOf(menu("Select model", "a", "x", "c"), full));
}

@Test
void onClaudeIdleSetsLifecycleToReady() {
model.setLifecycle(SessionLifecycle.WORKING);
Expand Down
Loading