@@ -211,6 +211,155 @@ function renderPlaceholder(message, type = "neutral") {
211211 container . appendChild ( emptyState ) ;
212212}
213213
214+ function showRowDetailModal ( row , columns , rowIndex ) {
215+ const utils = window . SQL4ALLExecutorUtils ;
216+
217+ // Remove any existing modal
218+ const existing = document . querySelector ( ".row-detail-overlay" ) ;
219+ if ( existing ) existing . remove ( ) ;
220+
221+ const overlay = document . createElement ( "div" ) ;
222+ overlay . className = "row-detail-overlay" ;
223+
224+ const modal = document . createElement ( "div" ) ;
225+ modal . className = "row-detail-modal" ;
226+
227+ // Header
228+ const header = document . createElement ( "div" ) ;
229+ header . className = "row-detail-header" ;
230+ const title = document . createElement ( "span" ) ;
231+ title . className = "row-detail-title" ;
232+ title . textContent = `Row #${ rowIndex + 1 } ` ;
233+ const closeBtn = document . createElement ( "button" ) ;
234+ closeBtn . className = "row-detail-close" ;
235+ closeBtn . textContent = "\u00d7" ;
236+ closeBtn . title = "Close (Esc)" ;
237+ header . appendChild ( title ) ;
238+ header . appendChild ( closeBtn ) ;
239+ modal . appendChild ( header ) ;
240+
241+ // Body
242+ const body = document . createElement ( "div" ) ;
243+ body . className = "row-detail-body" ;
244+
245+ columns . forEach ( ( col ) => {
246+ const field = document . createElement ( "div" ) ;
247+ field . className = "row-detail-field" ;
248+
249+ const label = document . createElement ( "div" ) ;
250+ label . className = "row-detail-label" ;
251+ label . textContent = col ;
252+ label . title = col ;
253+
254+ const valueWrap = document . createElement ( "div" ) ;
255+ valueWrap . className = "row-detail-value-wrap" ;
256+
257+ const formatted = utils . formatCellValue ( row [ col ] ) ;
258+ const rawValue = formatted . title || formatted . text ;
259+
260+ const valueEl = document . createElement ( "div" ) ;
261+ valueEl . className = "row-detail-value" ;
262+ if ( formatted . className ) valueEl . classList . add ( formatted . className ) ;
263+ valueEl . textContent = typeof row [ col ] === "object" && row [ col ] !== null
264+ ? JSON . stringify ( row [ col ] , null , 2 )
265+ : formatted . text ;
266+
267+ const copyBtn = document . createElement ( "button" ) ;
268+ copyBtn . className = "row-detail-copy-btn" ;
269+ copyBtn . textContent = "Copy" ;
270+ copyBtn . title = "Copy value" ;
271+ copyBtn . addEventListener ( "click" , ( e ) => {
272+ e . stopPropagation ( ) ;
273+ navigator . clipboard . writeText ( rawValue ) . then ( ( ) => {
274+ copyBtn . textContent = "\u2713" ;
275+ copyBtn . classList . add ( "copied" ) ;
276+ setTimeout ( ( ) => {
277+ copyBtn . textContent = "Copy" ;
278+ copyBtn . classList . remove ( "copied" ) ;
279+ } , 1500 ) ;
280+ } ) ;
281+ } ) ;
282+
283+ valueWrap . appendChild ( valueEl ) ;
284+ valueWrap . appendChild ( copyBtn ) ;
285+ field . appendChild ( label ) ;
286+ field . appendChild ( valueWrap ) ;
287+ body . appendChild ( field ) ;
288+ } ) ;
289+
290+ modal . appendChild ( body ) ;
291+ overlay . appendChild ( modal ) ;
292+ document . body . appendChild ( overlay ) ;
293+
294+ // Close handlers
295+ function close ( ) {
296+ overlay . remove ( ) ;
297+ document . removeEventListener ( "keydown" , onKey ) ;
298+ }
299+ function onKey ( e ) {
300+ if ( e . key === "Escape" ) close ( ) ;
301+ }
302+ closeBtn . addEventListener ( "click" , close ) ;
303+ overlay . addEventListener ( "click" , ( e ) => {
304+ if ( e . target === overlay ) close ( ) ;
305+ } ) ;
306+ document . addEventListener ( "keydown" , onKey ) ;
307+ }
308+
309+ function initColumnResize ( table ) {
310+ // Measure natural column widths before switching to fixed layout
311+ const rowNumTh = table . querySelector ( "thead th.row-number-cell" ) ;
312+ const headers = table . querySelectorAll ( "thead th:not(.row-number-cell)" ) ;
313+
314+ const naturalWidths = Array . from ( headers ) . map ( ( th ) => th . offsetWidth ) ;
315+
316+ // Now lock widths and switch to fixed layout
317+ if ( rowNumTh ) {
318+ rowNumTh . style . width = "52px" ;
319+ }
320+ headers . forEach ( ( th , i ) => {
321+ th . style . width = naturalWidths [ i ] + "px" ;
322+ } ) ;
323+ table . style . tableLayout = "fixed" ;
324+
325+ let activeHandle = null ;
326+ let startX = 0 ;
327+ let startWidth = 0 ;
328+ let activeTh = null ;
329+
330+ function onMouseMove ( e ) {
331+ if ( ! activeTh ) return ;
332+ const delta = e . clientX - startX ;
333+ const newWidth = Math . max ( 40 , startWidth + delta ) ;
334+ activeTh . style . width = newWidth + "px" ;
335+ }
336+
337+ function onMouseUp ( ) {
338+ if ( activeHandle ) activeHandle . classList . remove ( "is-resizing" ) ;
339+ activeHandle = null ;
340+ activeTh = null ;
341+ document . removeEventListener ( "mousemove" , onMouseMove ) ;
342+ document . removeEventListener ( "mouseup" , onMouseUp ) ;
343+ document . body . style . cursor = "" ;
344+ document . body . style . userSelect = "" ;
345+ }
346+
347+ table . addEventListener ( "mousedown" , ( e ) => {
348+ const handle = e . target . closest ( ".resize-handle" ) ;
349+ if ( ! handle ) return ;
350+ e . preventDefault ( ) ;
351+ activeTh = handle . parentElement ;
352+ activeHandle = handle ;
353+ startX = e . clientX ;
354+ startWidth = activeTh . offsetWidth ;
355+ handle . classList . add ( "is-resizing" ) ;
356+ document . body . style . cursor = "col-resize" ;
357+ document . body . style . userSelect = "none" ;
358+ document . addEventListener ( "mousemove" , onMouseMove ) ;
359+ document . addEventListener ( "mouseup" , onMouseUp ) ;
360+ } ) ;
361+ }
362+
214363function renderResultsTable ( payload , elapsedSeconds ) {
215364 const utils = window . SQL4ALLExecutorUtils ;
216365 const container = getResultsContainer ( ) ;
@@ -255,8 +404,13 @@ function renderResultsTable(payload, elapsedSeconds) {
255404
256405 columns . forEach ( ( column ) => {
257406 const th = document . createElement ( "th" ) ;
258- th . textContent = column ;
407+ const label = document . createElement ( "span" ) ;
408+ label . textContent = column ;
409+ th . appendChild ( label ) ;
259410 th . title = column ;
411+ const resizeHandle = document . createElement ( "div" ) ;
412+ resizeHandle . className = "resize-handle" ;
413+ th . appendChild ( resizeHandle ) ;
260414 headerRow . appendChild ( th ) ;
261415 } ) ;
262416
@@ -286,6 +440,10 @@ function renderResultsTable(payload, elapsedSeconds) {
286440 }
287441 rows . forEach ( ( row , index ) => {
288442 const tr = document . createElement ( "tr" ) ;
443+ tr . style . cursor = "pointer" ;
444+ tr . addEventListener ( "dblclick" , ( ) => {
445+ showRowDetailModal ( row , columns , index ) ;
446+ } ) ;
289447
290448 const rowNumber = document . createElement ( "td" ) ;
291449 rowNumber . textContent = String ( index + 1 ) ;
@@ -310,6 +468,8 @@ function renderResultsTable(payload, elapsedSeconds) {
310468 container . innerHTML = "" ;
311469 container . appendChild ( table ) ;
312470
471+ initColumnResize ( table ) ;
472+
313473 setResultMetrics ( payload . rowCount , columns . length ) ;
314474 setExportState ( true ) ;
315475 setResultStatus (
0 commit comments