@@ -99,6 +99,12 @@ public class CamelMonitor extends CamelCommand {
9999 private static final int MAX_TRACES = 200 ;
100100 private static final int NUM_TABS = 10 ;
101101
102+ // Compact tab bar (10 labels + 9 "|" dividers) needs 88 chars — that is the true minimum
103+ private static final int MIN_WIDTH = 88 ;
104+ private static final int MIN_HEIGHT = 24 ;
105+ // Full tab bar (10 labels + 9 " | " dividers) needs 126 chars; use compact below that
106+ private static final int TABS_FULL_MIN_WIDTH = 126 ;
107+
102108 // Tab indices
103109 private static final int TAB_OVERVIEW = 0 ;
104110 private static final int TAB_LOG = 1 ;
@@ -968,6 +974,11 @@ private void navigateDown() {
968974 private void render (Frame frame ) {
969975 Rect area = frame .area ();
970976
977+ if (area .width () < MIN_WIDTH || area .height () < MIN_HEIGHT ) {
978+ renderTooSmall (frame , area );
979+ return ;
980+ }
981+
971982 // Layout: header (1 row) + spacer (1 row) + tabs (2 rows) + spacer (1 row) + content (fill) + footer (1 row)
972983 List <Rect > mainChunks = Layout .vertical ()
973984 .constraints (
@@ -1066,15 +1077,62 @@ private void renderHeader(Frame frame, Rect area) {
10661077 area );
10671078 }
10681079
1080+ private void renderTooSmall (Frame frame , Rect area ) {
1081+ Style orange = Style .EMPTY .fg (Color .rgb (0xF6 , 0x91 , 0x23 ));
1082+ Style normal = Style .EMPTY ;
1083+ Style bold = Style .EMPTY .bold ();
1084+
1085+ String line1 = "Terminal size too small:" ;
1086+ String wLabel = " Width = " ;
1087+ String wVal = String .valueOf (area .width ());
1088+ String hLabel = " Height = " ;
1089+ String hVal = String .valueOf (area .height ());
1090+ String line2 = wLabel + wVal + hLabel + hVal ;
1091+
1092+ String line4 = "Needed for current config:" ;
1093+ String line5 = " Width = " + MIN_WIDTH + " Height = " + MIN_HEIGHT ;
1094+
1095+ // 5 content lines (2 + blank + 2 + blank), center vertically
1096+ int startY = area .y () + Math .max (0 , (area .height () - 5 ) / 2 );
1097+
1098+ int x1 = area .x () + Math .max (0 , (area .width () - CharWidth .of (line1 )) / 2 );
1099+ frame .buffer ().setString (x1 , startY , line1 , bold );
1100+
1101+ int x2 = area .x () + Math .max (0 , (area .width () - CharWidth .of (line2 )) / 2 );
1102+ int wLabelW = CharWidth .of (wLabel );
1103+ int wValW = CharWidth .of (wVal );
1104+ int hLabelW = CharWidth .of (hLabel );
1105+ frame .buffer ().setString (x2 , startY + 1 , wLabel , normal );
1106+ frame .buffer ().setString (x2 + wLabelW , startY + 1 , wVal ,
1107+ area .width () < MIN_WIDTH ? orange : normal );
1108+ frame .buffer ().setString (x2 + wLabelW + wValW , startY + 1 , hLabel , normal );
1109+ frame .buffer ().setString (x2 + wLabelW + wValW + hLabelW , startY + 1 , hVal ,
1110+ area .height () < MIN_HEIGHT ? orange : normal );
1111+
1112+ int x4 = area .x () + Math .max (0 , (area .width () - CharWidth .of (line4 )) / 2 );
1113+ frame .buffer ().setString (x4 , startY + 3 , line4 , bold );
1114+
1115+ int x5 = area .x () + Math .max (0 , (area .width () - CharWidth .of (line5 )) / 2 );
1116+ frame .buffer ().setString (x5 , startY + 4 , line5 , normal );
1117+ }
1118+
10691119 private void renderTabs (Frame frame , Rect area ) {
1120+ boolean compact = area .width () < TABS_FULL_MIN_WIDTH ;
1121+ String dividerStr = compact ? "|" : " | " ;
1122+ Span divider = Span .styled (dividerStr , Style .EMPTY .dim ());
10701123 boolean infraSelected = isInfraSelected ();
10711124
10721125 if (infraSelected ) {
10731126 // Infra mode: only Overview and Log tabs
1074- Line [] labels = {
1075- Line .from (" 1 Overview " ),
1076- Line .from (" 2 Log " ),
1077- };
1127+ Line [] labels = compact
1128+ ? new Line [] {
1129+ Line .from ("1 Overview" ),
1130+ Line .from ("2 Log" ),
1131+ }
1132+ : new Line [] {
1133+ Line .from (" 1 Overview " ),
1134+ Line .from (" 2 Log " ),
1135+ };
10781136
10791137 // Map real tab index to infra tab index for highlight
10801138 int infraTabIdx = tabsState .selected () == TAB_LOG ? 1 : 0 ;
@@ -1083,7 +1141,7 @@ private void renderTabs(Frame frame, Rect area) {
10831141 Tabs tabs = Tabs .builder ()
10841142 .titles (labels )
10851143 .highlightStyle (Style .EMPTY .fg (Color .rgb (0xF6 , 0x91 , 0x23 )).bold ())
1086- .divider (Span . styled ( " | " , Style . EMPTY . dim ()) )
1144+ .divider (divider )
10871145 .build ();
10881146
10891147 Rect labelsArea = area .height () >= 2
@@ -1093,24 +1151,37 @@ private void renderTabs(Frame frame, Rect area) {
10931151 return ;
10941152 }
10951153
1096- Line [] labels = {
1097- Line .from (" 1 Overview " ),
1098- Line .from (" 2 Log " ),
1099- Line .from (" 3 Diagram " ),
1100- Line .from (routesTab .isTopMode () ? " 4 Top " : " 4 Route " ),
1101- Line .from (" 5 Endpoint " ),
1102- Line .from (" 6 HTTP " ),
1103- Line .from (" 7 Health " ),
1104- Line .from (" 8 Inspect " ),
1105- Line .from (" 9 Errors " ),
1106- Line .from (" 0 More▾ " ),
1107- };
1154+ Line [] labels = compact
1155+ ? new Line [] {
1156+ Line .from ("1 Overview" ),
1157+ Line .from ("2 Log" ),
1158+ Line .from ("3 Diagram" ),
1159+ Line .from (routesTab .isTopMode () ? "4 Top " : "4 Route" ),
1160+ Line .from ("5 Endpoint" ),
1161+ Line .from ("6 HTTP" ),
1162+ Line .from ("7 Health" ),
1163+ Line .from ("8 Inspect" ),
1164+ Line .from ("9 Errors" ),
1165+ Line .from ("0 More▾" ),
1166+ }
1167+ : new Line [] {
1168+ Line .from (" 1 Overview " ),
1169+ Line .from (" 2 Log " ),
1170+ Line .from (" 3 Diagram " ),
1171+ Line .from (routesTab .isTopMode () ? " 4 Top " : " 4 Route " ),
1172+ Line .from (" 5 Endpoint " ),
1173+ Line .from (" 6 HTTP " ),
1174+ Line .from (" 7 Health " ),
1175+ Line .from (" 8 Inspect " ),
1176+ Line .from (" 9 Errors " ),
1177+ Line .from (" 0 More▾ " ),
1178+ };
11081179 currentTabLabels = labels ;
11091180
11101181 Tabs tabs = Tabs .builder ()
11111182 .titles (labels )
11121183 .highlightStyle (Style .EMPTY .fg (Color .rgb (0xF6 , 0x91 , 0x23 )).bold ())
1113- .divider (Span . styled ( " | " , Style . EMPTY . dim ()) )
1184+ .divider (divider )
11141185 .build ();
11151186
11161187 Rect labelsArea = area .height () >= 2
@@ -1124,7 +1195,7 @@ private void renderTabs(Frame frame, Rect area) {
11241195 computeTabBadges (badgeTexts , badgeStyles );
11251196
11261197 int badgeY = area .y ();
1127- int dividerW = CharWidth .of (" | " );
1198+ int dividerW = CharWidth .of (dividerStr );
11281199 int tabX = 0 ;
11291200 for (int i = 0 ; i < labels .length ; i ++) {
11301201 if (i > 0 ) {
@@ -1655,6 +1726,7 @@ private void renderFooter(Frame frame, Rect area) {
16551726 screenshotMessage = null ;
16561727
16571728 List <Span > spans = new ArrayList <>();
1729+ int secondaryFKey = 0 ;
16581730
16591731 if (helpOverlay .isVisible ()) {
16601732 helpOverlay .renderFooter (spans );
@@ -1684,10 +1756,10 @@ private void renderFooter(Frame frame, Rect area) {
16841756 MonitorTab tab = activeTab ();
16851757
16861758 if (tabsState .selected () == TAB_OVERVIEW ) {
1687- renderOverviewFooter (spans );
1759+ secondaryFKey = renderOverviewFooter (spans );
16881760 } else {
16891761 tab .renderFooter (spans );
1690- insertFKeyHints (spans );
1762+ secondaryFKey = insertFKeyHints (spans );
16911763 }
16921764 }
16931765
@@ -1733,9 +1805,34 @@ private void renderFooter(Frame frame, Rect area) {
17331805 }
17341806 }
17351807
1808+ int hintsWidth = spans .stream ().mapToInt (Span ::width ).sum ();
1809+ int rightWidth = rightSpans .stream ().mapToInt (Span ::width ).sum ();
1810+ int minGap = rightSpans .isEmpty () ? 0 : 1 ;
1811+
1812+ if (hintsWidth + rightWidth + minGap > area .width ()) {
1813+ // Drop decorative right-side content first
1814+ rightSpans .clear ();
1815+ rightWidth = 0 ;
1816+ minGap = 0 ;
1817+ // Drop secondary F-key hints (F2/F3/F6) before tab-specific action hints.
1818+ // They are inserted at position 2 (after the first tab hint), so the last
1819+ // secondary F-key pair sits at index (2 + secondaryFKey - 2).
1820+ while (secondaryFKey > 0 && hintsWidth > area .width ()) {
1821+ int dropIdx = 2 + secondaryFKey - 2 ;
1822+ Span labelSpan = spans .remove (dropIdx + 1 );
1823+ Span keySpan = spans .remove (dropIdx );
1824+ hintsWidth -= keySpan .width () + labelSpan .width ();
1825+ secondaryFKey -= 2 ;
1826+ }
1827+ // Then drop tab-specific hints from the tail, keeping at least 4 spans
1828+ while (spans .size () > 4 && hintsWidth > area .width ()) {
1829+ Span labelSpan = spans .remove (spans .size () - 1 );
1830+ Span keySpan = spans .remove (spans .size () - 1 );
1831+ hintsWidth -= keySpan .width () + labelSpan .width ();
1832+ }
1833+ }
1834+
17361835 if (!rightSpans .isEmpty ()) {
1737- int hintsWidth = spans .stream ().mapToInt (s -> s .width ()).sum ();
1738- int rightWidth = rightSpans .stream ().mapToInt (s -> s .width ()).sum ();
17391836 int gap = Math .max (1 , area .width () - hintsWidth - rightWidth );
17401837 spans .add (Span .raw (" " .repeat (gap )));
17411838 spans .addAll (rightSpans );
@@ -1744,11 +1841,12 @@ private void renderFooter(Frame frame, Rect area) {
17441841 frame .renderWidget (Paragraph .from (Line .from (spans )), area );
17451842 }
17461843
1747- private void insertFKeyHints (List <Span > spans ) {
1844+ private int insertFKeyHints (List <Span > spans ) {
17481845 int insertPos = Math .min (2 , spans .size ());
17491846 List <Span > fKeySpans = new ArrayList <>();
17501847 MonitorTab tab = activeTab ();
1751- if (tab != null && tab .getHelpText () != null ) {
1848+ boolean hasHelp = tab != null && tab .getHelpText () != null ;
1849+ if (hasHelp ) {
17521850 hint (fKeySpans , "F1" , "help" );
17531851 }
17541852 hint (fKeySpans , "F2" , "actions" );
@@ -1757,15 +1855,17 @@ private void insertFKeyHints(List<Span> spans) {
17571855 }
17581856 hint (fKeySpans , "F6" , "shell" );
17591857 spans .addAll (insertPos , fKeySpans );
1858+ // Return count of secondary (droppable) spans: F2/F3/F6 only, not F1
1859+ return fKeySpans .size () - (hasHelp ? 2 : 0 );
17601860 }
17611861
1762- private void renderOverviewFooter (List <Span > spans ) {
1862+ private int renderOverviewFooter (List <Span > spans ) {
17631863 if (actionsPopup .isVisible ()) {
17641864 actionsPopup .renderFooter (spans );
1765- return ;
1865+ return 0 ;
17661866 }
17671867 overviewTab .renderFooter (spans );
1768- insertFKeyHints (spans );
1868+ int secondaryFKey = insertFKeyHints (spans );
17691869 // Process action hints
17701870 if (ctx .selectedPid != null && !isInfraSelected ()) {
17711871 IntegrationInfo selInfo = findSelectedIntegration ();
@@ -1786,6 +1886,7 @@ private void renderOverviewFooter(List<Span> spans) {
17861886 hint (spans , "x" , "stop" );
17871887 hint (spans , "X" , "kill" );
17881888 }
1889+ return secondaryFKey ;
17891890 }
17901891
17911892 // ---- Data Loading ----
0 commit comments