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 0fb6d6c47ffc camel-tui: Dynamic tab visibility based on integration 
features
0fb6d6c47ffc is described below

commit 0fb6d6c47ffc8373e24bf9d30864ce3291c0207d
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Jul 27 13:45:37 2026 +0200

    camel-tui: Dynamic tab visibility based on integration features
    
    Hide More tabs that are not relevant for the selected integration.
    Tier 1 checks dev console IDs on classpath (kafka, resilience4j, micrometer,
    opentelemetry). Tier 2 checks runtime data (browseable endpoints,
    supervised route controller, datasources, HTTP endpoints, network
    services, producers). The connector reports devConsoles IDs and
    hasBrowseableEndpoints in the status JSON. The More popup, F2 Go-to
    popup, MCP tui_get_options, and keyboard shortcuts all filter inactive
    tabs. Switching integrations via F3 falls back to Overview if the
    current tab becomes inactive. Old Camel versions without devConsoles
    show all tabs. Also: show management endpoints by default in HTTP tab,
    expand Mgmt to Management, show group label for first More group,
    increase Page Up/Down jump to 10 entries.
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../camel/cli/connector/LocalCliConnector.java     | 13 +++
 .../dsl/jbang/core/commands/tui/ActionsPopup.java  |  6 ++
 .../dsl/jbang/core/commands/tui/CamelMonitor.java  | 17 ++++
 .../dsl/jbang/core/commands/tui/GotoTabPopup.java  | 41 +++++++--
 .../camel/dsl/jbang/core/commands/tui/HttpTab.java | 12 +--
 .../jbang/core/commands/tui/IntegrationInfo.java   |  3 +
 .../dsl/jbang/core/commands/tui/McpFacade.java     |  2 +-
 .../dsl/jbang/core/commands/tui/PopupManager.java  | 50 ++++++-----
 .../dsl/jbang/core/commands/tui/StatusParser.java  | 15 ++++
 .../dsl/jbang/core/commands/tui/TabRegistry.java   | 97 ++++++++++++++++++----
 .../jbang/core/commands/tui/PopupManagerTest.java  |  4 +
 11 files changed, 215 insertions(+), 45 deletions(-)

diff --git 
a/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java
 
b/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java
index 9dd068726e57..5b7d11c61dd4 100644
--- 
a/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java
+++ 
b/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java
@@ -49,6 +49,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
 import org.apache.camel.CamelContext;
 import org.apache.camel.CamelContextAware;
 import org.apache.camel.ConsumerTemplate;
+import org.apache.camel.Endpoint;
 import org.apache.camel.Exchange;
 import org.apache.camel.ExchangePattern;
 import org.apache.camel.Expression;
@@ -68,6 +69,7 @@ import org.apache.camel.model.RouteDefinition;
 import org.apache.camel.model.language.ExpressionDefinition;
 import org.apache.camel.spi.BacklogDebugger;
 import org.apache.camel.spi.BacklogTracer;
+import org.apache.camel.spi.BrowsableEndpoint;
 import org.apache.camel.spi.CliConnector;
 import org.apache.camel.spi.CliConnectorFactory;
 import org.apache.camel.spi.ContextReloadStrategy;
@@ -1661,7 +1663,18 @@ public class LocalCliConnector extends ServiceSupport 
implements CliConnector, C
                         root.put("sqlTrace", json);
                     }
                 }
+                JsonArray consoleIds = new JsonArray();
+                consoleIds.addAll(dcr.getConsoleIDs());
+                root.put("devConsoles", consoleIds);
+            }
+            boolean hasBrowseable = false;
+            for (Endpoint ep : getCamelContext().getEndpoints()) {
+                if (ep instanceof BrowsableEndpoint) {
+                    hasBrowseable = true;
+                    break;
+                }
             }
