@@ -113,6 +113,21 @@ const html = async (
113113 font-size: 0.85rem;
114114 box-sizing: border-box;
115115 }
116+ /* Column resize styles */
117+ table.display.data-table { table-layout: fixed; }
118+ table.display.data-table thead th { position: relative; }
119+ .th-resizer {
120+ position: absolute;
121+ right: 0;
122+ top: 0;
123+ width: 6px;
124+ height: 100%;
125+ cursor: col-resize;
126+ user-select: none;
127+ opacity: 0;
128+ transition: opacity 0.15s ease-in-out;
129+ }
130+ table.display.data-table thead th:hover .th-resizer { opacity: 1; }
116131 </style>
117132</head>
118133<body>
@@ -269,6 +284,50 @@ const html = async (
269284 });
270285 };
271286
287+ const setColWidth = (tableEl, colIndex, widthPx) => {
288+ const nth = colIndex + 1;
289+ const w = Math.max(50, widthPx) + 'px';
290+ const th = tableEl.querySelector('thead tr:first-child th:nth-child(' + nth + ')');
291+ if (th) th.style.width = w;
292+ const filterTh = tableEl.querySelector('thead tr.filter-row th:nth-child(' + nth + ')');
293+ if (filterTh) filterTh.style.width = w;
294+ const tds = tableEl.querySelectorAll('tbody tr td:nth-child(' + nth + ')');
295+ tds.forEach(td => { td.style.width = w; });
296+ };
297+
298+ const addColumnResizers = (tableEl) => {
299+ if (!tableEl || tableEl.dataset.resizers === '1') return;
300+ const ths = tableEl.querySelectorAll('thead tr:first-child th');
301+ ths.forEach((th, idx) => {
302+ if (th.querySelector('.th-resizer')) return;
303+ const handle = document.createElement('div');
304+ handle.className = 'th-resizer';
305+ th.appendChild(handle);
306+ let startX = 0;
307+ let startWidth = 0;
308+ const onMouseMove = (e) => {
309+ const dx = e.pageX - startX;
310+ setColWidth(tableEl, idx, startWidth + dx);
311+ };
312+ const onMouseUp = () => {
313+ document.removeEventListener('mousemove', onMouseMove);
314+ document.removeEventListener('mouseup', onMouseUp);
315+ document.body.style.cursor = '';
316+ };
317+ handle.addEventListener('mousedown', (e) => {
318+ e.preventDefault();
319+ startX = e.pageX;
320+ startWidth = th.offsetWidth;
321+ document.addEventListener('mousemove', onMouseMove);
322+ document.addEventListener('mouseup', onMouseUp);
323+ document.body.style.cursor = 'col-resize';
324+ });
325+ });
326+ tableEl.dataset.resizers = '1';
327+ // Ensure table layout
328+ tableEl.style.tableLayout = 'fixed';
329+ };
330+
272331 const initializeDataTablesIfPresent = async () => {
273332 try {
274333 // Ensure DataTables (ESM or UMD) is present
@@ -289,6 +348,7 @@ const html = async (
289348 ordering: true,
290349 orderMulti: true,
291350 pageLength: 25,
351+ autoWidth: false,
292352 // v1 fallback
293353 dom: 'lfrtip',
294354 // v2 layout API
@@ -351,6 +411,10 @@ const html = async (
351411 thead.appendChild(filterRow);
352412 }
353413 table.dataset.dtInit = '1';
414+ // Attach column resizers after DataTables and filters are in place
415+ addColumnResizers(table);
416+ // Let DataTables recalc
417+ try { if (dt && dt.columns) dt.columns().adjust(); } catch (e) {}
354418 });
355419 } catch (e) {
356420 console.error('DataTables init error', e);
0 commit comments