juxscript 1.1.194 → 1.1.195

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"dataframe.d.ts","sourceRoot":"","sources":["dataframe.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dataframe.d.ts","sourceRoot":"","sources":["dataframe.ts"],"names":[],"mappings":"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"}
@@ -3,7 +3,6 @@ import { DataFrame } from '../storage/DataFrame.js';
3
3
  import { TabularDriver } from '../storage/TabularDriver.js';
4
4
  import { FileUpload } from './fileupload.js';
5
5
  import { Table } from './table.js';
6
- import { Tabs } from './tabs.js';
7
6
  import { Modal } from './modal.js';
8
7
  import { renderIcon } from './icons.js';
9
8
  const TRIGGER_EVENTS = [];
@@ -253,25 +252,62 @@ export class DataFrameComponent extends BaseComponent {
253
252
  const existingTable = wrapper.querySelector('.jux-table-wrapper');
254
253
  if (existingTable)
255
254
  existingTable.remove();
255
+ const existingTabs = wrapper.querySelector('.jux-dataframe-tabs');
256
+ if (existingTabs)
257
+ existingTabs.remove();
256
258
  Object.entries(sheets).forEach(([name, df]) => {
257
259
  this._sheets.set(name, df);
258
260
  });
259
261
  const sheetNames = Object.keys(sheets);
260
- const tabs = new Tabs(`${this._id}-tabs`, {
261
- tabs: sheetNames.map(name => ({
262
- id: name,
263
- label: name,
264
- content: ''
265
- })),
266
- activeTab: sheetNames[0]
267
- });
268
- this._tabs = tabs;
262
+ // Build custom tab bar with settings cogs
269
263
  const tabsContainer = document.createElement('div');
270
264
  tabsContainer.className = 'jux-dataframe-tabs';
265
+ const tabList = document.createElement('div');
266
+ tabList.className = 'jux-tabs-list';
267
+ sheetNames.forEach((sheetName, idx) => {
268
+ const tabBtn = document.createElement('button');
269
+ tabBtn.className = 'jux-tabs-button' + (idx === 0 ? ' jux-tabs-button-active' : '');
270
+ tabBtn.setAttribute('data-sheet', sheetName);
271
+ const labelSpan = document.createElement('span');
272
+ labelSpan.textContent = sheetName;
273
+ tabBtn.appendChild(labelSpan);
274
+ // Add settings cog icon
275
+ const cogBtn = document.createElement('button');
276
+ cogBtn.className = 'jux-dataframe-tab-settings';
277
+ cogBtn.title = 'Import settings for ' + sheetName;
278
+ cogBtn.innerHTML = `<span class="iconify" data-icon="lucide:settings" style="width:14px;height:14px;"></span>`;
279
+ cogBtn.addEventListener('click', (e) => {
280
+ e.stopPropagation();
281
+ this._showReshapeModal();
282
+ });
283
+ tabBtn.appendChild(cogBtn);
284
+ tabBtn.addEventListener('click', () => {
285
+ // Switch active tab
286
+ tabList.querySelectorAll('.jux-tabs-button').forEach(btn => {
287
+ btn.classList.remove('jux-tabs-button-active');
288
+ });
289
+ tabBtn.classList.add('jux-tabs-button-active');
290
+ // Show/hide panels
291
+ wrapper.querySelectorAll('.jux-tabs-panel').forEach(panel => {
292
+ panel.style.display = 'none';
293
+ });
294
+ const panel = document.getElementById(`${this._id}-panel-${sheetName}`);
295
+ if (panel)
296
+ panel.style.display = 'block';
297
+ // Update current df reference
298
+ this._df = this._sheets.get(sheetName) || null;
299
+ });
300
+ tabList.appendChild(tabBtn);
301
+ });
302
+ tabsContainer.appendChild(tabList);
271
303
  wrapper.appendChild(tabsContainer);
272
- tabs.render(tabsContainer);
273
- sheetNames.forEach(sheetName => {
304
+ // Create panels for each sheet
305
+ sheetNames.forEach((sheetName, idx) => {
274
306
  const df = sheets[sheetName];
307
+ const panel = document.createElement('div');
308
+ panel.className = 'jux-tabs-panel';
309
+ panel.id = `${this._id}-panel-${sheetName}`;
310
+ panel.style.display = idx === 0 ? 'block' : 'none';
275
311
  const table = new Table(`${this._id}-table-${sheetName}`, {
276
312
  striped: this._tableOptions.striped,
277
313
  hoverable: this._tableOptions.hoverable,
@@ -282,10 +318,8 @@ export class DataFrameComponent extends BaseComponent {
282
318
  });
283
319
  const columnDefs = df.columns.map(col => ({ key: col, label: col }));
284
320
  table.columns(columnDefs).rows(df.toRows());
285
- const tabPanel = document.getElementById(`${this._id}-tabs-${sheetName}-panel`);
286
- if (!tabPanel)
287
- return;
288
- table.render(tabPanel);
321
+ wrapper.appendChild(panel);
322
+ table.render(panel);
289
323
  if (this._tableOptions.filterable) {
290
324
  const filterContainer = document.createElement('div');
291
325
  filterContainer.className = 'jux-dataframe-filter';
@@ -310,12 +344,18 @@ export class DataFrameComponent extends BaseComponent {
310
344
  const filtered = df.filter((row) => Object.values(row).some(v => v !== null && v !== undefined && String(v).toLowerCase().includes(text)));
311
345
  table.rows(filtered.toRows());
312
346
  });
313
- const tableWrapper = tabPanel.querySelector('.jux-table-wrapper');
347
+ const tableWrapper = panel.querySelector('.jux-table-wrapper');
314
348
  if (tableWrapper) {
315
- tabPanel.insertBefore(filterContainer, tableWrapper);
349
+ panel.insertBefore(filterContainer, tableWrapper);
316
350
  }
317
351
  }
318
352
  });
353
+ // Ensure Iconify renders the cog icons
354
+ requestAnimationFrame(() => {
355
+ if (window.Iconify) {
356
+ window.Iconify.scan();
357
+ }
358
+ });
319
359
  const totalRows = Object.values(sheets).reduce((sum, df) => sum + df.height, 0);
320
360
  this._updateStatus(`${sourceName} — ${sheetNames.length} sheets, ${totalRows} total rows`, 'success');
321
361
  this._df = sheets[sheetNames[0]];
@@ -574,30 +614,37 @@ export class DataFrameComponent extends BaseComponent {
574
614
  const lines = [];
575
615
  // Show skipped rows (rows before the header)
576
616
  if (rawSheet && headerRow > 0) {
617
+ lines.push('');
618
+ lines.push(` ┌${'─'.repeat(100)}┐`);
619
+ lines.push(` │ ROWS 0-${headerRow - 1} WILL BE SKIPPED (not imported)`.padEnd(101) + '│');
620
+ lines.push(` └${'─'.repeat(100)}┘`);
621
+ lines.push('');
577
622
  const rawRows = rawSheet.toRows();
578
- // Row 0 in raw parse is the raw header, rows after are data
579
- // Show raw header as row 0
580
623
  const rawCols = rawSheet.columns;
581
- lines.push(`${'Idx'.padEnd(idxWidth)}${rawCols.map(c => String(c).substring(0, colWidth - 2).padEnd(colWidth)).join('| ')}`);
624
+ // Show raw header (row 0) - dimmed
625
+ lines.push(`${'[0]'.padEnd(idxWidth)}${rawCols.map(c => String(c).substring(0, colWidth - 2).padEnd(colWidth)).join('│ ')} ← skipped`);
582
626
  const skippedCount = Math.min(headerRow - 1, rawRows.length);
583
627
  for (let i = 0; i < skippedCount; i++) {
584
628
  const row = rawRows[i];
585
- const rowIdx = String(i + 1).padEnd(idxWidth);
586
- const cols = Object.values(row).map(v => String(v ?? '').substring(0, colWidth - 2).padEnd(colWidth)).join('| ');
587
- lines.push(`${rowIdx}${cols}`);
629
+ const rowIdx = `[${i + 1}]`.padEnd(idxWidth);
630
+ const cols = Object.values(row).map(v => String(v ?? '').substring(0, colWidth - 2).padEnd(colWidth)).join(' ');
631
+ lines.push(`${rowIdx}${cols} ← skipped`);
588
632
  }
589
- // Separator: everything above is skipped
590
- lines.push(`${''.repeat(6)}${'── skipped '.padEnd(colWidth * Math.min(rawCols.length, 5), '─')}`);
633
+ lines.push('');
634
+ lines.push(` ╔${''.repeat(100)}╗`);
635
+ lines.push(` ║ ▼ DATA STARTS HERE (Row ${headerRow} = Column Headers)`.padEnd(101) + '║');
636
+ lines.push(` ╚${'═'.repeat(100)}╝`);
637
+ lines.push('');
591
638
  }
592
639
  // Show header row and data rows from parsed result
593
640
  if (parsedSheet) {
594
- const headerLine = `${''.padEnd(idxWidth)}${parsedSheet.columns.map(c => String(c).substring(0, colWidth - 2).padEnd(colWidth)).join('| ')}`;
641
+ const headerLine = `${'HDR'.padEnd(idxWidth)}${parsedSheet.columns.map(c => String(c).substring(0, colWidth - 2).padEnd(colWidth)).join(' ')} ◀ HEADERS`;
595
642
  lines.push(headerLine);
596
- lines.push(''.repeat(Math.min(headerLine.length, 140)));
643
+ lines.push(`${'───'.padEnd(idxWidth)}${'─'.repeat(Math.min(colWidth * parsedSheet.columns.length, 120))}`);
597
644
  const dataRows = parsedSheet.toRows().slice(0, 8);
598
645
  dataRows.forEach((row, i) => {
599
- const rowIdx = String(headerRow + 1 + i).padEnd(idxWidth);
600
- const cols = Object.values(row).map(v => String(v ?? '').substring(0, colWidth - 2).padEnd(colWidth)).join('| ');
646
+ const rowIdx = `[${headerRow + 1 + i}]`.padEnd(idxWidth);
647
+ const cols = Object.values(row).map(v => String(v ?? '').substring(0, colWidth - 2).padEnd(colWidth)).join(' ');
601
648
  lines.push(`${rowIdx}${cols}`);
602
649
  });
603
650
  }
@@ -736,17 +783,26 @@ export class DataFrameComponent extends BaseComponent {
736
783
  hasHeader: true,
737
784
  maxRows: totalOffset + 1
738
785
  });
786
+ lines.push('');
787
+ lines.push(` ┌${'─'.repeat(100)}┐`);
788
+ lines.push(` │ ROWS 0-${totalOffset - 1} WILL BE SKIPPED (not imported)`.padEnd(101) + '│');
789
+ lines.push(` └${'─'.repeat(100)}┘`);
790
+ lines.push('');
739
791
  const rawCols = rawDf.columns;
740
- lines.push(`${'Idx'.padEnd(idxWidth)}${rawCols.map(c => String(c).substring(0, colWidth - 2).padEnd(colWidth)).join('| ')}`);
792
+ lines.push(`${'[0]'.padEnd(idxWidth)}${rawCols.map(c => String(c).substring(0, colWidth - 2).padEnd(colWidth)).join(' ')} ← skipped`);
741
793
  const rawRows = rawDf.toRows();
742
794
  const skippedCount = Math.min(totalOffset - 1, rawRows.length);
743
795
  for (let i = 0; i < skippedCount; i++) {
744
796
  const row = rawRows[i];
745
- const rowIdx = String(i + 1).padEnd(idxWidth);
746
- const cols = Object.values(row).map(v => String(v ?? '').substring(0, colWidth - 2).padEnd(colWidth)).join('| ');
747
- lines.push(`${rowIdx}${cols}`);
797
+ const rowIdx = `[${i + 1}]`.padEnd(idxWidth);
798
+ const cols = Object.values(row).map(v => String(v ?? '').substring(0, colWidth - 2).padEnd(colWidth)).join(' ');
799
+ lines.push(`${rowIdx}${cols} ← skipped`);
748
800
  }
749
- lines.push(`${''.repeat(6)}${'── skipped '.padEnd(colWidth * Math.min(rawCols.length, 5), '─')}`);
801
+ lines.push('');
802
+ lines.push(` ╔${'═'.repeat(100)}╗`);
803
+ lines.push(` ║ ▼ DATA STARTS HERE (Row ${totalOffset} = Column Headers)`.padEnd(101) + '║');
804
+ lines.push(` ╚${'═'.repeat(100)}╝`);
805
+ lines.push('');
750
806
  }
751
807
  // Parse with actual settings for header + data rows
752
808
  const df = this._driver.parseCSV(this._rawFileData.text, {
@@ -756,13 +812,13 @@ export class DataFrameComponent extends BaseComponent {
756
812
  hasHeader: true,
757
813
  maxRows: 8
758
814
  });
759
- const headerLine = `${''.padEnd(idxWidth)}${df.columns.map(c => String(c).substring(0, colWidth - 2).padEnd(colWidth)).join('| ')}`;
815
+ const headerLine = `${'HDR'.padEnd(idxWidth)}${df.columns.map(c => String(c).substring(0, colWidth - 2).padEnd(colWidth)).join(' ')} ◀ HEADERS`;
760
816
  lines.push(headerLine);
761
- lines.push(''.repeat(Math.min(headerLine.length, 140)));
817
+ lines.push(`${'───'.padEnd(idxWidth)}${'─'.repeat(Math.min(colWidth * df.columns.length, 120))}`);
762
818
  const dataRows = df.toRows();
763
819
  dataRows.forEach((row, i) => {
764
- const rowIdx = String(totalOffset + 1 + i).padEnd(idxWidth);
765
- const cols = Object.values(row).map(v => String(v ?? '').substring(0, colWidth - 2).padEnd(colWidth)).join('| ');
820
+ const rowIdx = `[${totalOffset + 1 + i}]`.padEnd(idxWidth);
821
+ const cols = Object.values(row).map(v => String(v ?? '').substring(0, colWidth - 2).padEnd(colWidth)).join(' ');
766
822
  lines.push(`${rowIdx}${cols}`);
767
823
  });
768
824
  if (previewDiv) {
@@ -314,32 +314,75 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
314
314
  const existingTable = wrapper.querySelector('.jux-table-wrapper');
315
315
  if (existingTable) existingTable.remove();
316
316
 
317
+ const existingTabs = wrapper.querySelector('.jux-dataframe-tabs');
318
+ if (existingTabs) existingTabs.remove();
319
+
317
320
  Object.entries(sheets).forEach(([name, df]) => {
318
321
  this._sheets.set(name, df);
319
322
  });
320
323
 
321
324
  const sheetNames = Object.keys(sheets);
322
325
 
323
- const tabs = new Tabs(`${this._id}-tabs`, {
324
- tabs: sheetNames.map(name => ({
325
- id: name,
326
- label: name,
327
- content: ''
328
- })),
329
- activeTab: sheetNames[0]
330
- });
331
-
332
- this._tabs = tabs;
333
-
326
+ // Build custom tab bar with settings cogs
334
327
  const tabsContainer = document.createElement('div');
335
328
  tabsContainer.className = 'jux-dataframe-tabs';
336
- wrapper.appendChild(tabsContainer);
337
329
 
338
- tabs.render(tabsContainer);
330
+ const tabList = document.createElement('div');
331
+ tabList.className = 'jux-tabs-list';
332
+
333
+ sheetNames.forEach((sheetName, idx) => {
334
+ const tabBtn = document.createElement('button');
335
+ tabBtn.className = 'jux-tabs-button' + (idx === 0 ? ' jux-tabs-button-active' : '');
336
+ tabBtn.setAttribute('data-sheet', sheetName);
337
+
338
+ const labelSpan = document.createElement('span');
339
+ labelSpan.textContent = sheetName;
340
+ tabBtn.appendChild(labelSpan);
341
+
342
+ // Add settings cog icon
343
+ const cogBtn = document.createElement('button');
344
+ cogBtn.className = 'jux-dataframe-tab-settings';
345
+ cogBtn.title = 'Import settings for ' + sheetName;
346
+ cogBtn.innerHTML = `<span class="iconify" data-icon="lucide:settings" style="width:14px;height:14px;"></span>`;
347
+ cogBtn.addEventListener('click', (e) => {
348
+ e.stopPropagation();
349
+ this._showReshapeModal();
350
+ });
351
+ tabBtn.appendChild(cogBtn);
352
+
353
+ tabBtn.addEventListener('click', () => {
354
+ // Switch active tab
355
+ tabList.querySelectorAll('.jux-tabs-button').forEach(btn => {
356
+ btn.classList.remove('jux-tabs-button-active');
357
+ });
358
+ tabBtn.classList.add('jux-tabs-button-active');
359
+
360
+ // Show/hide panels
361
+ wrapper.querySelectorAll('.jux-tabs-panel').forEach(panel => {
362
+ (panel as HTMLElement).style.display = 'none';
363
+ });
364
+ const panel = document.getElementById(`${this._id}-panel-${sheetName}`);
365
+ if (panel) panel.style.display = 'block';
366
+
367
+ // Update current df reference
368
+ this._df = this._sheets.get(sheetName) || null;
369
+ });
370
+
371
+ tabList.appendChild(tabBtn);
372
+ });
373
+
374
+ tabsContainer.appendChild(tabList);
375
+ wrapper.appendChild(tabsContainer);
339
376
 
340
- sheetNames.forEach(sheetName => {
377
+ // Create panels for each sheet
378
+ sheetNames.forEach((sheetName, idx) => {
341
379
  const df = sheets[sheetName];
342
380
 
381
+ const panel = document.createElement('div');
382
+ panel.className = 'jux-tabs-panel';
383
+ panel.id = `${this._id}-panel-${sheetName}`;
384
+ panel.style.display = idx === 0 ? 'block' : 'none';
385
+
343
386
  const table = new Table(`${this._id}-table-${sheetName}`, {
344
387
  striped: this._tableOptions.striped,
345
388
  hoverable: this._tableOptions.hoverable,
@@ -352,10 +395,8 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
352
395
  const columnDefs = df.columns.map(col => ({ key: col, label: col }));
353
396
  table.columns(columnDefs).rows(df.toRows());
354
397
 
355
- const tabPanel = document.getElementById(`${this._id}-tabs-${sheetName}-panel`);
356
- if (!tabPanel) return;
357
-
358
- table.render(tabPanel);
398
+ wrapper.appendChild(panel);
399
+ table.render(panel);
359
400
 
360
401
  if (this._tableOptions.filterable) {
361
402
  const filterContainer = document.createElement('div');
@@ -388,13 +429,20 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
388
429
  table.rows(filtered.toRows());
389
430
  });
390
431
 
391
- const tableWrapper = tabPanel.querySelector('.jux-table-wrapper');
432
+ const tableWrapper = panel.querySelector('.jux-table-wrapper');
392
433
  if (tableWrapper) {
393
- tabPanel.insertBefore(filterContainer, tableWrapper);
434
+ panel.insertBefore(filterContainer, tableWrapper);
394
435
  }
395
436
  }
396
437
  });
397
438
 
439
+ // Ensure Iconify renders the cog icons
440
+ requestAnimationFrame(() => {
441
+ if ((window as any).Iconify) {
442
+ (window as any).Iconify.scan();
443
+ }
444
+ });
445
+
398
446
  const totalRows = Object.values(sheets).reduce((sum, df) => sum + df.height, 0);
399
447
  this._updateStatus(
400
448
  `${sourceName} — ${sheetNames.length} sheets, ${totalRows} total rows`,
@@ -704,34 +752,43 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
704
752
 
705
753
  // Show skipped rows (rows before the header)
706
754
  if (rawSheet && headerRow > 0) {
755
+ lines.push('');
756
+ lines.push(` ┌${'─'.repeat(100)}┐`);
757
+ lines.push(` │ ROWS 0-${headerRow - 1} WILL BE SKIPPED (not imported)`.padEnd(101) + '│');
758
+ lines.push(` └${'─'.repeat(100)}┘`);
759
+ lines.push('');
760
+
707
761
  const rawRows = rawSheet.toRows();
708
- // Row 0 in raw parse is the raw header, rows after are data
709
- // Show raw header as row 0
710
762
  const rawCols = rawSheet.columns;
711
- lines.push(`${'Idx'.padEnd(idxWidth)}${rawCols.map(c => String(c).substring(0, colWidth - 2).padEnd(colWidth)).join('| ')}`);
763
+
764
+ // Show raw header (row 0) - dimmed
765
+ lines.push(`${'[0]'.padEnd(idxWidth)}${rawCols.map(c => String(c).substring(0, colWidth - 2).padEnd(colWidth)).join('│ ')} ← skipped`);
712
766
 
713
767
  const skippedCount = Math.min(headerRow - 1, rawRows.length);
714
768
  for (let i = 0; i < skippedCount; i++) {
715
769
  const row = rawRows[i];
716
- const rowIdx = String(i + 1).padEnd(idxWidth);
717
- const cols = Object.values(row).map(v => String(v ?? '').substring(0, colWidth - 2).padEnd(colWidth)).join('| ');
718
- lines.push(`${rowIdx}${cols}`);
770
+ const rowIdx = `[${i + 1}]`.padEnd(idxWidth);
771
+ const cols = Object.values(row).map(v => String(v ?? '').substring(0, colWidth - 2).padEnd(colWidth)).join(' ');
772
+ lines.push(`${rowIdx}${cols} ← skipped`);
719
773
  }
720
774
 
721
- // Separator: everything above is skipped
722
- lines.push(`${''.repeat(6)}${'── skipped '.padEnd(colWidth * Math.min(rawCols.length, 5), '─')}`);
775
+ lines.push('');
776
+ lines.push(` ╔${''.repeat(100)}╗`);
777
+ lines.push(` ║ ▼ DATA STARTS HERE (Row ${headerRow} = Column Headers)`.padEnd(101) + '║');
778
+ lines.push(` ╚${'═'.repeat(100)}╝`);
779
+ lines.push('');
723
780
  }
724
781
 
725
782
  // Show header row and data rows from parsed result
726
783
  if (parsedSheet) {
727
- const headerLine = `${''.padEnd(idxWidth)}${parsedSheet.columns.map(c => String(c).substring(0, colWidth - 2).padEnd(colWidth)).join('| ')}`;
784
+ const headerLine = `${'HDR'.padEnd(idxWidth)}${parsedSheet.columns.map(c => String(c).substring(0, colWidth - 2).padEnd(colWidth)).join(' ')} ◀ HEADERS`;
728
785
  lines.push(headerLine);
729
- lines.push(''.repeat(Math.min(headerLine.length, 140)));
786
+ lines.push(`${'───'.padEnd(idxWidth)}${'─'.repeat(Math.min(colWidth * parsedSheet.columns.length, 120))}`);
730
787
 
731
788
  const dataRows = parsedSheet.toRows().slice(0, 8);
732
789
  dataRows.forEach((row, i) => {
733
- const rowIdx = String(headerRow + 1 + i).padEnd(idxWidth);
734
- const cols = Object.values(row).map(v => String(v ?? '').substring(0, colWidth - 2).padEnd(colWidth)).join('| ');
790
+ const rowIdx = `[${headerRow + 1 + i}]`.padEnd(idxWidth);
791
+ const cols = Object.values(row).map(v => String(v ?? '').substring(0, colWidth - 2).padEnd(colWidth)).join(' ');
735
792
  lines.push(`${rowIdx}${cols}`);
736
793
  });
737
794
  }
@@ -885,19 +942,29 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
885
942
  maxRows: totalOffset + 1
886
943
  });
887
944
 
945
+ lines.push('');
946
+ lines.push(` ┌${'─'.repeat(100)}┐`);
947
+ lines.push(` │ ROWS 0-${totalOffset - 1} WILL BE SKIPPED (not imported)`.padEnd(101) + '│');
948
+ lines.push(` └${'─'.repeat(100)}┘`);
949
+ lines.push('');
950
+
888
951
  const rawCols = rawDf.columns;
889
- lines.push(`${'Idx'.padEnd(idxWidth)}${rawCols.map(c => String(c).substring(0, colWidth - 2).padEnd(colWidth)).join('| ')}`);
952
+ lines.push(`${'[0]'.padEnd(idxWidth)}${rawCols.map(c => String(c).substring(0, colWidth - 2).padEnd(colWidth)).join(' ')} ← skipped`);
890
953
 
891
954
  const rawRows = rawDf.toRows();
892
955
  const skippedCount = Math.min(totalOffset - 1, rawRows.length);
893
956
  for (let i = 0; i < skippedCount; i++) {
894
957
  const row = rawRows[i];
895
- const rowIdx = String(i + 1).padEnd(idxWidth);
896
- const cols = Object.values(row).map(v => String(v ?? '').substring(0, colWidth - 2).padEnd(colWidth)).join('| ');
897
- lines.push(`${rowIdx}${cols}`);
958
+ const rowIdx = `[${i + 1}]`.padEnd(idxWidth);
959
+ const cols = Object.values(row).map(v => String(v ?? '').substring(0, colWidth - 2).padEnd(colWidth)).join(' ');
960
+ lines.push(`${rowIdx}${cols} ← skipped`);
898
961
  }
899
962
 
900
- lines.push(`${''.repeat(6)}${'── skipped '.padEnd(colWidth * Math.min(rawCols.length, 5), '─')}`);
963
+ lines.push('');
964
+ lines.push(` ╔${'═'.repeat(100)}╗`);
965
+ lines.push(` ║ ▼ DATA STARTS HERE (Row ${totalOffset} = Column Headers)`.padEnd(101) + '║');
966
+ lines.push(` ╚${'═'.repeat(100)}╝`);
967
+ lines.push('');
901
968
  }
902
969
 
903
970
  // Parse with actual settings for header + data rows
@@ -909,14 +976,14 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
909
976
  maxRows: 8
910
977
  });
911
978
 
912
- const headerLine = `${''.padEnd(idxWidth)}${df.columns.map(c => String(c).substring(0, colWidth - 2).padEnd(colWidth)).join('| ')}`;
979
+ const headerLine = `${'HDR'.padEnd(idxWidth)}${df.columns.map(c => String(c).substring(0, colWidth - 2).padEnd(colWidth)).join(' ')} ◀ HEADERS`;
913
980
  lines.push(headerLine);
914
- lines.push(''.repeat(Math.min(headerLine.length, 140)));
981
+ lines.push(`${'───'.padEnd(idxWidth)}${'─'.repeat(Math.min(colWidth * df.columns.length, 120))}`);
915
982
 
916
983
  const dataRows = df.toRows();
917
984
  dataRows.forEach((row, i) => {
918
- const rowIdx = String(totalOffset + 1 + i).padEnd(idxWidth);
919
- const cols = Object.values(row).map(v => String(v ?? '').substring(0, colWidth - 2).padEnd(colWidth)).join('| ');
985
+ const rowIdx = `[${totalOffset + 1 + i}]`.padEnd(idxWidth);
986
+ const cols = Object.values(row).map(v => String(v ?? '').substring(0, colWidth - 2).padEnd(colWidth)).join(' ');
920
987
  lines.push(`${rowIdx}${cols}`);
921
988
  });
922
989
 
@@ -589,49 +589,47 @@ code {
589
589
  .jux-modal-position-fullscreen .jux-modal { width: 100%; max-width: 100%; height: 100vh; max-height: 100vh; border-radius: 0; }
590
590
 
591
591
  /* ═══════════════════════════════════════════════════════════════════
592
- * RESHAPE MODAL (DataFrame Import Settings)
592
+ * DATAFRAME TAB SETTINGS COG
593
593
  * ═══════════════════════════════════════════════════════════════════ */
594
594
 
595
- .jux-reshape-hint {
596
- font-size: 0.8125rem;
595
+ .jux-dataframe-tab-settings {
596
+ display: inline-flex;
597
+ align-items: center;
598
+ justify-content: center;
599
+ background: transparent;
600
+ border: none;
601
+ padding: 0.125rem;
602
+ margin-left: 0.375rem;
603
+ border-radius: 0.25rem;
604
+ cursor: pointer;
597
605
  color: hsl(var(--muted-foreground));
598
- margin-top: 0.5rem;
599
- padding: 0.625rem 0.75rem;
600
- background: hsl(var(--warning) / 0.08);
601
- border-radius: var(--radius);
602
- border: 1px solid hsl(var(--warning) / 0.2);
603
- line-height: 1.5;
606
+ opacity: 0.5;
607
+ transition: all 0.15s;
604
608
  }
605
609
 
606
- .jux-reshape-hint strong {
610
+ .jux-dataframe-tab-settings:hover {
611
+ opacity: 1;
612
+ background: hsl(var(--muted) / 0.5);
607
613
  color: hsl(var(--foreground));
608
614
  }
609
615
 
610
- .jux-reshape-preview-container {
611
- border: 1px solid hsl(var(--border));
612
- border-radius: var(--radius);
613
- padding: 1rem;
614
- background: hsl(var(--muted) / 0.3);
615
- max-height: 400px;
616
- overflow: auto;
616
+ .jux-tabs-button-active .jux-dataframe-tab-settings {
617
+ opacity: 0.7;
617
618
  }
618
619
 
619
- .jux-reshape-preview {
620
- font-family: 'JetBrains Mono', 'Cascadia Code', 'Fira Code', 'Courier New', monospace;
621
- font-size: 0.6875rem;
622
- white-space: pre;
623
- color: hsl(var(--foreground));
624
- line-height: 1.6;
625
- tab-size: 4;
620
+ .jux-tabs-button-active .jux-dataframe-tab-settings:hover {
621
+ opacity: 1;
626
622
  }
627
623
 
628
- /* ═══════════════════════════════════════════════════════════════════
629
- * BUTTON SIZES
630
- * ═══════════════════════════════════════════════════════════════════ */
624
+ /* Make tabs list scrollable if many sheets */
625
+ .jux-dataframe-tabs .jux-tabs-list {
626
+ overflow-x: auto;
627
+ flex-wrap: nowrap;
628
+ max-width: 100%;
629
+ }
631
630
 
632
- .jux-button-sm {
633
- height: 1.75rem;
634
- padding: 0 0.625rem;
635
- font-size: 0.75rem;
636
- border-radius: calc(var(--radius) - 2px);
631
+ .jux-dataframe-tabs .jux-tabs-button {
632
+ display: inline-flex;
633
+ align-items: center;
634
+ flex-shrink: 0;
637
635
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "juxscript",
3
- "version": "1.1.194",
3
+ "version": "1.1.195",
4
4
  "type": "module",
5
5
  "description": "A JavaScript UX authorship platform",
6
6
  "main": "index.js",