+            root.put("hasBrowseableEndpoints", hasBrowseable);
             // various details
             JsonObject mem = collectMemory();
             if (mem != null) {
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ActionsPopup.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ActionsPopup.java
index 65905a55fbf6..c5aff66eb649 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ActionsPopup.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ActionsPopup.java
@@ -216,6 +216,12 @@ class ActionsPopup {
         settingsPopup.setTabEntries(entries);
     }
 
+    void setActiveTabFilter(
+            java.util.function.Supplier<IntegrationInfo> integrationSupplier,
+            java.util.function.Supplier<List<TabRegistry.MoreTab>> 
moreTabsSupplier) {
+        gotoTabPopup.setActiveTabFilter(integrationSupplier, moreTabsSupplier);
+    }
+
     void setClearScreenAction(Runnable clearScreen) {
         settingsPopup.setClearScreen(clearScreen);
     }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CamelMonitor.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CamelMonitor.java
index 14f4418c8838..e24dde92c837 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CamelMonitor.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CamelMonitor.java
@@ -346,6 +346,7 @@ public class CamelMonitor extends CamelCommand {
                 }
             }
         });
+        actionsPopup.setActiveTabFilter(ctx::findSelectedIntegration, 
tabRegistry::moreTabs);
 
         popupManager = new PopupManager(
                 ctx, this::getNonVanishingIntegrations, tabRegistry::moreTabs, 
filesBrowser,
@@ -369,6 +370,22 @@ public class CamelMonitor extends CamelCommand {
                     public void stopSelectedProcess(boolean forceKill) {
                         CamelMonitor.this.stopSelectedProcess(forceKill);
                     }
+
+                    @Override
+                    public void fallbackToOverviewIfTabInactive() {
+                        if (tabRegistry.selectedTabIndex() == 
TabRegistry.TAB_MORE) {
+                            MonitorTab active = tabRegistry.getActiveMoreTab();
+                            if (active != null) {
+                                IntegrationInfo info = 
ctx.findSelectedIntegration();
+                                for (TabRegistry.MoreTab mt : 
tabRegistry.moreTabs()) {
+                                    if (mt.tab() == active && 
!TabRegistry.isMoreTabActive(mt, info)) {
+                                        
tabsState.select(TabRegistry.TAB_OVERVIEW);
+                                        break;
+                                    }
+                                }
+                            }
+                        }
+                    }
                 });
 
         tabRegistry.overviewTab().setActions(new OverviewTab.OverviewActions() 
{
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/GotoTabPopup.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/GotoTabPopup.java
index 633ffbf47737..ae28fa7aa250 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/GotoTabPopup.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/GotoTabPopup.java
@@ -46,16 +46,26 @@ class GotoTabPopup {
     private final ListState listState = new ListState();
     private final ScrollbarState scrollbarState = new ScrollbarState();
     private List<TabRegistry.TabEntry> allEntries;
+    private List<TabRegistry.TabEntry> activeEntries;
     private List<TabRegistry.TabEntry> filteredEntries;
     private Rect popupRect;
     private Runnable callback;
     private TabRegistry.TabEntry pendingEntry;
+    private java.util.function.Supplier<IntegrationInfo> integrationSupplier;
+    private java.util.function.Supplier<List<TabRegistry.MoreTab>> 
moreTabsSupplier;
 
     void setTabEntries(List<TabRegistry.TabEntry> entries, Runnable callback) {
         this.allEntries = entries;
         this.callback = callback;
     }
 
+    void setActiveTabFilter(
+            java.util.function.Supplier<IntegrationInfo> integrationSupplier,
+            java.util.function.Supplier<List<TabRegistry.MoreTab>> 
moreTabsSupplier) {
+        this.integrationSupplier = integrationSupplier;
+        this.moreTabsSupplier = moreTabsSupplier;
+    }
+
     boolean isVisible() {
         return visible;
     }
@@ -92,13 +102,13 @@ class GotoTabPopup {
             return true;
         }
         if (ke.isPageUp() || ke.isKey(KeyCode.PAGE_UP)) {
-            for (int i = 0; i < 5; i++) {
+            for (int i = 0; i < 10; i++) {
                 listState.selectPrevious();
             }
             return true;
         }
         if (ke.isPageDown() || ke.isKey(KeyCode.PAGE_DOWN)) {
-            for (int i = 0; i < 5; i++) {
+            for (int i = 0; i < 10; i++) {
                 listState.selectNext(size);
             }
             return true;
@@ -247,7 +257,7 @@ class GotoTabPopup {
             renderState.select(sel + 2);
         }
 
-        int total = allEntries != null ? allEntries.size() : 0;
+        int total = activeEntries != null ? activeEntries.size() : 0;
         int shown = filteredEntries.size();
         String title = shown == total
                 ? " Go to Tab (" + total + ") "
@@ -289,12 +299,14 @@ class GotoTabPopup {
             filteredEntries = List.of();
             return;
         }
+        activeEntries = filterActiveEntries(allEntries);
+        List<TabRegistry.TabEntry> active = activeEntries;
         if (!filter.hasFilter()) {
-            filteredEntries = new ArrayList<>(allEntries);
+            filteredEntries = new ArrayList<>(active);
         } else {
             filteredEntries = new ArrayList<>();
             String f = filter.filter();
-            for (TabRegistry.TabEntry entry : allEntries) {
+            for (TabRegistry.TabEntry entry : active) {
                 boolean nameMatch;
                 if (f.length() <= 2) {
                     nameMatch = entry.name().toLowerCase().contains(f);
@@ -311,6 +323,25 @@ class GotoTabPopup {
         listState.select(filteredEntries.isEmpty() ? null : 0);
     }
 
+    private List<TabRegistry.TabEntry> 
filterActiveEntries(List<TabRegistry.TabEntry> entries) {
+        if (integrationSupplier == null || moreTabsSupplier == null) {
+            return entries;
+        }
+        IntegrationInfo info = integrationSupplier.get();
+        List<TabRegistry.MoreTab> moreTabs = moreTabsSupplier.get();
+        List<TabRegistry.TabEntry> active = new ArrayList<>();
+        for (TabRegistry.TabEntry entry : entries) {
+            if (entry.moreIndex() >= 0 && entry.moreIndex() < moreTabs.size()) 
{
+                TabRegistry.MoreTab mt = moreTabs.get(entry.moreIndex());
+                if (!TabRegistry.isMoreTabActive(mt, info)) {
+                    continue;
+                }
+            }
+            active.add(entry);
+        }
+        return active;
+    }
+
     private void navigateToEntry(TabRegistry.TabEntry entry) {
         if (callback != null) {
             pendingEntry = entry;
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HttpTab.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HttpTab.java
index d6327c8d8b1d..a450d58120ee 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HttpTab.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HttpTab.java
@@ -80,7 +80,7 @@ class HttpTab extends AbstractTableTab {
     private final AtomicBoolean specLoading = new AtomicBoolean(false);
 
     private int filter;
-    private boolean showManagement;
+    private boolean showManagement = true;
 
     private boolean showSpec;
     private List<String> specLines = Collections.emptyList();
@@ -1249,7 +1249,7 @@ class HttpTab extends AbstractTableTab {
         }
         if (mgmtCount > 0) {
             spans.add(Span.raw("  "));
-            spans.add(Span.styled("Mgmt: ", Theme.label().dim()));
+            spans.add(Span.styled("Management: ", Theme.label().dim()));
             spans.add(Span.raw(mgmtCount + ""));
         }
         spans.add(Span.raw(" "));
@@ -1265,7 +1265,7 @@ class HttpTab extends AbstractTableTab {
             String produces = ep.produces != null ? ep.produces : "";
             String source;
             if (ep.management) {
-                source = "Mgmt";
+                source = "Management";
             } else if (ep.specification) {
                 source = "API Spec";
             } else if (ep.fromRest) {
@@ -1611,8 +1611,8 @@ class HttpTab extends AbstractTableTab {
                  METHOD  PATH                  TOTAL  CONSUMES          
PRODUCES          SOURCE        STATE
                  GET     /api/users            142    application/json  
application/json  REST(code)    Started
                  POST    /api/users            38     application/json  
application/json  REST(code)    Started
-                 GET     /observe/health       97                              
           Mgmt          Started
-                 GET     /observe/metrics      52                       
text/plain        Mgmt          Started
+                 GET     /observe/health       97                              
           Management          Started
+                 GET     /observe/metrics      52                       
text/plain        Management          Started
                  GET     /q/openapi            15                       
application/json  REST(contract) Started
                 ```
 
@@ -1623,7 +1623,7 @@ class HttpTab extends AbstractTableTab {
                 - **REST(code)** — Defined in Camel REST DSL code. The 
developer wrote `rest("/api").get("/users").to("direct:getUsers")` in the route 
definition
                 - **REST(contract)** — Generated from an OpenAPI/Swagger 
specification file (contract-first approach). The API structure comes from a 
`.json` or `.yaml` spec file
                 - **HTTP** — Registered directly via the `platform-http` 
component using `from("platform-http:/path")`
-                - **Mgmt** — Management endpoint added automatically by Camel 
for observability: health checks, metrics, OpenAPI specs, developer console
+                - **Management** — Management endpoint added automatically by 
Camel for observability: health checks, metrics, OpenAPI specs, developer 
console
 
                 ## HTTP Probe
 
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/IntegrationInfo.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/IntegrationInfo.java
index 6f3b6945be2e..2cb345b043d7 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/IntegrationInfo.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/IntegrationInfo.java
@@ -18,6 +18,7 @@ package org.apache.camel.dsl.jbang.core.commands.tui;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
 
 class IntegrationInfo {
     String pid;
@@ -109,4 +110,6 @@ class IntegrationInfo {
     long sqlTraceFailedCount;
     String httpServer;
     String readmeFiles;
+    Set<String> devConsoles = Set.of();
+    boolean hasBrowseableEndpoints;
 }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/McpFacade.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/McpFacade.java
index 3f7151ea6c89..5130e4767da6 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/McpFacade.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/McpFacade.java
@@ -313,7 +313,7 @@ class McpFacade {
     }
 
     List<TabRegistry.TabEntry> getTabEntries() {
-        return tabRegistry.allTabEntries();
+        return tabRegistry.allTabEntries(ctx != null ? 
ctx.findSelectedIntegration() : null);
     }
 
     List<String> getActionLabels() {
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/PopupManager.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/PopupManager.java
index 4d230b6c1497..f425cb17aebc 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/PopupManager.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/PopupManager.java
@@ -60,6 +60,8 @@ class PopupManager {
         void refreshLogData();
 
         void stopSelectedProcess(boolean forceKill);
+
+        void fallbackToOverviewIfTabInactive();
     }
 
     private final MonitorContext ctx;
@@ -100,14 +102,16 @@ class PopupManager {
 
     private List<TabRegistry.MoreTab> buildMoreVisualList() {
         List<TabRegistry.MoreTab> tabs = moreTabsSupplier.get();
+        IntegrationInfo info = ctx.findSelectedIntegration();
         List<TabRegistry.MoreTab> visual = new ArrayList<>();
         String currentGroup = null;
         for (TabRegistry.MoreTab tab : tabs) {
+            if (!TabRegistry.isMoreTabActive(tab, info)) {
+                continue;
+            }
             String group = tab.group();
             if (group != null && !group.equals(currentGroup)) {
-                if (currentGroup != null) {
-                    visual.add(null);
-                }
+                visual.add(null);
                 currentGroup = group;
             }
             visual.add(tab);
@@ -129,24 +133,21 @@ class PopupManager {
         if (visualIndex < 0 || visualIndex >= visual.size() || 
visual.get(visualIndex) == null) {
             return -1;
         }
-        int moreIndex = 0;
-        for (int i = 0; i < visualIndex; i++) {
-            if (visual.get(i) != null) {
-                moreIndex++;
-            }
-        }
-        return moreIndex;
+        TabRegistry.MoreTab selected = visual.get(visualIndex);
+        List<TabRegistry.MoreTab> fullList = moreTabsSupplier.get();
+        return fullList.indexOf(selected);
     }
 
     private int moreToVisualIndex(int moreIndex) {
+        List<TabRegistry.MoreTab> fullList = moreTabsSupplier.get();
+        if (moreIndex < 0 || moreIndex >= fullList.size()) {
+            return 0;
+        }
+        TabRegistry.MoreTab target = fullList.get(moreIndex);
         List<TabRegistry.MoreTab> visual = buildMoreVisualList();
-        int count = 0;
         for (int i = 0; i < visual.size(); i++) {
-            if (visual.get(i) != null) {
-                if (count == moreIndex) {
-                    return i;
-                }
-                count++;
+            if (target.equals(visual.get(i))) {
+                return i;
             }
         }
         return 0;
@@ -199,7 +200,11 @@ class PopupManager {
     void openMorePopup() {
         showMorePopup = !showMorePopup;
         if (showMorePopup) {
-            morePopupState.select(lastMoreSelection);
+            int sel = lastMoreSelection;
+            if (isMoreDividerIndex(sel)) {
+                sel++;
+            }
+            morePopupState.select(sel);
         }
     }
 
@@ -373,6 +378,7 @@ class PopupManager {
                 ctx.selectedPid = chosen.pid;
                 ctx.lastSelectedName = chosen.name;
                 callbacks.resetIntegrationTabState();
+                callbacks.fallbackToOverviewIfTabInactive();
                 if (selectedTab == tabLog) {
                     callbacks.refreshLogData();
                 }
@@ -480,6 +486,7 @@ class PopupManager {
             ctx.selectedPid = chosen.pid;
             ctx.lastSelectedName = chosen.name;
             callbacks.resetIntegrationTabState();
+            callbacks.fallbackToOverviewIfTabInactive();
             if (selectedTab == tabLog) {
                 callbacks.refreshLogData();
             }
@@ -685,15 +692,20 @@ class PopupManager {
 
     int[] morePopupShortcut(KeyEvent ke) {
         List<TabRegistry.MoreTab> tabs = moreTabsSupplier.get();
+        IntegrationInfo info = ctx.findSelectedIntegration();
         List<Integer> matches = new ArrayList<>();
         char pressedUpper = 0;
 
         for (int i = 0; i < tabs.size(); i++) {
-            int idx = tabs.get(i).mnemonicIndex();
+            TabRegistry.MoreTab tab = tabs.get(i);
+            if (!TabRegistry.isMoreTabActive(tab, info)) {
+                continue;
+            }
+            int idx = tab.mnemonicIndex();
             if (idx < 0) {
                 continue;
             }
-            char letter = tabs.get(i).displayName().charAt(idx);
+            char letter = tab.displayName().charAt(idx);
             if (ke.isChar(Character.toLowerCase(letter)) || 
ke.isChar(Character.toUpperCase(letter))) {
                 if (pressedUpper == 0) {
                     pressedUpper = Character.toUpperCase(letter);
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/StatusParser.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/StatusParser.java
index 0f6ab05993cc..e7a725f2049a 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/StatusParser.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/StatusParser.java
@@ -21,9 +21,11 @@ import java.time.Instant;
 import java.time.ZoneId;
 import java.util.ArrayList;
 import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Set;
 
 import org.apache.camel.dsl.jbang.core.common.ProcessHelper;
 import org.apache.camel.dsl.jbang.core.common.VersionHelper;
@@ -676,6 +678,19 @@ final class StatusParser {
             }
         }
 
+        // Parse registered dev console IDs
+        JsonArray devConsolesArr = (JsonArray) root.get("devConsoles");
+        if (devConsolesArr != null) {
+            Set<String> ids = new LinkedHashSet<>();
+            for (Object o : devConsolesArr) {
+                ids.add(o.toString());
+            }
+            info.devConsoles = Set.copyOf(ids);
+        }
+        if (root.containsKey("hasBrowseableEndpoints")) {
+            info.hasBrowseableEndpoints = 
root.getBooleanOrDefault("hasBrowseableEndpoints", false);
+        }
+
         return info;
     }
 
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TabRegistry.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TabRegistry.java
index a5715381a39e..3d667e06667a 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TabRegistry.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TabRegistry.java
@@ -18,6 +18,7 @@ package org.apache.camel.dsl.jbang.core.commands.tui;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.function.Predicate;
 
 import dev.tamboui.widgets.tabs.TabsState;
 
@@ -166,28 +167,50 @@ class TabRegistry {
         // The group field drives divider rendering in the More popup.
         moreTabs = List.of(
                 // Routing
-                new MoreTab(TuiIcons.TAB_BROWSE, "Browse Endpoints", "&Browse 
Endpoints", browseTab, "Routing"),
+                new MoreTab(
+                        TuiIcons.TAB_BROWSE, "Browse Endpoints", "&Browse 
Endpoints", browseTab, "Routing",
+                        List.of(), info -> info.hasBrowseableEndpoints),
                 new MoreTab(TuiIcons.TAB_CONSUMERS, "Consumers", "&Consumers", 
consumersTab, "Routing"),
-                new MoreTab(TuiIcons.TAB_HTTP, "HTTP", "&HTTP", httpTab, 
"Routing"),
+                new MoreTab(
+                        TuiIcons.TAB_HTTP, "HTTP", "&HTTP", httpTab, "Routing",
+                        List.of("platform-http"), info -> 
!info.httpEndpoints.isEmpty()),
                 new MoreTab(TuiIcons.TAB_INFLIGHT, "Inflight", "&Inflight", 
inflightTab, "Routing"),
-                new MoreTab(TuiIcons.TAB_PRODUCERS, "Producers", "&Producers", 
producersTab, "Routing"),
+                new MoreTab(
+                        TuiIcons.TAB_PRODUCERS, "Producers", "&Producers", 
producersTab, "Routing",
+                        List.of(), info -> !info.producers.isEmpty()),
                 new MoreTab(
                         TuiIcons.TAB_ROUTE_CONTROLLER, "Route Controller", 
"&Route Controller", routeControllerTab,
-                        "Routing"),
+                        "Routing",
+                        List.of(), info -> 
"SupervisingRouteController".equals(info.routeControllerType)),
                 // Observability
                 new MoreTab(
                         TuiIcons.TAB_CIRCUIT_BREAKER, "Circuit Breaker", 
"&Circuit Breaker", circuitBreakerTab,
-                        "Observability"),
+                        "Observability",
+                        List.of("resilience4j", "fault-tolerance")),
                 new MoreTab(TuiIcons.TAB_HEALTH, "Health", "&Health", 
healthTab, "Observability"),
-                new MoreTab(TuiIcons.TAB_METRICS, "Metrics", "&Metrics", 
metricsTab, "Observability"),
-                new MoreTab(TuiIcons.TAB_NETWORK, "Network Services", 
"&Network Services", networkTab, "Observability"),
+                new MoreTab(
+                        TuiIcons.TAB_METRICS, "Metrics", "&Metrics", 
metricsTab, "Observability",
+                        List.of("micrometer")),
+                new MoreTab(
+                        TuiIcons.TAB_NETWORK, "Network Services", "&Network 
Services", networkTab, "Observability",
+                        List.of(), info -> !info.services.isEmpty()),
                 new MoreTab(TuiIcons.TAB_EVENTS, "Events", "&Exchange Events", 
eventTab, "Observability"),
-                new MoreTab(TuiIcons.TAB_SPANS, "Spans", "&OTel Spans", 
spansTab, "Observability"),
+                new MoreTab(
+                        TuiIcons.TAB_SPANS, "Spans", "&OTel Spans", spansTab, 
"Observability",
+                        List.of("opentelemetry")),
                 // Data
-                new MoreTab(TuiIcons.TAB_DATASOURCE, "JDBC DataSource", "&JDBC 
DataSource", dataSourceTab, "Data"),
-                new MoreTab(TuiIcons.TAB_KAFKA, "Kafka", "&Kafka", kafkaTab, 
"Data"),
-                new MoreTab(TuiIcons.TAB_SQL_QUERY, "SQL Query", "S&QL Query", 
sqlQueryTab, "Data"),
-                new MoreTab(TuiIcons.TAB_SQL_TRACE, "SQL Trace", "S&QL Trace", 
sqlTraceTab, "Data"),
+                new MoreTab(
+                        TuiIcons.TAB_DATASOURCE, "JDBC DataSource", "&JDBC 
DataSource", dataSourceTab, "Data",
+                        List.of(), info -> !info.dataSources.isEmpty()),
+                new MoreTab(
+                        TuiIcons.TAB_KAFKA, "Kafka", "&Kafka", kafkaTab, 
"Data",
+                        List.of("kafka")),
+                new MoreTab(
+                        TuiIcons.TAB_SQL_QUERY, "SQL Query", "S&QL Query", 
sqlQueryTab, "Data",
+                        List.of(), info -> !info.dataSources.isEmpty()),
+                new MoreTab(
+                        TuiIcons.TAB_SQL_TRACE, "SQL Trace", "S&QL Trace", 
sqlTraceTab, "Data",
+                        List.of(), info -> !info.dataSources.isEmpty()),
                 // JVM
                 new MoreTab(TuiIcons.TAB_CLASSPATH, "Classpath", "&Classpath", 
classpathTab, "JVM"),
                 new MoreTab(TuiIcons.TAB_HEAP, "Heap Memory Histogram", "Heap 
&Memory Histogram", heapHistogramTab, "JVM"),
@@ -415,10 +438,25 @@ class TabRegistry {
      * shortcut letter and its highlight offset are derived from {@code label} 
via
      * {@link TuiIcons#mnemonicIndex(String)}, so there is no separate index 
or shortcut list to keep aligned.
      */
-    record MoreTab(String icon, String name, String label, MonitorTab tab, 
String group) {
+    /**
+     * @param requiredConsoles dev console IDs that must be present (any-of) 
for this tab to be active; empty = always
+     *                         active
+     * @param activeWhen       runtime predicate on IntegrationInfo; null = 
always active (after console check)
+     */
+    record MoreTab(String icon, String name, String label, MonitorTab tab, 
String group,
+            List<String> requiredConsoles, Predicate<IntegrationInfo> 
activeWhen) {
+
+        MoreTab(String icon, String name, String label, MonitorTab tab, String 
group,
+                List<String> requiredConsoles) {
+            this(icon, name, label, tab, group, requiredConsoles, null);
+        }
+
+        MoreTab(String icon, String name, String label, MonitorTab tab, String 
group) {
+            this(icon, name, label, tab, group, List.of(), null);
+        }
 
         MoreTab(String icon, String name, String label, MonitorTab tab) {
-            this(icon, name, label, tab, null);
+            this(icon, name, label, tab, null, List.of(), null);
         }
 
         MoreTab {
@@ -446,6 +484,30 @@ class TabRegistry {
         return moreTabs;
     }
 
+    static boolean isMoreTabActive(MoreTab mt, IntegrationInfo info) {
+        if (mt.requiredConsoles().isEmpty() && mt.activeWhen() == null) {
+            return true;
+        }
+        if (info == null) {
+            return true;
+        }
+        if (!mt.requiredConsoles().isEmpty()) {
+            if (info.devConsoles.isEmpty()) {
+                // old Camel without devConsoles reporting — show all
+            } else if 
(mt.requiredConsoles().stream().noneMatch(info.devConsoles::contains)) {
+                return false;
+            }
+        }
+        if (mt.activeWhen() != null && !mt.activeWhen().test(info)) {
+            return false;
+        }
+        return true;
+    }
+
+    List<MoreTab> activeMoreTabs(IntegrationInfo info) {
+        return moreTabs.stream().filter(mt -> isMoreTabActive(mt, 
info)).toList();
+    }
+
     /** Position of the More tab with the given programmatic {@link 
MoreTab#name() name}, or -1 when absent. */
     int moreTabIndex(String name) {
         for (int i = 0; i < moreTabs.size(); i++) {
@@ -457,6 +519,10 @@ class TabRegistry {
     }
 
     List<TabEntry> allTabEntries() {
+        return allTabEntries(null);
+    }
+
+    List<TabEntry> allTabEntries(IntegrationInfo info) {
         List<TabEntry> entries = new ArrayList<>();
         entries.add(
                 new TabEntry(icon(TAB_OVERVIEW), "Overview", 
overviewTab.description(), "1", TAB_OVERVIEW, -1, overviewTab));
@@ -472,6 +538,9 @@ class TabRegistry {
         entries.add(new TabEntry(icon(TAB_ERRORS), "Errors", 
errorsTab.description(), "9", TAB_ERRORS, -1, errorsTab));
         for (int i = 0; i < moreTabs.size(); i++) {
             MoreTab mt = moreTabs.get(i);
+            if (!isMoreTabActive(mt, info)) {
+                continue;
+            }
             entries.add(new TabEntry(
                     mt.icon(), mt.name(), mt.tab().description(), 
String.valueOf(mt.shortcut()),
                     TAB_MORE, i, mt.tab()));
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/PopupManagerTest.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/PopupManagerTest.java
index 9a8e7c4b9e32..c95c96e6008f 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/PopupManagerTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/PopupManagerTest.java
@@ -69,6 +69,10 @@ class PopupManagerTest {
             @Override
             public void stopSelectedProcess(boolean forceKill) {
             }
+
+            @Override
+            public void fallbackToOverviewIfTabInactive() {
+            }
         };
 
         popupManager = new PopupManager(

Reply via email to