juxscript 1.1.206 → 1.1.207

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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"}
@@ -502,45 +502,15 @@ export class DataFrameComponent extends BaseComponent {
502
502
  if (!this._rawFileData?.file)
503
503
  return;
504
504
  this._cleanupReshapeModal();
505
- // Read raw cells from the file
506
- const XLSX = await import('xlsx');
507
- const buffer = await this._rawFileData.file.arrayBuffer();
508
- const workbook = XLSX.read(buffer, {
509
- type: 'array',
510
- sheetRows: 20,
511
- dense: false
512
- });
513
- const sheetName = workbook.SheetNames[0];
514
- const worksheet = workbook.Sheets[sheetName];
515
- const ref = worksheet['!ref'];
516
- if (!ref)
505
+ // Use the SAME cell-reading method as the parser
506
+ const rawRows = await this._driver.readRawExcelRows(this._rawFileData.file, 15);
507
+ if (rawRows.length === 0)
517
508
  return;
518
- const range = XLSX.utils.decode_range(ref);
519
- const endRow = Math.min(range.e.r, 14); // Show up to 15 rows
520
- const startCol = range.s.c;
521
- const endCol = range.e.c;
522
- const readCellValue = (r, c) => {
523
- const addr = XLSX.utils.encode_cell({ r, c });
524
- const cell = worksheet[addr];
525
- if (!cell)
526
- return null;
527
- if (cell.w !== undefined)
528
- return cell.w;
529
- if (cell.v !== undefined)
530
- return cell.v;
531
- return null;
532
- };
533
- // Build raw row data with actual sheet row indices
534
- const rawRows = [];
535
- for (let r = 0; r <= endRow; r++) {
536
- const values = [];
537
- for (let c = startCol; c <= endCol; c++) {
538
- values.push(readCellValue(r, c));
539
- }
540
- rawRows.push({ sheetRow: r, values });
541
- }
509
+ // Log what we got so we can verify alignment
510
+ console.log('[DataFrame Preview] Raw rows from readRawExcelRows:');
511
+ rawRows.forEach(r => console.log(` sheetRow ${r.sheetRow}:`, r.values.slice(0, 5)));
542
512
  // Auto-detect best header row
543
- let selectedSheetRow = 0;
513
+ let selectedSheetRow = rawRows[0].sheetRow;
544
514
  for (const { sheetRow, values } of rawRows) {
545
515
  const nonEmpty = values.filter(v => v !== null && v !== undefined && String(v).trim() !== '');
546
516
  if (nonEmpty.length < values.length * 0.5)
@@ -584,6 +554,7 @@ export class DataFrameComponent extends BaseComponent {
584
554
  click: async () => {
585
555
  const input = document.getElementById(`${this._id}-header-row`);
586
556
  const headerRow = parseInt(input.value) || 0;
557
+ console.log(`[DataFrame] Apply clicked: headerRow=${headerRow}`);
587
558
  this.state.loading = true;
588
559
  this._updateStatus('Re-parsing with new settings...', 'loading');
589
560
  try {
@@ -626,22 +597,22 @@ export class DataFrameComponent extends BaseComponent {
626
597
  const vals = rawRows.find(r => r.sheetRow === row)?.values ?? [];
627
598
  const headerNames = vals.filter((v) => v != null && String(v).trim() !== '').map((v) => String(v).trim());
628
599
  const preview = headerNames.slice(0, 4).join(', ') + (headerNames.length > 4 ? '…' : '');
629
- if (row > 0) {
630
- hintDiv.innerHTML = `Row <strong>${row}</strong> selected as header. Columns: <code>${this._escapeHtml(preview)}</code>. Rows 0–${row - 1} will be skipped.`;
600
+ if (row > rawRows[0].sheetRow) {
601
+ hintDiv.innerHTML = `Sheet row <strong>${row}</strong> selected as header. Columns: <code>${this._escapeHtml(preview)}</code>. Rows before it will be skipped.`;
631
602
  }
632
603
  else {
633
- hintDiv.innerHTML = `Row <strong>0</strong> (first row) selected as header. Columns: <code>${this._escapeHtml(preview)}</code>`;
604
+ hintDiv.innerHTML = `Sheet row <strong>${row}</strong> (first row) selected as header. Columns: <code>${this._escapeHtml(preview)}</code>`;
634
605
  }
635
606
  };
636
607
  const renderPreview = (selected) => {
637
608
  if (!previewDiv)
638
609
  return;
639
610
  previewDiv.innerHTML = this._buildClickablePreviewHTML(rawRows, selected);
640
- // Wire click handlers on each row
641
611
  previewDiv.querySelectorAll('tr[data-sheet-row]').forEach(tr => {
642
612
  tr.addEventListener('click', () => {
643
613
  const rowIdx = parseInt(tr.dataset.sheetRow);
644
614
  hiddenInput.value = String(rowIdx);
615
+ console.log(`[DataFrame Preview] Clicked sheetRow=${rowIdx}`);
645
616
  updateHint(rowIdx);
646
617
  renderPreview(rowIdx);
647
618
  });
@@ -759,11 +730,11 @@ export class DataFrameComponent extends BaseComponent {
759
730
  const vals = rawRows.find(r => r.sheetRow === row)?.values ?? [];
760
731
  const headerNames = vals.filter((v) => v != null && String(v).trim() !== '').map((v) => String(v).trim());
761
732
  const preview = headerNames.slice(0, 4).join(', ') + (headerNames.length > 4 ? '…' : '');
762
- if (row > 0) {
763
- hintDiv.innerHTML = `Row <strong>${row}</strong> selected as header. Columns: <code>${this._escapeHtml(preview)}</code>. Rows 0–${row - 1} will be skipped.`;
733
+ if (row > rawRows[0].sheetRow) {
734
+ hintDiv.innerHTML = `Sheet row <strong>${row}</strong> selected as header. Columns: <code>${this._escapeHtml(preview)}</code>. Rows before it will be skipped.`;
764
735
  }
765
736
  else {
766
- hintDiv.innerHTML = `Row <strong>0</strong> (first row) selected as header. Columns: <code>${this._escapeHtml(preview)}</code>`;
737
+ hintDiv.innerHTML = `Sheet row <strong>${row}</strong> (first row) selected as header. Columns: <code>${this._escapeHtml(preview)}</code>`;
767
738
  }
768
739
  };
769
740
  const reparse = () => {
@@ -786,6 +757,7 @@ export class DataFrameComponent extends BaseComponent {
786
757
  tr.addEventListener('click', () => {
787
758
  const rowIdx = parseInt(tr.dataset.sheetRow);
788
759
  hiddenInput.value = String(rowIdx);
760
+ console.log(`[DataFrame Preview] Clicked sheetRow=${rowIdx}`);
789
761
  updateHint(rowIdx);
790
762
  renderPreview(rowIdx);
791
763
  });
@@ -617,46 +617,17 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
617
617
 
618
618
  this._cleanupReshapeModal();
619
619
 
620
- // Read raw cells from the file
621
- const XLSX = await import('xlsx');
622
- const buffer = await this._rawFileData.file.arrayBuffer();
623
- const workbook = XLSX.read(buffer, {
624
- type: 'array',
625
- sheetRows: 20,
626
- dense: false
627
- });
620
+ // Use the SAME cell-reading method as the parser
621
+ const rawRows = await this._driver.readRawExcelRows(this._rawFileData.file, 15);
628
622
 
629
- const sheetName = workbook.SheetNames[0];
630
- const worksheet = workbook.Sheets[sheetName];
631
- const ref = worksheet['!ref'];
632
- if (!ref) return;
633
-
634
- const range = XLSX.utils.decode_range(ref);
635
- const endRow = Math.min(range.e.r, 14); // Show up to 15 rows
636
- const startCol = range.s.c;
637
- const endCol = range.e.c;
638
-
639
- const readCellValue = (r: number, c: number): any => {
640
- const addr = XLSX.utils.encode_cell({ r, c });
641
- const cell = worksheet[addr];
642
- if (!cell) return null;
643
- if (cell.w !== undefined) return cell.w;
644
- if (cell.v !== undefined) return cell.v;
645
- return null;
646
- };
623
+ if (rawRows.length === 0) return;
647
624
 
648
- // Build raw row data with actual sheet row indices
649
- const rawRows: { sheetRow: number; values: any[] }[] = [];
650
- for (let r = 0; r <= endRow; r++) {
651
- const values: any[] = [];
652
- for (let c = startCol; c <= endCol; c++) {
653
- values.push(readCellValue(r, c));
654
- }
655
- rawRows.push({ sheetRow: r, values });
656
- }
625
+ // Log what we got so we can verify alignment
626
+ console.log('[DataFrame Preview] Raw rows from readRawExcelRows:');
627
+ rawRows.forEach(r => console.log(` sheetRow ${r.sheetRow}:`, r.values.slice(0, 5)));
657
628
 
658
629
  // Auto-detect best header row
659
- let selectedSheetRow = 0;
630
+ let selectedSheetRow = rawRows[0].sheetRow;
660
631
  for (const { sheetRow, values } of rawRows) {
661
632
  const nonEmpty = values.filter(v => v !== null && v !== undefined && String(v).trim() !== '');
662
633
  if (nonEmpty.length < values.length * 0.5) continue;
@@ -703,6 +674,8 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
703
674
  const input = document.getElementById(`${this._id}-header-row`) as HTMLInputElement;
704
675
  const headerRow = parseInt(input.value) || 0;
705
676
 
677
+ console.log(`[DataFrame] Apply clicked: headerRow=${headerRow}`);
678
+
706
679
  this.state.loading = true;
707
680
  this._updateStatus('Re-parsing with new settings...', 'loading');
708
681
 
@@ -752,10 +725,10 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
752
725
  const vals = rawRows.find(r => r.sheetRow === row)?.values ?? [];
753
726
  const headerNames = vals.filter((v: any) => v != null && String(v).trim() !== '').map((v: any) => String(v).trim());
754
727
  const preview = headerNames.slice(0, 4).join(', ') + (headerNames.length > 4 ? '…' : '');
755
- if (row > 0) {
756
- hintDiv.innerHTML = `Row <strong>${row}</strong> selected as header. Columns: <code>${this._escapeHtml(preview)}</code>. Rows 0–${row - 1} will be skipped.`;
728
+ if (row > rawRows[0].sheetRow) {
729
+ hintDiv.innerHTML = `Sheet row <strong>${row}</strong> selected as header. Columns: <code>${this._escapeHtml(preview)}</code>. Rows before it will be skipped.`;
757
730
  } else {
758
- hintDiv.innerHTML = `Row <strong>0</strong> (first row) selected as header. Columns: <code>${this._escapeHtml(preview)}</code>`;
731
+ hintDiv.innerHTML = `Sheet row <strong>${row}</strong> (first row) selected as header. Columns: <code>${this._escapeHtml(preview)}</code>`;
759
732
  }
760
733
  };
761
734
 
@@ -763,11 +736,11 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
763
736
  if (!previewDiv) return;
764
737
  previewDiv.innerHTML = this._buildClickablePreviewHTML(rawRows, selected);
765
738
 
766
- // Wire click handlers on each row
767
739
  previewDiv.querySelectorAll('tr[data-sheet-row]').forEach(tr => {
768
740
  tr.addEventListener('click', () => {
769
741
  const rowIdx = parseInt((tr as HTMLElement).dataset.sheetRow!);
770
742
  hiddenInput.value = String(rowIdx);
743
+ console.log(`[DataFrame Preview] Clicked sheetRow=${rowIdx}`);
771
744
  updateHint(rowIdx);
772
745
  renderPreview(rowIdx);
773
746
  });
@@ -898,10 +871,10 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
898
871
  const vals = rawRows.find(r => r.sheetRow === row)?.values ?? [];
899
872
  const headerNames = vals.filter((v: any) => v != null && String(v).trim() !== '').map((v: any) => String(v).trim());
900
873
  const preview = headerNames.slice(0, 4).join(', ') + (headerNames.length > 4 ? '…' : '');
901
- if (row > 0) {
902
- hintDiv.innerHTML = `Row <strong>${row}</strong> selected as header. Columns: <code>${this._escapeHtml(preview)}</code>. Rows 0–${row - 1} will be skipped.`;
874
+ if (row > rawRows[0].sheetRow) {
875
+ hintDiv.innerHTML = `Sheet row <strong>${row}</strong> selected as header. Columns: <code>${this._escapeHtml(preview)}</code>. Rows before it will be skipped.`;
903
876
  } else {
904
- hintDiv.innerHTML = `Row <strong>0</strong> (first row) selected as header. Columns: <code>${this._escapeHtml(preview)}</code>`;
877
+ hintDiv.innerHTML = `Sheet row <strong>${row}</strong> (first row) selected as header. Columns: <code>${this._escapeHtml(preview)}</code>`;
905
878
  }
906
879
  };
907
880
 
@@ -926,6 +899,7 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
926
899
  tr.addEventListener('click', () => {
927
900
  const rowIdx = parseInt((tr as HTMLElement).dataset.sheetRow!);
928
901
  hiddenInput.value = String(rowIdx);
902
+ console.log(`[DataFrame Preview] Clicked sheetRow=${rowIdx}`);
929
903
  updateHint(rowIdx);
930
904
  renderPreview(rowIdx);
931
905
  });
@@ -87,12 +87,18 @@ export declare class TabularDriver {
87
87
  * Fetch and stream-parse a remote CSV/TSV file
88
88
  */
89
89
  fetch(url: string, options?: ParseOptions): Promise<DataFrame>;
90
+ /**
91
+ * Read raw cell values from first sheet of an Excel file.
92
+ * Returns rows with their actual sheet row indices.
93
+ * Used by both the preview UI and the parser to ensure consistency.
94
+ */
95
+ readRawExcelRows(file: File, maxRows?: number): Promise<{
96
+ sheetRow: number;
97
+ values: any[];
98
+ }[]>;
90
99
  /**
91
100
  * ✅ FIXED: Stream Excel file with optional headerRow override
92
- * headerRow is 0-based: 0 = first row, 1 = second row, etc.
93
- *
94
- * Uses direct cell access instead of sheet_to_json to avoid
95
- * issues with blank row handling and sparse arrays.
101
+ * headerRow is the absolute sheet row index (same as sheetRow from readRawExcelRows).
96
102
  */
97
103
  streamFileMultiSheet(file: File, options?: ParseOptions): Promise<Record<string, DataFrame>>;
98
104
  private _splitLines;
@@ -1 +1 @@
1
- {"version":3,"file":"TabularDriver.d.ts","sourceRoot":"","sources":["TabularDriver.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"TabularDriver.d.ts","sourceRoot":"","sources":["TabularDriver.ts"],"names":[],"mappings":"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"}
@@ -486,12 +486,63 @@ export class TabularDriver {
486
486
  df = df.select(...selectCols);
487
487
  return df;
488
488
  }
489
+ /**
490
+ * Read raw cell values from first sheet of an Excel file.
491
+ * Returns rows with their actual sheet row indices.
492
+ * Used by both the preview UI and the parser to ensure consistency.
493
+ */
494
+ async readRawExcelRows(file, maxRows = 15) {
495
+ let XLSX;
496
+ try {
497
+ XLSX = await import('xlsx');
498
+ }
499
+ catch {
500
+ throw new Error('XLSX support requires the "xlsx" package.');
501
+ }
502
+ const buffer = await file.arrayBuffer();
503
+ const workbook = XLSX.read(buffer, {
504
+ type: 'array',
505
+ sheetRows: maxRows + 5,
506
+ dense: false
507
+ });
508
+ const sheetName = workbook.SheetNames[0];
509
+ const worksheet = workbook.Sheets[sheetName];
510
+ const ref = worksheet['!ref'];
511
+ if (!ref)
512
+ return [];
513
+ const range = XLSX.utils.decode_range(ref);
514
+ const startRow = range.s.r;
515
+ const endRow = Math.min(range.e.r, startRow + maxRows - 1);
516
+ const startCol = range.s.c;
517
+ const endCol = range.e.c;
518
+ console.log(`[readRawExcelRows] ref=${ref}, startRow=${startRow}, endRow=${endRow}, cols=${startCol}-${endCol}`);
519
+ const rows = [];
520
+ for (let r = startRow; r <= endRow; r++) {
521
+ const values = [];
522
+ for (let c = startCol; c <= endCol; c++) {
523
+ const addr = XLSX.utils.encode_cell({ r, c });
524
+ const cell = worksheet[addr];
525
+ if (!cell) {
526
+ values.push(null);
527
+ continue;
528
+ }
529
+ if (cell.w !== undefined) {
530
+ values.push(cell.w);
531
+ continue;
532
+ }
533
+ if (cell.v !== undefined) {
534
+ values.push(cell.v);
535
+ continue;
536
+ }
537
+ values.push(null);
538
+ }
539
+ rows.push({ sheetRow: r, values });
540
+ }
541
+ return rows;
542
+ }
489
543
  /**
490
544
  * ✅ FIXED: Stream Excel file with optional headerRow override
491
- * headerRow is 0-based: 0 = first row, 1 = second row, etc.
492
- *
493
- * Uses direct cell access instead of sheet_to_json to avoid
494
- * issues with blank row handling and sparse arrays.
545
+ * headerRow is the absolute sheet row index (same as sheetRow from readRawExcelRows).
495
546
  */
496
547
  async streamFileMultiSheet(file, options = {}) {
497
548
  const { maxSheetSize = 100000, sheetChunkSize = 10000, onProgress, headerRow = 0 } = options;
@@ -531,7 +582,6 @@ export class TabularDriver {
531
582
  processedSheets++;
532
583
  continue;
533
584
  }
534
- // Direct cell reader - bypasses sheet_to_json completely
535
585
  const readCellValue = (r, c) => {
536
586
  const addr = XLSX.utils.encode_cell({ r, c });
537
587
  const cell = worksheet[addr];
@@ -550,46 +600,37 @@ export class TabularDriver {
550
600
  }
551
601
  return vals;
552
602
  };
553
- // ✅ FIX: headerRow is the user's 0-based index counting from
554
- // the first row of the file. Do NOT add startRow — the user
555
- // sees row 0 as the first row regardless of where the sheet
556
- // range begins. We simply use headerRow as the absolute sheet
557
- // row index.
603
+ // headerRow is the absolute sheet row index same value as
604
+ // sheetRow from readRawExcelRows(). No offset needed.
558
605
  const headerSheetRow = headerRow;
559
- console.log(`[TabularDriver] Sheet "${sheetName}": range=${ref}, startRow=${startRow}, endRow=${endRow}, startCol=${startCol}, endCol=${endCol}`);
560
- console.log(`[TabularDriver] headerRow=${headerRow}, headerSheetRow=${headerSheetRow} (absolute, no startRow offset)`);
606
+ console.log(`[TabularDriver] Sheet "${sheetName}": range=${ref}, startRow=${startRow}, endRow=${endRow}`);
607
+ console.log(`[TabularDriver] headerRow=${headerRow}, headerSheetRow=${headerSheetRow}`);
561
608
  if (headerSheetRow > endRow) {
562
- console.warn(`[TabularDriver] headerRow ${headerRow} (sheet row ${headerSheetRow}) exceeds endRow ${endRow}`);
609
+ console.warn(`[TabularDriver] headerRow ${headerRow} exceeds endRow ${endRow}`);
563
610
  processedSheets++;
564
611
  continue;
565
612
  }
566
- // Read header values directly from cells
567
613
  const headerValues = readRow(headerSheetRow);
568
614
  console.log(`[TabularDriver] Raw header values at sheet row ${headerSheetRow}:`, headerValues);
569
- // Build headers array
570
615
  const headers = headerValues.map((h, i) => {
571
616
  if (h === null || h === undefined || String(h).trim() === '') {
572
617
  return `__EMPTY${i > 0 ? '_' + i : ''}`;
573
618
  }
574
619
  return String(h).trim();
575
620
  });
576
- // Count valid headers
577
621
  const validHeaders = headers.filter(h => !h.startsWith('__EMPTY'));
578
622
  console.log(`[TabularDriver] Headers (${validHeaders.length} valid / ${headers.length} total):`, headers);
579
623
  if (validHeaders.length === 0) {
580
- console.warn(`[TabularDriver] No valid headers found at row ${headerRow} (sheet row ${headerSheetRow}) in sheet "${sheetName}"`);
581
- // Log surrounding rows for debugging
624
+ console.warn(`[TabularDriver] No valid headers found at row ${headerRow} in sheet "${sheetName}"`);
582
625
  for (let debugR = Math.max(startRow, headerSheetRow - 2); debugR <= Math.min(endRow, headerSheetRow + 2); debugR++) {
583
626
  console.log(`[TabularDriver] row ${debugR}:`, readRow(debugR));
584
627
  }
585
628
  processedSheets++;
586
629
  continue;
587
630
  }
588
- // Build data rows: everything after the header row
589
631
  const rows = [];
590
632
  for (let r = headerSheetRow + 1; r <= endRow; r++) {
591
633
  const rowData = readRow(r);
592
- // Skip completely empty rows
593
634
  const hasContent = rowData.some(cell => cell !== null && cell !== undefined && String(cell).trim() !== '');
594
635
  if (!hasContent)
595
636
  continue;
@@ -600,9 +641,6 @@ export class TabularDriver {
600
641
  rows.push(row);
601
642
  }
602
643
  console.log(`[TabularDriver] Built ${rows.length} data rows for sheet "${sheetName}"`);
603
- if (rows.length > 0) {
604
- console.log(`[TabularDriver] First row:`, rows[0]);
605
- }
606
644
  if (rows.length > 0) {
607
645
  sheets[sheetName] = new DataFrame(rows);
608
646
  }
@@ -606,12 +606,59 @@ export class TabularDriver {
606
606
  return df;
607
607
  }
608
608
 
609
+ /**
610
+ * Read raw cell values from first sheet of an Excel file.
611
+ * Returns rows with their actual sheet row indices.
612
+ * Used by both the preview UI and the parser to ensure consistency.
613
+ */
614
+ async readRawExcelRows(file: File, maxRows: number = 15): Promise<{ sheetRow: number; values: any[] }[]> {
615
+ let XLSX: any;
616
+ try {
617
+ XLSX = await import('xlsx');
618
+ } catch {
619
+ throw new Error('XLSX support requires the "xlsx" package.');
620
+ }
621
+
622
+ const buffer = await file.arrayBuffer();
623
+ const workbook = XLSX.read(buffer, {
624
+ type: 'array',
625
+ sheetRows: maxRows + 5,
626
+ dense: false
627
+ });
628
+
629
+ const sheetName = workbook.SheetNames[0];
630
+ const worksheet = workbook.Sheets[sheetName];
631
+ const ref = worksheet['!ref'];
632
+ if (!ref) return [];
633
+
634
+ const range = XLSX.utils.decode_range(ref);
635
+ const startRow = range.s.r;
636
+ const endRow = Math.min(range.e.r, startRow + maxRows - 1);
637
+ const startCol = range.s.c;
638
+ const endCol = range.e.c;
639
+
640
+ console.log(`[readRawExcelRows] ref=${ref}, startRow=${startRow}, endRow=${endRow}, cols=${startCol}-${endCol}`);
641
+
642
+ const rows: { sheetRow: number; values: any[] }[] = [];
643
+ for (let r = startRow; r <= endRow; r++) {
644
+ const values: any[] = [];
645
+ for (let c = startCol; c <= endCol; c++) {
646
+ const addr = XLSX.utils.encode_cell({ r, c });
647
+ const cell = worksheet[addr];
648
+ if (!cell) { values.push(null); continue; }
649
+ if (cell.w !== undefined) { values.push(cell.w); continue; }
650
+ if (cell.v !== undefined) { values.push(cell.v); continue; }
651
+ values.push(null);
652
+ }
653
+ rows.push({ sheetRow: r, values });
654
+ }
655
+
656
+ return rows;
657
+ }
658
+
609
659
  /**
610
660
  * ✅ FIXED: Stream Excel file with optional headerRow override
611
- * headerRow is 0-based: 0 = first row, 1 = second row, etc.
612
- *
613
- * Uses direct cell access instead of sheet_to_json to avoid
614
- * issues with blank row handling and sparse arrays.
661
+ * headerRow is the absolute sheet row index (same as sheetRow from readRawExcelRows).
615
662
  */
616
663
  async streamFileMultiSheet(file: File, options: ParseOptions = {}): Promise<Record<string, DataFrame>> {
617
664
  const { maxSheetSize = 100000, sheetChunkSize = 10000, onProgress, headerRow = 0 } = options;
@@ -659,7 +706,6 @@ export class TabularDriver {
659
706
  continue;
660
707
  }
661
708
 
662
- // Direct cell reader - bypasses sheet_to_json completely
663
709
  const readCellValue = (r: number, c: number): any => {
664
710
  const addr = XLSX.utils.encode_cell({ r, c });
665
711
  const cell = worksheet[addr];
@@ -677,27 +723,22 @@ export class TabularDriver {
677
723
  return vals;
678
724
  };
679
725
 
680
- // ✅ FIX: headerRow is the user's 0-based index counting from
681
- // the first row of the file. Do NOT add startRow — the user
682
- // sees row 0 as the first row regardless of where the sheet
683
- // range begins. We simply use headerRow as the absolute sheet
684
- // row index.
726
+ // headerRow is the absolute sheet row index same value as
727
+ // sheetRow from readRawExcelRows(). No offset needed.
685
728
  const headerSheetRow = headerRow;
686
729
 
687
- console.log(`[TabularDriver] Sheet "${sheetName}": range=${ref}, startRow=${startRow}, endRow=${endRow}, startCol=${startCol}, endCol=${endCol}`);
688
- console.log(`[TabularDriver] headerRow=${headerRow}, headerSheetRow=${headerSheetRow} (absolute, no startRow offset)`);
730
+ console.log(`[TabularDriver] Sheet "${sheetName}": range=${ref}, startRow=${startRow}, endRow=${endRow}`);
731
+ console.log(`[TabularDriver] headerRow=${headerRow}, headerSheetRow=${headerSheetRow}`);
689
732
 
690
733
  if (headerSheetRow > endRow) {
691
- console.warn(`[TabularDriver] headerRow ${headerRow} (sheet row ${headerSheetRow}) exceeds endRow ${endRow}`);
734
+ console.warn(`[TabularDriver] headerRow ${headerRow} exceeds endRow ${endRow}`);
692
735
  processedSheets++;
693
736
  continue;
694
737
  }
695
738
 
696
- // Read header values directly from cells
697
739
  const headerValues = readRow(headerSheetRow);
698
740
  console.log(`[TabularDriver] Raw header values at sheet row ${headerSheetRow}:`, headerValues);
699
741
 
700
- // Build headers array
701
742
  const headers: string[] = headerValues.map((h: any, i: number) => {
702
743
  if (h === null || h === undefined || String(h).trim() === '') {
703
744
  return `__EMPTY${i > 0 ? '_' + i : ''}`;
@@ -705,13 +746,11 @@ export class TabularDriver {
705
746
  return String(h).trim();
706
747
  });
707
748
 
708
- // Count valid headers
709
749
  const validHeaders = headers.filter(h => !h.startsWith('__EMPTY'));
710
750
  console.log(`[TabularDriver] Headers (${validHeaders.length} valid / ${headers.length} total):`, headers);
711
751
 
712
752
  if (validHeaders.length === 0) {
713
- console.warn(`[TabularDriver] No valid headers found at row ${headerRow} (sheet row ${headerSheetRow}) in sheet "${sheetName}"`);
714
- // Log surrounding rows for debugging
753
+ console.warn(`[TabularDriver] No valid headers found at row ${headerRow} in sheet "${sheetName}"`);
715
754
  for (let debugR = Math.max(startRow, headerSheetRow - 2); debugR <= Math.min(endRow, headerSheetRow + 2); debugR++) {
716
755
  console.log(`[TabularDriver] row ${debugR}:`, readRow(debugR));
717
756
  }
@@ -719,12 +758,10 @@ export class TabularDriver {
719
758
  continue;
720
759
  }
721
760
 
722
- // Build data rows: everything after the header row
723
761
  const rows: Record<string, any>[] = [];
724
762
  for (let r = headerSheetRow + 1; r <= endRow; r++) {
725
763
  const rowData = readRow(r);
726
764
 
727
- // Skip completely empty rows
728
765
  const hasContent = rowData.some(cell =>
729
766
  cell !== null && cell !== undefined && String(cell).trim() !== ''
730
767
  );
@@ -738,9 +775,6 @@ export class TabularDriver {
738
775
  }
739
776
 
740
777
  console.log(`[TabularDriver] Built ${rows.length} data rows for sheet "${sheetName}"`);
741
- if (rows.length > 0) {
742
- console.log(`[TabularDriver] First row:`, rows[0]);
743
- }
744
778
 
745
779
  if (rows.length > 0) {
746
780
  sheets[sheetName] = new DataFrame(rows);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "juxscript",
3
- "version": "1.1.206",
3
+ "version": "1.1.207",
4
4
  "type": "module",
5
5
  "description": "A JavaScript UX authorship platform",
6
6
  "main": "index.js",