@@ -75,18 +75,47 @@ public static function render(PdoDb $db, Layout $layout, int $paneIndex, bool $a
7575 }
7676
7777 $ visibleHeight = $ content ['height ' ] - 1 ; // -1 for header
78- $ colWidth = (int )floor ($ content ['width ' ] / 4 );
78+ // Use full available width - getContentArea already accounts for borders
79+ $ availableWidth = $ content ['width ' ];
80+ $ colWidth = (int )floor ($ availableWidth / 4 );
7981
8082 // Header
8183 if (Terminal::supportsColors ()) {
8284 Terminal::bold ();
8385 }
8486 Terminal::moveTo ($ content ['row ' ], $ content ['col ' ]);
85- $ headerText = str_pad ('ID ' , $ colWidth ) . str_pad ('Time ' , $ colWidth ) . str_pad ('DB ' , $ colWidth ) . 'Query ' ;
86- // Truncate if too long
87- if (mb_strlen ($ headerText , 'UTF-8 ' ) > $ content ['width ' ]) {
88- $ headerText = mb_substr ($ headerText , 0 , $ content ['width ' ], 'UTF-8 ' );
87+ // Clear only the content area, not the entire line (to preserve right border)
88+ echo str_repeat (' ' , $ availableWidth );
89+ Terminal::moveTo ($ content ['row ' ], $ content ['col ' ]);
90+
91+ // Build header with UTF-8 aware padding
92+ $ headerId = 'ID ' ;
93+ $ headerTime = 'Time ' ;
94+ $ headerDb = 'DB ' ;
95+ $ headerQuery = 'Query ' ;
96+
97+ $ idLen = mb_strlen ($ headerId , 'UTF-8 ' );
98+ $ timeLen = mb_strlen ($ headerTime , 'UTF-8 ' );
99+ $ dbLen = mb_strlen ($ headerDb , 'UTF-8 ' );
100+
101+ $ headerIdPadded = $ headerId . str_repeat (' ' , max (0 , $ colWidth - $ idLen ));
102+ $ headerTimePadded = $ headerTime . str_repeat (' ' , max (0 , $ colWidth - $ timeLen ));
103+ $ headerDbPadded = $ headerDb . str_repeat (' ' , max (0 , $ colWidth - $ dbLen ));
104+
105+ // Calculate available width for Query header
106+ $ usedWidth = ($ colWidth * 3 );
107+ $ queryHeaderWidth = max (1 , $ availableWidth - $ usedWidth );
108+ $ headerQueryTruncated = mb_strlen ($ headerQuery , 'UTF-8 ' ) > $ queryHeaderWidth
109+ ? mb_substr ($ headerQuery , 0 , $ queryHeaderWidth , 'UTF-8 ' )
110+ : $ headerQuery ;
111+
112+ $ headerText = $ headerIdPadded . $ headerTimePadded . $ headerDbPadded . $ headerQueryTruncated ;
113+
114+ // Final safety check
115+ if (mb_strlen ($ headerText , 'UTF-8 ' ) > $ availableWidth ) {
116+ $ headerText = mb_substr ($ headerText , 0 , $ availableWidth , 'UTF-8 ' );
89117 }
118+
90119 echo $ headerText ;
91120 Terminal::reset ();
92121
@@ -114,7 +143,9 @@ public static function render(PdoDb $db, Layout $layout, int $paneIndex, bool $a
114143 }
115144
116145 Terminal::moveTo ($ displayRow , $ content ['col ' ]);
117- Terminal::clearLine (); // Clear line before rendering to avoid artifacts
146+ // Clear only the content area, not the entire line (to preserve right border)
147+ echo str_repeat (' ' , $ availableWidth );
148+ Terminal::moveTo ($ displayRow , $ content ['col ' ]);
118149
119150 $ isSelected = $ active && $ selectedIndex === $ i ;
120151 if ($ isSelected && Terminal::supportsColors ()) {
@@ -125,7 +156,6 @@ public static function render(PdoDb $db, Layout $layout, int $paneIndex, bool $a
125156 $ id = self ::truncate ((string )($ query ['id ' ] ?? $ query ['pid ' ] ?? $ query ['session_id ' ] ?? '' ), $ colWidth );
126157 $ time = self ::truncate ((string )($ query ['time ' ] ?? $ query ['duration ' ] ?? '' ), $ colWidth );
127158 $ dbName = self ::truncate ((string )($ query ['db ' ] ?? $ query ['database ' ] ?? $ query ['datname ' ] ?? '' ), $ colWidth );
128- $ queryText = self ::truncate ((string )($ query ['query ' ] ?? '' ), $ content ['width ' ] - ($ colWidth * 3 ));
129159
130160 // Highlight slow queries (> 5 seconds) if not selected
131161 if (!$ isSelected ) {
@@ -137,12 +167,68 @@ public static function render(PdoDb $db, Layout $layout, int $paneIndex, bool $a
137167 }
138168
139169 $ marker = $ isSelected ? '> ' : ' ' ;
140- $ rowText = $ marker . str_pad ($ id , $ colWidth - 2 ) . str_pad ($ time , $ colWidth ) . str_pad ($ dbName , $ colWidth ) . $ queryText ;
141- // Truncate if too long
142- if (mb_strlen ($ rowText , 'UTF-8 ' ) > $ content ['width ' ]) {
143- $ rowText = mb_substr ($ rowText , 0 , $ content ['width ' ], 'UTF-8 ' );
170+
171+ // Build row text with proper padding (using UTF-8 aware length)
172+ $ idPadded = $ id ;
173+ $ timePadded = $ time ;
174+ $ dbNamePadded = $ dbName ;
175+
176+ // Pad using UTF-8 aware method
177+ $ idLen = mb_strlen ($ id , 'UTF-8 ' );
178+ if ($ idLen < $ colWidth ) {
179+ $ idPadded = $ id . str_repeat (' ' , $ colWidth - $ idLen );
180+ }
181+
182+ $ timeLen = mb_strlen ($ time , 'UTF-8 ' );
183+ if ($ timeLen < $ colWidth ) {
184+ $ timePadded = $ time . str_repeat (' ' , $ colWidth - $ timeLen );
185+ }
186+
187+ $ dbNameLen = mb_strlen ($ dbName , 'UTF-8 ' );
188+ if ($ dbNameLen < $ colWidth ) {
189+ $ dbNamePadded = $ dbName . str_repeat (' ' , $ colWidth - $ dbNameLen );
190+ }
191+
192+ // Calculate actual used width after padding
193+ $ markerWidth = mb_strlen ($ marker , 'UTF-8 ' );
194+ $ idPaddedWidth = mb_strlen ($ idPadded , 'UTF-8 ' );
195+ $ timePaddedWidth = mb_strlen ($ timePadded , 'UTF-8 ' );
196+ $ dbNamePaddedWidth = mb_strlen ($ dbNamePadded , 'UTF-8 ' );
197+ $ usedWidth = $ markerWidth + $ idPaddedWidth + $ timePaddedWidth + $ dbNamePaddedWidth ;
198+
199+ // Calculate available width for query text
200+ $ queryTextWidth = max (1 , $ availableWidth - $ usedWidth );
201+ $ queryText = self ::truncate ((string )($ query ['query ' ] ?? '' ), $ queryTextWidth );
202+
203+ $ rowText = $ marker . $ idPadded . $ timePadded . $ dbNamePadded . $ queryText ;
204+
205+ // Final safety check - ensure total width doesn't exceed available width
206+ $ totalWidth = mb_strlen ($ rowText , 'UTF-8 ' );
207+ if ($ totalWidth > $ availableWidth ) {
208+ // Truncate queryText further if needed
209+ $ queryTextWidth = max (0 , $ queryTextWidth - ($ totalWidth - $ availableWidth ));
210+ $ queryText = self ::truncate ((string )($ query ['query ' ] ?? '' ), $ queryTextWidth );
211+ $ rowText = $ marker . $ idPadded . $ timePadded . $ dbNamePadded . $ queryText ;
212+ // Final truncation as last resort
213+ if (mb_strlen ($ rowText , 'UTF-8 ' ) > $ availableWidth ) {
214+ $ rowText = mb_substr ($ rowText , 0 , $ availableWidth , 'UTF-8 ' );
215+ }
144216 }
217+
218+ // Ensure we don't output more than available width (strict check)
219+ $ finalWidth = mb_strlen ($ rowText , 'UTF-8 ' );
220+ if ($ finalWidth > $ availableWidth ) {
221+ // Truncate to exact available width
222+ $ rowText = mb_substr ($ rowText , 0 , $ availableWidth , 'UTF-8 ' );
223+ }
224+
225+ // Output text and pad to exact width to ensure we don't overwrite anything
145226 echo $ rowText ;
227+ $ actualWidth = mb_strlen ($ rowText , 'UTF-8 ' );
228+ if ($ actualWidth < $ availableWidth ) {
229+ // Pad with spaces to fill exactly availableWidth (prevents any overflow)
230+ echo str_repeat (' ' , $ availableWidth - $ actualWidth );
231+ }
146232 Terminal::reset ();
147233
148234 $ lastDisplayRow = $ displayRow ; // Update last displayed row
@@ -160,7 +246,8 @@ public static function render(PdoDb $db, Layout $layout, int $paneIndex, bool $a
160246
161247 for ($ row = $ lastDisplayRow + 1 ; $ row < $ maxRow ; $ row ++) {
162248 Terminal::moveTo ($ row , $ content ['col ' ]);
163- Terminal::clearLine ();
249+ // Clear only the content area, not the entire line (to preserve right border)
250+ echo str_repeat (' ' , $ availableWidth );
164251 }
165252
166253 // Show scroll indicator
@@ -175,7 +262,9 @@ public static function render(PdoDb $db, Layout $layout, int $paneIndex, bool $a
175262
176263 if ($ scrollOffset > 0 || $ endIdx < count ($ queries )) {
177264 Terminal::moveTo ($ scrollIndicatorRow , $ content ['col ' ]);
178- Terminal::clearLine (); // Clear before rendering indicator
265+ // Clear only the content area, not the entire line (to preserve right border)
266+ echo str_repeat (' ' , $ availableWidth );
267+ Terminal::moveTo ($ scrollIndicatorRow , $ content ['col ' ]);
179268 if (Terminal::supportsColors ()) {
180269 Terminal::color (Terminal::COLOR_YELLOW );
181270 }
@@ -194,7 +283,8 @@ public static function render(PdoDb $db, Layout $layout, int $paneIndex, bool $a
194283 } else {
195284 // Clear scroll indicator row if no scrolling needed
196285 Terminal::moveTo ($ scrollIndicatorRow , $ content ['col ' ]);
197- Terminal::clearLine ();
286+ // Clear only the content area, not the entire line (to preserve right border)
287+ echo str_repeat (' ' , $ availableWidth );
198288 }
199289 }
200290
0 commit comments