This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new f2876d3ce630 CAMEL-24254: Add shell and AI history settings to F2
settings dialog
f2876d3ce630 is described below
commit f2876d3ce630879b88bdc3aae3a50c42b50d461f
Author: Claus Ibsen <[email protected]>
AuthorDate: Sat Jul 25 13:11:01 2026 +0200
CAMEL-24254: Add shell and AI history settings to F2 settings dialog
Wire the shellHistory and aiPromptHistory options into the SettingsPopup
so users can configure them via F2 instead of editing config files
manually. Also reduce the dialog width from 80 to 60 columns.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Signed-off-by: Claus Ibsen <[email protected]>
---
.../dsl/jbang/core/commands/tui/SettingsPopup.java | 48 +++++++++++++++++++---
.../core/commands/tui/SettingsPopupRenderTest.java | 2 +
.../jbang/core/commands/tui/SettingsPopupTest.java | 37 ++++++++++++++++-
3 files changed, 80 insertions(+), 7 deletions(-)
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SettingsPopup.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SettingsPopup.java
index 8a5e924a76c7..0ea39ed5da30 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SettingsPopup.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SettingsPopup.java
@@ -52,10 +52,12 @@ class SettingsPopup {
private static final int ROW_LOG_PIN = 3;
private static final int ROW_RATE_PER = 4;
private static final int ROW_FOLDER = 5;
- private static final int ROW_AI_PROVIDER = 6;
- private static final int ROW_AI_MODEL = 7;
- private static final int ROW_AI_URL = 8;
- private static final int ROW_COUNT = 9;
+ private static final int ROW_SHELL_HISTORY = 6;
+ private static final int ROW_AI_PROVIDER = 7;
+ private static final int ROW_AI_MODEL = 8;
+ private static final int ROW_AI_URL = 9;
+ private static final int ROW_AI_PROMPT_HISTORY = 10;
+ private static final int ROW_COUNT = 11;
private static final String[] LOG_PIN_OPTIONS = { "off", "25", "50", "75"
};
private static final String[] RATE_PER_OPTIONS = { "seconds", "minutes" };
@@ -81,8 +83,10 @@ class SettingsPopup {
private int ratePerIndex;
private int aiProviderIndex;
private TextInputState folderInput;
+ private TextInputState shellHistoryInput;
private TextInputState aiModelInput;
private TextInputState aiUrlInput;
+ private TextInputState aiPromptHistoryInput;
private List<String> tabNames = new ArrayList<>();
private List<TabRegistry.TabEntry> tabEntries;
@@ -151,11 +155,14 @@ class SettingsPopup {
ratePerIndex = "minutes".equals(currentRatePer) ? 1 : 0;
folderInput = new TextInputState(settings.getDefaultFolder() != null ?
settings.getDefaultFolder() : "");
+ shellHistoryInput = new TextInputState(settings.getShellHistory() !=
null ? settings.getShellHistory() : "");
String currentProvider = settings.getAiProvider() != null ?
settings.getAiProvider() : "auto";
int providerIdx = AI_PROVIDERS.indexOf(currentProvider);
aiProviderIndex = providerIdx >= 0 ? providerIdx :
AI_PROVIDERS.indexOf("auto");
aiModelInput = new TextInputState(settings.getAiModel() != null ?
settings.getAiModel() : "");
aiUrlInput = new TextInputState(settings.getAiUrl() != null ?
settings.getAiUrl() : "");
+ aiPromptHistoryInput = new TextInputState(
+ settings.getAiPromptHistory() != null ?
settings.getAiPromptHistory() : "");
selectedRow = ROW_THEME;
visible = true;
}
@@ -244,6 +251,10 @@ class SettingsPopup {
handleTextInput(ke, folderInput);
return true;
}
+ if (selectedRow == ROW_SHELL_HISTORY) {
+ handleTextInput(ke, shellHistoryInput);
+ return true;
+ }
if (selectedRow == ROW_AI_PROVIDER) {
if (ke.isChar(' ') || ke.isRight()) {
aiProviderIndex = (aiProviderIndex + 1) % AI_PROVIDERS.size();
@@ -260,6 +271,10 @@ class SettingsPopup {
handleTextInput(ke, aiUrlInput);
return true;
}
+ if (selectedRow == ROW_AI_PROMPT_HISTORY) {
+ handleTextInput(ke, aiPromptHistoryInput);
+ return true;
+ }
return true;
}
@@ -281,9 +296,11 @@ class SettingsPopup {
monitorContext.ratePerMinute = "minutes".equals(ratePerValue);
}
settings.setDefaultFolder(stripControlChars(folderInput.text().trim()));
+
settings.setShellHistory(stripControlChars(shellHistoryInput.text().trim()));
settings.setAiProvider(AI_PROVIDERS.get(aiProviderIndex));
settings.setAiModel(stripControlChars(aiModelInput.text().trim()));
settings.setAiUrl(stripControlChars(aiUrlInput.text().trim()));
+
settings.setAiPromptHistory(stripControlChars(aiPromptHistoryInput.text().trim()));
settings.save();
if (Theme.mode().equals(selectedThemeId)) {
// Already active via live preview (or unchanged): just persist
and clear the preview marker.
@@ -297,7 +314,7 @@ class SettingsPopup {
}
void render(Frame frame, Rect area) {
- int popupW = Math.min(80, area.width() - 4);
+ int popupW = Math.min(60, area.width() - 4);
int popupH = 2 + ROW_COUNT;
int x = area.left() + Math.max(0, (area.width() - popupW) / 2);
int y = area.top() + 2;
@@ -344,6 +361,11 @@ class SettingsPopup {
renderFolder(frame, innerX + labelW, rowY, fieldW, selectedRow ==
ROW_FOLDER);
rowY++;
+ renderLabel(frame, innerX, rowY, labelW, "Shell History:", selectedRow
== ROW_SHELL_HISTORY);
+ renderTextInput(frame, innerX + labelW, rowY, fieldW,
shellHistoryInput,
+ selectedRow == ROW_SHELL_HISTORY, "(100)");
+ rowY++;
+
renderLabel(frame, innerX, rowY, labelW, "AI Provider:", selectedRow
== ROW_AI_PROVIDER);
renderValue(frame, innerX + labelW, rowY, fieldW,
AI_PROVIDERS.get(aiProviderIndex),
selectedRow == ROW_AI_PROVIDER);
@@ -355,12 +377,18 @@ class SettingsPopup {
renderLabel(frame, innerX, rowY, labelW, "AI Base URL:", selectedRow
== ROW_AI_URL);
renderTextInput(frame, innerX + labelW, rowY, fieldW, aiUrlInput,
selectedRow == ROW_AI_URL, "(auto)");
+ rowY++;
+
+ renderLabel(frame, innerX, rowY, labelW, "AI History:", selectedRow ==
ROW_AI_PROMPT_HISTORY);
+ renderTextInput(frame, innerX + labelW, rowY, fieldW,
aiPromptHistoryInput,
+ selectedRow == ROW_AI_PROMPT_HISTORY, "(100)");
}
void renderFooter(List<Span> spans) {
hint(spans, TuiIcons.HINT_SCROLL, "navigate");
if (selectedRow == ROW_THEME || selectedRow == ROW_START_TAB ||
selectedRow == ROW_SELECT_TAB
- || selectedRow == ROW_LOG_PIN || selectedRow == ROW_RATE_PER
|| selectedRow == ROW_AI_PROVIDER) {
+ || selectedRow == ROW_LOG_PIN || selectedRow == ROW_RATE_PER
+ || selectedRow == ROW_AI_PROVIDER) {
hint(spans, "Space", "cycle");
}
hint(spans, "Enter", "save");
@@ -500,4 +528,12 @@ class SettingsPopup {
String aiUrlText() {
return aiUrlInput != null ? aiUrlInput.text() : "";
}
+
+ String shellHistoryText() {
+ return shellHistoryInput != null ? shellHistoryInput.text() : "";
+ }
+
+ String aiPromptHistoryText() {
+ return aiPromptHistoryInput != null ? aiPromptHistoryInput.text() : "";
+ }
}
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/SettingsPopupRenderTest.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/SettingsPopupRenderTest.java
index 52d532aa23a4..e1683035b23a 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/SettingsPopupRenderTest.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/SettingsPopupRenderTest.java
@@ -82,5 +82,7 @@ class SettingsPopupRenderTest {
assertTrue(rendered.contains("AI Provider"), "the AI Provider row
should be shown");
assertTrue(rendered.contains("AI Model"), "the AI Model row should be
shown");
assertTrue(rendered.contains("AI Base URL"), "the AI Base URL row
should be shown");
+ assertTrue(rendered.contains("Shell History"), "the Shell History row
should be shown");
+ assertTrue(rendered.contains("AI History"), "the AI History row should
be shown");
}
}
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/SettingsPopupTest.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/SettingsPopupTest.java
index abfc218bc188..cea26dd4af25 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/SettingsPopupTest.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/SettingsPopupTest.java
@@ -176,7 +176,8 @@ class SettingsPopupTest {
popup.handleKeyEvent(key(KeyCode.DOWN));
popup.handleKeyEvent(key(KeyCode.DOWN));
popup.handleKeyEvent(key(KeyCode.DOWN));
- assertEquals(6, popup.selectedRow());
+ popup.handleKeyEvent(key(KeyCode.DOWN));
+ assertEquals(7, popup.selectedRow());
assertEquals("auto", popup.selectedAiProvider());
popup.handleKeyEvent(KeyEvent.ofChar(' '));
assertEquals("ollama", popup.selectedAiProvider());
@@ -201,6 +202,40 @@ class SettingsPopupTest {
assertEquals("https://example.test", persisted.getAiUrl());
}
+ @Test
+ void historyFieldsPersistValues(@TempDir Path tempDir) {
+ useHome(tempDir);
+ SettingsPopup popup = new SettingsPopup();
+ popup.setTabEntries(tabs());
+ popup.open();
+
+ // navigate to Shell History (row 6)
+ for (int i = 0; i < 6; i++) {
+ popup.handleKeyEvent(key(KeyCode.DOWN));
+ }
+ assertEquals(6, popup.selectedRow());
+ for (char c : "50".toCharArray()) {
+ popup.handleKeyEvent(KeyEvent.ofChar(c));
+ }
+ assertEquals("50", popup.shellHistoryText());
+
+ // navigate to AI Prompt History (row 10)
+ for (int i = 0; i < 4; i++) {
+ popup.handleKeyEvent(key(KeyCode.DOWN));
+ }
+ assertEquals(10, popup.selectedRow());
+ for (char c : "200".toCharArray()) {
+ popup.handleKeyEvent(KeyEvent.ofChar(c));
+ }
+ assertEquals("200", popup.aiPromptHistoryText());
+
+ popup.handleKeyEvent(key(KeyCode.ENTER));
+
+ TuiSettings persisted = TuiSettings.load();
+ assertEquals("50", persisted.getShellHistory());
+ assertEquals("200", persisted.getAiPromptHistory());
+ }
+
@Test
void enterPersistsSelectionsAndAppliesThemeLive(@TempDir Path tempDir)
throws Exception {
useHome(tempDir);