juxscript 1.1.191 → 1.1.193

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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"}
@@ -360,8 +360,8 @@ export class DataFrameComponent extends BaseComponent {
360
360
  this._table.columns(columnDefs).rows(this._df.toRows());
361
361
  }
362
362
  const isMalformed = this._detectMalformedData(this._df);
363
- if (isMalformed && this._showReshapeWarning && this._rawFileData) {
364
- this._updateStatus(`${sourceName} — ${this._df.height} rows × ${this._df.width} cols (Data may be malformed — headers may be on wrong row)`, 'warning');
363
+ if (isMalformed && this._rawFileData) {
364
+ this._updateStatus(`${sourceName} — ${this._df.height} rows × ${this._df.width} cols (Data may need reformatting)`, 'warning');
365
365
  requestAnimationFrame(() => {
366
366
  const statusEl = document.getElementById(`${this._id}-status`);
367
367
  if (statusEl) {
@@ -376,12 +376,13 @@ export class DataFrameComponent extends BaseComponent {
376
376
  }
377
377
  else {
378
378
  this._updateStatus(`${sourceName} — ${this._df.height} rows × ${this._df.width} cols`, 'success');
379
- if (this._showReshapeWarning && this._rawFileData) {
379
+ // Always show settings button if we have raw file data
380
+ if (this._rawFileData) {
380
381
  requestAnimationFrame(() => {
381
382
  const statusEl = document.getElementById(`${this._id}-status`);
382
383
  if (statusEl) {
383
384
  const settingsBtn = document.createElement('button');
384
- settingsBtn.textContent = 'Settings';
385
+ settingsBtn.textContent = 'Import Settings';
385
386
  settingsBtn.className = 'jux-button jux-button-sm jux-button-ghost';
386
387
  settingsBtn.style.marginLeft = '0.5rem';
387
388
  settingsBtn.addEventListener('click', () => this._showReshapeModal());
@@ -430,13 +431,13 @@ export class DataFrameComponent extends BaseComponent {
430
431
  return isNaN(Number(str)) && str !== '';
431
432
  }).length;
432
433
  if (nonNumericCount >= nonEmpty.length * 0.7 && i > 0) {
433
- // Return 1-based row number for the UI
434
- // i is index in toRows() but row 0 of the file was consumed as header,
435
- // so the actual file row is i + 1, and 1-based display is i + 2
436
- return i + 2;
434
+ // i is the index in toRows(). Row 0 of the file was already consumed
435
+ // as the header during the initial parse, so the actual 0-based file
436
+ // row index is i + 1.
437
+ return i + 1;
437
438
  }
438
439
  }
439
- return 1; // Default to row 1 (1-based)
440
+ return 0;
440
441
  }
441
442
  /* ═══════════════════════════════════════════════════
442
443
  * RESHAPE MODAL
@@ -464,7 +465,7 @@ export class DataFrameComponent extends BaseComponent {
464
465
  if (!this._rawFileData?.file)
465
466
  return;
466
467
  this._cleanupReshapeModal();
467
- const suggestedRow = this._df ? this._detectLikelyHeaderRow(this._df) : 1;
468
+ const suggestedRow = this._df ? this._detectLikelyHeaderRow(this._df) : 0;
468
469
  this._reshapeModal = new Modal(`${this._id}-reshape-modal`, {
469
470
  title: 'Excel Import Settings',
470
471
  size: 'large',
@@ -481,14 +482,11 @@ export class DataFrameComponent extends BaseComponent {
481
482
  id="${this._id}-header-row"
482
483
  class="jux-input-element"
483
484
  value="${suggestedRow}"
484
- min="1"
485
+ min="0"
485
486
  max="50"
486
487
  style="width: 100%;"
487
488
  />
488
- <div class="jux-reshape-hint">
489
- <strong>Detected issue:</strong> The current header row appears to contain metadata or empty values.
490
- Row ${suggestedRow} looks like it contains the actual column headers.
491
- Adjust the value above and check the preview below.
489
+ <div id="${this._id}-reshape-hint" class="jux-reshape-hint">
492
490
  </div>
493
491
  </div>
494
492
  <div class="jux-reshape-preview-container">
@@ -511,8 +509,7 @@ export class DataFrameComponent extends BaseComponent {
511
509
  variant: 'primary',
512
510
  click: async () => {
513
511
  const input = document.getElementById(`${this._id}-header-row`);
514
- const headerRowOneBased = parseInt(input.value) || 1;
515
- const headerRow = headerRowOneBased - 1; // Convert to 0-based for driver
512
+ const headerRow = parseInt(input.value) || 0;
516
513
  this.state.loading = true;
517
514
  this._updateStatus('Re-parsing with new settings...', 'loading');
518
515
  try {
@@ -527,7 +524,6 @@ export class DataFrameComponent extends BaseComponent {
527
524
  this._renderMultiSheet(sheets, this._rawFileData.file.name);
528
525
  }
529
526
  else {
530
- this._showReshapeWarning = false;
531
527
  this._setDataFrame(sheets[sheetNames[0]], this._rawFileData.file.name);
532
528
  }
533
529
  this._reshapeModal.closeModal();
@@ -543,9 +539,16 @@ export class DataFrameComponent extends BaseComponent {
543
539
  await new Promise(resolve => requestAnimationFrame(resolve));
544
540
  const headerRowInput = document.getElementById(`${this._id}-header-row`);
545
541
  const previewDiv = document.getElementById(`${this._id}-preview`);
542
+ const hintDiv = document.getElementById(`${this._id}-reshape-hint`);
543
+ const updateHint = (headerRow) => {
544
+ if (!hintDiv)
545
+ return;
546
+ hintDiv.innerHTML = `The data starting at <strong>row ${headerRow}</strong> will be used as column headers. ` +
547
+ `Rows before it will be skipped. The preview below shows the row index as the first column.`;
548
+ };
546
549
  const updatePreview = async () => {
547
- const headerRowOneBased = parseInt(headerRowInput?.value) || 1;
548
- const headerRow = headerRowOneBased - 1; // Convert to 0-based for driver
550
+ const headerRow = parseInt(headerRowInput?.value) || 0;
551
+ updateHint(headerRow);
549
552
  try {
550
553
  const sheets = await this._driver.streamFileMultiSheet(this._rawFileData.file, {
551
554
  headerRow,
@@ -557,12 +560,18 @@ export class DataFrameComponent extends BaseComponent {
557
560
  previewDiv.textContent = 'No data found';
558
561
  return;
559
562
  }
560
- const preview = firstSheet.toRows().slice(0, 10).map((row, i) => {
561
- const cols = Object.values(row).map(v => String(v ?? '').padEnd(20)).join(' | ');
562
- return `${i === 0 ? '>> ' : ' '}${cols}`;
563
+ const dataRows = firstSheet.toRows().slice(0, 10);
564
+ const idxWidth = 6;
565
+ const colWidth = 22;
566
+ const headerLine = 'Idx'.padEnd(idxWidth) + firstSheet.columns.map(c => String(c).substring(0, colWidth - 2).padEnd(colWidth)).join('| ');
567
+ const separator = '─'.repeat(Math.min(headerLine.length, 140));
568
+ const preview = dataRows.map((row, i) => {
569
+ const rowIdx = String(headerRow + 1 + i).padEnd(idxWidth);
570
+ const cols = Object.values(row).map(v => String(v ?? '').substring(0, colWidth - 2).padEnd(colWidth)).join('| ');
571
+ return `${rowIdx}${cols}`;
563
572
  }).join('\n');
564
573
  if (previewDiv) {
565
- previewDiv.textContent = `Columns: ${firstSheet.columns.join(' | ')}\n${'─'.repeat(80)}\n${preview}`;
574
+ previewDiv.textContent = `${headerLine}\n${separator}\n${preview}`;
566
575
  }
567
576
  }
568
577
  catch (err) {
@@ -597,13 +606,14 @@ export class DataFrameComponent extends BaseComponent {
597
606
  </div>
598
607
  <div style="margin-bottom: 1rem;">
599
608
  <label style="display: block; font-weight: 600; margin-bottom: 0.5rem;">Header Row</label>
600
- <input type="number" id="${this._id}-header-row" class="jux-input-element" value="1" min="1" max="50" style="width: 100%;" />
609
+ <input type="number" id="${this._id}-header-row" class="jux-input-element" value="0" min="0" max="50" style="width: 100%;" />
601
610
  </div>
602
611
  <div style="margin-bottom: 1rem;">
603
612
  <label style="display: block; font-weight: 600; margin-bottom: 0.5rem;">Skip Rows Before Header</label>
604
613
  <input type="number" id="${this._id}-skip-rows" class="jux-input-element" value="0" min="0" max="50" style="width: 100%;" />
605
614
  </div>
606
- <div class="jux-reshape-preview-container">
615
+ <div id="${this._id}-reshape-hint" class="jux-reshape-hint"></div>
616
+ <div class="jux-reshape-preview-container" style="margin-top: 1rem;">
607
617
  <div style="font-weight: 600; margin-bottom: 0.5rem; color: hsl(var(--foreground));">Preview</div>
608
618
  <div id="${this._id}-preview" class="jux-reshape-preview"></div>
609
619
  </div>
@@ -626,8 +636,7 @@ export class DataFrameComponent extends BaseComponent {
626
636
  const headerRowInput = document.getElementById(`${this._id}-header-row`);
627
637
  const skipRowsInput = document.getElementById(`${this._id}-skip-rows`);
628
638
  const delim = delimiterSelect.value;
629
- const headerRowOneBased = parseInt(headerRowInput.value) || 1;
630
- const headerRow = headerRowOneBased - 1; // Convert to 0-based
639
+ const headerRow = parseInt(headerRowInput.value) || 0;
631
640
  const skipRows = parseInt(skipRowsInput.value) || 0;
632
641
  this.state.loading = true;
633
642
  this._updateStatus('Re-parsing with new settings...', 'loading');
@@ -636,11 +645,9 @@ export class DataFrameComponent extends BaseComponent {
636
645
  delimiter: delim,
637
646
  headerRow,
638
647
  skipRows,
639
- hasHeader: true,
640
- maxRows: 10
648
+ hasHeader: true
641
649
  });
642
650
  await this._driver.store(this._rawFileData.file.name, df, { source: this._rawFileData.file.name });
643
- this._showReshapeWarning = false;
644
651
  this._setDataFrame(df, this._rawFileData.file.name);
645
652
  this._reshapeModal.closeModal();
646
653
  }
@@ -657,21 +664,31 @@ export class DataFrameComponent extends BaseComponent {
657
664
  const headerRowInput = document.getElementById(`${this._id}-header-row`);
658
665
  const skipRowsInput = document.getElementById(`${this._id}-skip-rows`);
659
666
  const previewDiv = document.getElementById(`${this._id}-preview`);
667
+ const hintDiv = document.getElementById(`${this._id}-reshape-hint`);
660
668
  if (this._rawFileData?.text) {
661
669
  const detected = this._driver._detectDelimiter(this._rawFileData.text);
662
670
  if (delimiterSelect)
663
671
  delimiterSelect.value = detected;
664
672
  const detectedHeaderRow = this._driver._detectHeaderRow(this._rawFileData.text, detected);
665
673
  if (headerRowInput)
666
- headerRowInput.value = String(detectedHeaderRow + 1); // Convert 0-based to 1-based for display
674
+ headerRowInput.value = String(detectedHeaderRow);
667
675
  }
676
+ const updateHint = () => {
677
+ if (!hintDiv)
678
+ return;
679
+ const headerRow = parseInt(headerRowInput?.value) || 0;
680
+ const skipRows = parseInt(skipRowsInput?.value) || 0;
681
+ hintDiv.innerHTML = `Using <strong>row ${headerRow}</strong> as column headers` +
682
+ (skipRows > 0 ? ` (skipping ${skipRows} rows before it)` : '') +
683
+ `. The Idx column shows the file row index.`;
684
+ };
668
685
  const updatePreview = () => {
669
686
  if (!this._rawFileData?.text)
670
687
  return;
671
688
  const delim = delimiterSelect?.value || ',';
672
- const headerRowOneBased = parseInt(headerRowInput?.value) || 1;
673
- const headerRow = headerRowOneBased - 1; // Convert to 0-based
689
+ const headerRow = parseInt(headerRowInput?.value) || 0;
674
690
  const skipRows = parseInt(skipRowsInput?.value) || 0;
691
+ updateHint();
675
692
  try {
676
693
  const df = this._driver.parseCSV(this._rawFileData.text, {
677
694
  delimiter: delim,
@@ -680,12 +697,18 @@ export class DataFrameComponent extends BaseComponent {
680
697
  hasHeader: true,
681
698
  maxRows: 10
682
699
  });
683
- const preview = df.toRows().map((row, i) => {
684
- const cols = Object.values(row).map(v => String(v ?? '').padEnd(20)).join(' | ');
685
- return `${i === 0 ? '>> ' : ' '}${cols}`;
700
+ const dataRows = df.toRows();
701
+ const idxWidth = 6;
702
+ const colWidth = 22;
703
+ const headerLine = 'Idx'.padEnd(idxWidth) + df.columns.map(c => String(c).substring(0, colWidth - 2).padEnd(colWidth)).join('| ');
704
+ const separator = '─'.repeat(Math.min(headerLine.length, 140));
705
+ const preview = dataRows.map((row, i) => {
706
+ const rowIdx = String(headerRow + skipRows + 1 + i).padEnd(idxWidth);
707
+ const cols = Object.values(row).map(v => String(v ?? '').substring(0, colWidth - 2).padEnd(colWidth)).join('| ');
708
+ return `${rowIdx}${cols}`;
686
709
  }).join('\n');
687
710
  if (previewDiv) {
688
- previewDiv.textContent = `Columns: ${df.columns.join(' | ')}\n${'─'.repeat(80)}\n${preview}`;
711
+ previewDiv.textContent = `${headerLine}\n${separator}\n${preview}`;
689
712
  }
690
713
  }
691
714
  catch (err) {
@@ -452,9 +452,9 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
452
452
 
453
453
  const isMalformed = this._detectMalformedData(this._df!);
454
454
 
455
- if (isMalformed && this._showReshapeWarning && this._rawFileData) {
455
+ if (isMalformed && this._rawFileData) {
456
456
  this._updateStatus(
457
- `${sourceName} — ${this._df!.height} rows × ${this._df!.width} cols (Data may be malformed — headers may be on wrong row)`,
457
+ `${sourceName} — ${this._df!.height} rows × ${this._df!.width} cols (Data may need reformatting)`,
458
458
  'warning'
459
459
  );
460
460
 
@@ -475,12 +475,13 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
475
475
  'success'
476
476
  );
477
477
 
478
- if (this._showReshapeWarning && this._rawFileData) {
478
+ // Always show settings button if we have raw file data
479
+ if (this._rawFileData) {
479
480
  requestAnimationFrame(() => {
480
481
  const statusEl = document.getElementById(`${this._id}-status`);
481
482
  if (statusEl) {
482
483
  const settingsBtn = document.createElement('button');
483
- settingsBtn.textContent = 'Settings';
484
+ settingsBtn.textContent = 'Import Settings';
484
485
  settingsBtn.className = 'jux-button jux-button-sm jux-button-ghost';
485
486
  settingsBtn.style.marginLeft = '0.5rem';
486
487
  settingsBtn.addEventListener('click', () => this._showReshapeModal());
@@ -542,14 +543,14 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
542
543
  }).length;
543
544
 
544
545
  if (nonNumericCount >= nonEmpty.length * 0.7 && i > 0) {
545
- // Return 1-based row number for the UI
546
- // i is index in toRows() but row 0 of the file was consumed as header,
547
- // so the actual file row is i + 1, and 1-based display is i + 2
548
- return i + 2;
546
+ // i is the index in toRows(). Row 0 of the file was already consumed
547
+ // as the header during the initial parse, so the actual 0-based file
548
+ // row index is i + 1.
549
+ return i + 1;
549
550
  }
550
551
  }
551
552
 
552
- return 1; // Default to row 1 (1-based)
553
+ return 0;
553
554
  }
554
555
 
555
556
  /* ═══════════════════════════════════════════════════
@@ -580,7 +581,7 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
580
581
 
581
582
  this._cleanupReshapeModal();
582
583
 
583
- const suggestedRow = this._df ? this._detectLikelyHeaderRow(this._df) : 1;
584
+ const suggestedRow = this._df ? this._detectLikelyHeaderRow(this._df) : 0;
584
585
 
585
586
  this._reshapeModal = new Modal(`${this._id}-reshape-modal`, {
586
587
  title: 'Excel Import Settings',
@@ -599,14 +600,11 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
599
600
  id="${this._id}-header-row"
600
601
  class="jux-input-element"
601
602
  value="${suggestedRow}"
602
- min="1"
603
+ min="0"
603
604
  max="50"
604
605
  style="width: 100%;"
605
606
  />
606
- <div class="jux-reshape-hint">
607
- <strong>Detected issue:</strong> The current header row appears to contain metadata or empty values.
608
- Row ${suggestedRow} looks like it contains the actual column headers.
609
- Adjust the value above and check the preview below.
607
+ <div id="${this._id}-reshape-hint" class="jux-reshape-hint">
610
608
  </div>
611
609
  </div>
612
610
  <div class="jux-reshape-preview-container">
@@ -630,8 +628,7 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
630
628
  variant: 'primary',
631
629
  click: async () => {
632
630
  const input = document.getElementById(`${this._id}-header-row`) as HTMLInputElement;
633
- const headerRowOneBased = parseInt(input.value) || 1;
634
- const headerRow = headerRowOneBased - 1; // Convert to 0-based for driver
631
+ const headerRow = parseInt(input.value) || 0;
635
632
 
636
633
  this.state.loading = true;
637
634
  this._updateStatus('Re-parsing with new settings...', 'loading');
@@ -649,7 +646,6 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
649
646
  if (sheetNames.length > 1) {
650
647
  this._renderMultiSheet(sheets, this._rawFileData!.file.name);
651
648
  } else {
652
- this._showReshapeWarning = false;
653
649
  this._setDataFrame(sheets[sheetNames[0]], this._rawFileData!.file.name);
654
650
  }
655
651
 
@@ -668,10 +664,19 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
668
664
 
669
665
  const headerRowInput = document.getElementById(`${this._id}-header-row`) as HTMLInputElement;
670
666
  const previewDiv = document.getElementById(`${this._id}-preview`)!;
667
+ const hintDiv = document.getElementById(`${this._id}-reshape-hint`)!;
668
+
669
+ const updateHint = (headerRow: number) => {
670
+ if (!hintDiv) return;
671
+ hintDiv.innerHTML = `The data starting at <strong>row ${headerRow}</strong> will be used as column headers. ` +
672
+ `Rows before it will be skipped. The preview below shows the row index as the first column.`;
673
+ };
671
674
 
672
675
  const updatePreview = async () => {
673
- const headerRowOneBased = parseInt(headerRowInput?.value) || 1;
674
- const headerRow = headerRowOneBased - 1; // Convert to 0-based for driver
676
+ const headerRow = parseInt(headerRowInput?.value) || 0;
677
+
678
+ updateHint(headerRow);
679
+
675
680
  try {
676
681
  const sheets = await this._driver.streamFileMultiSheet(this._rawFileData!.file, {
677
682
  headerRow,
@@ -684,13 +689,21 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
684
689
  return;
685
690
  }
686
691
 
687
- const preview = firstSheet.toRows().slice(0, 10).map((row, i) => {
688
- const cols = Object.values(row).map(v => String(v ?? '').padEnd(20)).join(' | ');
689
- return `${i === 0 ? '>> ' : ' '}${cols}`;
692
+ const dataRows = firstSheet.toRows().slice(0, 10);
693
+ const idxWidth = 6;
694
+ const colWidth = 22;
695
+
696
+ const headerLine = 'Idx'.padEnd(idxWidth) + firstSheet.columns.map(c => String(c).substring(0, colWidth - 2).padEnd(colWidth)).join('| ');
697
+ const separator = '─'.repeat(Math.min(headerLine.length, 140));
698
+
699
+ const preview = dataRows.map((row, i) => {
700
+ const rowIdx = String(headerRow + 1 + i).padEnd(idxWidth);
701
+ const cols = Object.values(row).map(v => String(v ?? '').substring(0, colWidth - 2).padEnd(colWidth)).join('| ');
702
+ return `${rowIdx}${cols}`;
690
703
  }).join('\n');
691
704
 
692
705
  if (previewDiv) {
693
- previewDiv.textContent = `Columns: ${firstSheet.columns.join(' | ')}\n${'─'.repeat(80)}\n${preview}`;
706
+ previewDiv.textContent = `${headerLine}\n${separator}\n${preview}`;
694
707
  }
695
708
  } catch (err: any) {
696
709
  if (previewDiv) previewDiv.textContent = `Error: ${err.message}`;
@@ -727,13 +740,14 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
727
740
  </div>
728
741
  <div style="margin-bottom: 1rem;">
729
742
  <label style="display: block; font-weight: 600; margin-bottom: 0.5rem;">Header Row</label>
730
- <input type="number" id="${this._id}-header-row" class="jux-input-element" value="1" min="1" max="50" style="width: 100%;" />
743
+ <input type="number" id="${this._id}-header-row" class="jux-input-element" value="0" min="0" max="50" style="width: 100%;" />
731
744
  </div>
732
745
  <div style="margin-bottom: 1rem;">
733
746
  <label style="display: block; font-weight: 600; margin-bottom: 0.5rem;">Skip Rows Before Header</label>
734
747
  <input type="number" id="${this._id}-skip-rows" class="jux-input-element" value="0" min="0" max="50" style="width: 100%;" />
735
748
  </div>
736
- <div class="jux-reshape-preview-container">
749
+ <div id="${this._id}-reshape-hint" class="jux-reshape-hint"></div>
750
+ <div class="jux-reshape-preview-container" style="margin-top: 1rem;">
737
751
  <div style="font-weight: 600; margin-bottom: 0.5rem; color: hsl(var(--foreground));">Preview</div>
738
752
  <div id="${this._id}-preview" class="jux-reshape-preview"></div>
739
753
  </div>
@@ -758,8 +772,7 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
758
772
  const skipRowsInput = document.getElementById(`${this._id}-skip-rows`) as HTMLInputElement;
759
773
 
760
774
  const delim = delimiterSelect.value;
761
- const headerRowOneBased = parseInt(headerRowInput.value) || 1;
762
- const headerRow = headerRowOneBased - 1; // Convert to 0-based
775
+ const headerRow = parseInt(headerRowInput.value) || 0;
763
776
  const skipRows = parseInt(skipRowsInput.value) || 0;
764
777
 
765
778
  this.state.loading = true;
@@ -770,12 +783,10 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
770
783
  delimiter: delim,
771
784
  headerRow,
772
785
  skipRows,
773
- hasHeader: true,
774
- maxRows: 10
786
+ hasHeader: true
775
787
  });
776
788
 
777
789
  await this._driver.store(this._rawFileData.file.name, df, { source: this._rawFileData.file.name });
778
- this._showReshapeWarning = false;
779
790
  this._setDataFrame(df, this._rawFileData.file.name);
780
791
 
781
792
  this._reshapeModal!.closeModal();
@@ -794,23 +805,34 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
794
805
  const headerRowInput = document.getElementById(`${this._id}-header-row`) as HTMLInputElement;
795
806
  const skipRowsInput = document.getElementById(`${this._id}-skip-rows`) as HTMLInputElement;
796
807
  const previewDiv = document.getElementById(`${this._id}-preview`)!;
808
+ const hintDiv = document.getElementById(`${this._id}-reshape-hint`)!;
797
809
 
798
810
  if (this._rawFileData?.text) {
799
811
  const detected = (this._driver as any)._detectDelimiter(this._rawFileData.text);
800
812
  if (delimiterSelect) delimiterSelect.value = detected;
801
813
 
802
814
  const detectedHeaderRow = (this._driver as any)._detectHeaderRow(this._rawFileData.text, detected);
803
- if (headerRowInput) headerRowInput.value = String(detectedHeaderRow + 1); // Convert 0-based to 1-based for display
815
+ if (headerRowInput) headerRowInput.value = String(detectedHeaderRow);
804
816
  }
805
817
 
818
+ const updateHint = () => {
819
+ if (!hintDiv) return;
820
+ const headerRow = parseInt(headerRowInput?.value) || 0;
821
+ const skipRows = parseInt(skipRowsInput?.value) || 0;
822
+ hintDiv.innerHTML = `Using <strong>row ${headerRow}</strong> as column headers` +
823
+ (skipRows > 0 ? ` (skipping ${skipRows} rows before it)` : '') +
824
+ `. The Idx column shows the file row index.`;
825
+ };
826
+
806
827
  const updatePreview = () => {
807
828
  if (!this._rawFileData?.text) return;
808
829
 
809
830
  const delim = delimiterSelect?.value || ',';
810
- const headerRowOneBased = parseInt(headerRowInput?.value) || 1;
811
- const headerRow = headerRowOneBased - 1; // Convert to 0-based
831
+ const headerRow = parseInt(headerRowInput?.value) || 0;
812
832
  const skipRows = parseInt(skipRowsInput?.value) || 0;
813
833
 
834
+ updateHint();
835
+
814
836
  try {
815
837
  const df = this._driver.parseCSV(this._rawFileData.text, {
816
838
  delimiter: delim,
@@ -820,13 +842,21 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
820
842
  maxRows: 10
821
843
  });
822
844
 
823
- const preview = df.toRows().map((row, i) => {
824
- const cols = Object.values(row).map(v => String(v ?? '').padEnd(20)).join(' | ');
825
- return `${i === 0 ? '>> ' : ' '}${cols}`;
845
+ const dataRows = df.toRows();
846
+ const idxWidth = 6;
847
+ const colWidth = 22;
848
+
849
+ const headerLine = 'Idx'.padEnd(idxWidth) + df.columns.map(c => String(c).substring(0, colWidth - 2).padEnd(colWidth)).join('| ');
850
+ const separator = '─'.repeat(Math.min(headerLine.length, 140));
851
+
852
+ const preview = dataRows.map((row, i) => {
853
+ const rowIdx = String(headerRow + skipRows + 1 + i).padEnd(idxWidth);
854
+ const cols = Object.values(row).map(v => String(v ?? '').substring(0, colWidth - 2).padEnd(colWidth)).join('| ');
855
+ return `${rowIdx}${cols}`;
826
856
  }).join('\n');
827
857
 
828
858
  if (previewDiv) {
829
- previewDiv.textContent = `Columns: ${df.columns.join(' | ')}\n${'─'.repeat(80)}\n${preview}`;
859
+ previewDiv.textContent = `${headerLine}\n${separator}\n${preview}`;
830
860
  }
831
861
  } catch (err: any) {
832
862
  if (previewDiv) previewDiv.textContent = `Error: ${err.message}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "juxscript",
3
- "version": "1.1.191",
3
+ "version": "1.1.193",
4
4
  "type": "module",
5
5
  "description": "A JavaScript UX authorship platform",
6
6
  "main": "index.js",