juxscript 1.1.192 → 1.1.194

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":"AAAA,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAQnC,MAAM,WAAW,gBAAgB;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,KAAK,cAAc,GAAG,SAAS,GAAG;IAC9B,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,qBAAa,kBAAmB,SAAQ,aAAa,CAAC,cAAc,CAAC;IACjE,OAAO,CAAC,GAAG,CAA0B;IACrC,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,KAAK,CAAqB;IAClC,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,aAAa,CAOnB;IACF,OAAO,CAAC,UAAU,CAA2B;IAC7C,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,cAAc,CAAsC;IAC5D,OAAO,CAAC,aAAa,CAAgE;IACrF,OAAO,CAAC,WAAW,CAAiB;IACpC,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,aAAa,CAAkB;IACvC,OAAO,CAAC,eAAe,CAAiB;IACxC,OAAO,CAAC,YAAY,CAAc;IAClC,OAAO,CAAC,mBAAmB,CAAiB;IAC5C,OAAO,CAAC,YAAY,CAAiE;IACrF,OAAO,CAAC,aAAa,CAAsB;IAC3C,OAAO,CAAC,qBAAqB,CAAkB;gBAEnC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB;IAmCtD,SAAS,CAAC,gBAAgB,IAAI,SAAS,MAAM,EAAE;IAC/C,SAAS,CAAC,iBAAiB,IAAI,SAAS,MAAM,EAAE;IAMhD,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAwB9B,UAAU,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAWpC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,IAAI;IAiBnE,UAAU,CAAC,KAAK,GAAE,MAAsB,EAAE,MAAM,GAAE,MAAoC,EAAE,IAAI,GAAE,MAAiB,GAAG,IAAI;IAStH,UAAU,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI;IAC5B,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IAM3B,KAAK,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,SAAS,KAAK,SAAS,GAAG,IAAI;IAQ7C,MAAM,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,GAAG,IAAI;IAI7E,MAAM,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI;IAI/B,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,OAAO,GAAG,IAAI;IAI7C,IAAI,CAAC,CAAC,GAAE,MAAU,GAAG,IAAI;IAIzB,IAAI,CAAC,CAAC,GAAE,MAAU,GAAG,IAAI;IAIzB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,GAAG,GAAG,IAAI;IAIpF,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,GAAG,IAAI,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,GAAG,UAAU,GAAG,YAAY,GAAG,UAAU,EAAE,KAAK,EAAE,GAAG,GAAG,IAAI;IAQxH,IAAI,EAAE,IAAI,SAAS,GAAG,IAAI,CAAqB;IAC/C,IAAI,MAAM,IAAI,aAAa,CAAyB;IACpD,IAAI,KAAK,IAAI,KAAK,GAAG,IAAI,CAAwB;IACjD,QAAQ,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IACtC,KAAK,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM;IACjC,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE;IAC/B,IAAI,KAAK,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAsC;IACnE,IAAI,OAAO,IAAI,MAAM,EAAE,CAAoC;IAErD,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAUhD,OAAO,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI;IACzB,SAAS,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI;IAC3B,QAAQ,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI;IAC1B,UAAU,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI;IAC5B,SAAS,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI;IAC3B,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IAC5B,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IAC7B,cAAc,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IAC/B,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;YAMf,WAAW;IAyDzB,OAAO,CAAC,iBAAiB;IAyGzB,OAAO,CAAC,aAAa;IAuBrB,OAAO,CAAC,aAAa;IAkErB,OAAO,CAAC,oBAAoB;IA6B5B,OAAO,CAAC,sBAAsB;IA8B9B,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,oBAAoB;YASd,sBAAsB;IA8KpC,OAAO,CAAC,oBAAoB;IA+L5B,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,GAAG,IAAI;IAExC,MAAM,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,IAAI;CAoErE;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,kBAAkB,CAExF"}
@@ -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());
@@ -485,15 +486,12 @@ export class DataFrameComponent extends BaseComponent {
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. The Row column shows the index for each row.
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">
495
493
  <div style="font-weight: 600; margin-bottom: 0.5rem; color: hsl(var(--foreground));">
496
- Preview (first 10 rows)
494
+ Preview
497
495
  </div>
498
496
  <div id="${this._id}-preview" class="jux-reshape-preview"></div>
499
497
  </div>
@@ -526,7 +524,6 @@ export class DataFrameComponent extends BaseComponent {
526
524
  this._renderMultiSheet(sheets, this._rawFileData.file.name);
527
525
  }
528
526
  else {
529
- this._showReshapeWarning = false;
530
527
  this._setDataFrame(sheets[sheetNames[0]], this._rawFileData.file.name);
531
528
  }
532
529
  this._reshapeModal.closeModal();
@@ -542,29 +539,70 @@ export class DataFrameComponent extends BaseComponent {
542
539
  await new Promise(resolve => requestAnimationFrame(resolve));
543
540
  const headerRowInput = document.getElementById(`${this._id}-header-row`);
544
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 = headerRow > 0
547
+ ? `Row <strong>${headerRow}</strong> will be used as column headers. ` +
548
+ `The <strong>${headerRow}</strong> row${headerRow > 1 ? 's' : ''} above will be skipped.`
549
+ : `Row <strong>0</strong> (first row) will be used as column headers.`;
550
+ };
545
551
  const updatePreview = async () => {
546
552
  const headerRow = parseInt(headerRowInput?.value) || 0;
553
+ updateHint(headerRow);
547
554
  try {
548
- const sheets = await this._driver.streamFileMultiSheet(this._rawFileData.file, {
555
+ // First, get raw data (headerRow=0) to show skipped rows
556
+ const rawSheets = await this._driver.streamFileMultiSheet(this._rawFileData.file, {
557
+ headerRow: 0,
558
+ maxSheetSize: headerRow + 15
559
+ });
560
+ const rawSheet = Object.values(rawSheets)[0];
561
+ // Then, get parsed data with the chosen header row
562
+ const parsedSheets = await this._driver.streamFileMultiSheet(this._rawFileData.file, {
549
563
  headerRow,
550
- maxSheetSize: headerRow + 20
564
+ maxSheetSize: headerRow + 15
551
565
  });
552
- const firstSheet = Object.values(sheets)[0];
553
- if (!firstSheet) {
566
+ const parsedSheet = Object.values(parsedSheets)[0];
567
+ if (!rawSheet && !parsedSheet) {
554
568
  if (previewDiv)
555
569
  previewDiv.textContent = 'No data found';
556
570
  return;
557
571
  }
558
- const dataRows = firstSheet.toRows().slice(0, 10);
559
- const colHeader = 'Row'.padEnd(6) + firstSheet.columns.map(c => String(c).padEnd(20)).join(' | ');
560
- const separator = '─'.repeat(Math.min(colHeader.length, 120));
561
- const preview = dataRows.map((row, i) => {
562
- const rowIdx = String(headerRow + 1 + i).padEnd(6);
563
- const cols = Object.values(row).map(v => String(v ?? '').padEnd(20)).join(' | ');
564
- return `${rowIdx}${cols}`;
565
- }).join('\n');
572
+ const colWidth = 22;
573
+ const idxWidth = 6;
574
+ const lines = [];
575
+ // Show skipped rows (rows before the header)
576
+ if (rawSheet && headerRow > 0) {
577
+ 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
+ const rawCols = rawSheet.columns;
581
+ lines.push(`${'Idx'.padEnd(idxWidth)}${rawCols.map(c => String(c).substring(0, colWidth - 2).padEnd(colWidth)).join('| ')}`);
582
+ const skippedCount = Math.min(headerRow - 1, rawRows.length);
583
+ for (let i = 0; i < skippedCount; i++) {
584
+ 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}`);
588
+ }
589
+ // Separator: everything above is skipped
590
+ lines.push(`${'─'.repeat(6)}${'── skipped '.padEnd(colWidth * Math.min(rawCols.length, 5), '─')}`);
591
+ }
592
+ // Show header row and data rows from parsed result
593
+ if (parsedSheet) {
594
+ const headerLine = `${'▶'.padEnd(idxWidth)}${parsedSheet.columns.map(c => String(c).substring(0, colWidth - 2).padEnd(colWidth)).join('| ')}`;
595
+ lines.push(headerLine);
596
+ lines.push('═'.repeat(Math.min(headerLine.length, 140)));
597
+ const dataRows = parsedSheet.toRows().slice(0, 8);
598
+ 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('| ');
601
+ lines.push(`${rowIdx}${cols}`);
602
+ });
603
+ }
566
604
  if (previewDiv) {
567
- previewDiv.textContent = `${'Row'.padEnd(6)}Columns: ${firstSheet.columns.join(' | ')}\n${separator}\n${preview}`;
605
+ previewDiv.textContent = lines.join('\n');
568
606
  }
569
607
  }
570
608
  catch (err) {
@@ -605,7 +643,8 @@ export class DataFrameComponent extends BaseComponent {
605
643
  <label style="display: block; font-weight: 600; margin-bottom: 0.5rem;">Skip Rows Before Header</label>
606
644
  <input type="number" id="${this._id}-skip-rows" class="jux-input-element" value="0" min="0" max="50" style="width: 100%;" />
607
645
  </div>
608
- <div class="jux-reshape-preview-container">
646
+ <div id="${this._id}-reshape-hint" class="jux-reshape-hint"></div>
647
+ <div class="jux-reshape-preview-container" style="margin-top: 1rem;">
609
648
  <div style="font-weight: 600; margin-bottom: 0.5rem; color: hsl(var(--foreground));">Preview</div>
610
649
  <div id="${this._id}-preview" class="jux-reshape-preview"></div>
611
650
  </div>
@@ -640,7 +679,6 @@ export class DataFrameComponent extends BaseComponent {
640
679
  hasHeader: true
641
680
  });
642
681
  await this._driver.store(this._rawFileData.file.name, df, { source: this._rawFileData.file.name });
643
- this._showReshapeWarning = false;
644
682
  this._setDataFrame(df, this._rawFileData.file.name);
645
683
  this._reshapeModal.closeModal();
646
684
  }
@@ -657,6 +695,7 @@ export class DataFrameComponent extends BaseComponent {
657
695
  const headerRowInput = document.getElementById(`${this._id}-header-row`);
658
696
  const skipRowsInput = document.getElementById(`${this._id}-skip-rows`);
659
697
  const previewDiv = document.getElementById(`${this._id}-preview`);
698
+ const hintDiv = document.getElementById(`${this._id}-reshape-hint`);
660
699
  if (this._rawFileData?.text) {
661
700
  const detected = this._driver._detectDelimiter(this._rawFileData.text);
662
701
  if (delimiterSelect)
@@ -665,30 +704,69 @@ export class DataFrameComponent extends BaseComponent {
665
704
  if (headerRowInput)
666
705
  headerRowInput.value = String(detectedHeaderRow);
667
706
  }
707
+ const updateHint = () => {
708
+ if (!hintDiv)
709
+ return;
710
+ const headerRow = parseInt(headerRowInput?.value) || 0;
711
+ const skipRows = parseInt(skipRowsInput?.value) || 0;
712
+ const totalSkipped = headerRow + skipRows;
713
+ hintDiv.innerHTML = totalSkipped > 0
714
+ ? `Row <strong>${headerRow + skipRows}</strong> will be used as column headers. ` +
715
+ `<strong>${totalSkipped}</strong> row${totalSkipped > 1 ? 's' : ''} above will be skipped.`
716
+ : `Row <strong>0</strong> (first row) will be used as column headers.`;
717
+ };
668
718
  const updatePreview = () => {
669
719
  if (!this._rawFileData?.text)
670
720
  return;
671
721
  const delim = delimiterSelect?.value || ',';
672
722
  const headerRow = parseInt(headerRowInput?.value) || 0;
673
723
  const skipRows = parseInt(skipRowsInput?.value) || 0;
724
+ updateHint();
674
725
  try {
726
+ const colWidth = 22;
727
+ const idxWidth = 6;
728
+ const lines = [];
729
+ const totalOffset = headerRow + skipRows;
730
+ // Parse raw (no header offset) to show skipped rows
731
+ if (totalOffset > 0) {
732
+ const rawDf = this._driver.parseCSV(this._rawFileData.text, {
733
+ delimiter: delim,
734
+ headerRow: 0,
735
+ skipRows: 0,
736
+ hasHeader: true,
737
+ maxRows: totalOffset + 1
738
+ });
739
+ const rawCols = rawDf.columns;
740
+ lines.push(`${'Idx'.padEnd(idxWidth)}${rawCols.map(c => String(c).substring(0, colWidth - 2).padEnd(colWidth)).join('| ')}`);
741
+ const rawRows = rawDf.toRows();
742
+ const skippedCount = Math.min(totalOffset - 1, rawRows.length);
743
+ for (let i = 0; i < skippedCount; i++) {
744
+ 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}`);
748
+ }
749
+ lines.push(`${'─'.repeat(6)}${'── skipped '.padEnd(colWidth * Math.min(rawCols.length, 5), '─')}`);
750
+ }
751
+ // Parse with actual settings for header + data rows
675
752
  const df = this._driver.parseCSV(this._rawFileData.text, {
676
753
  delimiter: delim,
677
754
  headerRow,
678
755
  skipRows,
679
756
  hasHeader: true,
680
- maxRows: 10
757
+ maxRows: 8
681
758
  });
759
+ const headerLine = `${'▶'.padEnd(idxWidth)}${df.columns.map(c => String(c).substring(0, colWidth - 2).padEnd(colWidth)).join('| ')}`;
760
+ lines.push(headerLine);
761
+ lines.push('═'.repeat(Math.min(headerLine.length, 140)));
682
762
  const dataRows = df.toRows();
683
- const colHeader = 'Row'.padEnd(6) + df.columns.map(c => String(c).padEnd(20)).join(' | ');
684
- const separator = '─'.repeat(Math.min(colHeader.length, 120));
685
- const preview = dataRows.map((row, i) => {
686
- const rowIdx = String(headerRow + skipRows + 1 + i).padEnd(6);
687
- const cols = Object.values(row).map(v => String(v ?? '').padEnd(20)).join(' | ');
688
- return `${rowIdx}${cols}`;
689
- }).join('\n');
763
+ 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('| ');
766
+ lines.push(`${rowIdx}${cols}`);
767
+ });
690
768
  if (previewDiv) {
691
- previewDiv.textContent = `${'Row'.padEnd(6)}Columns: ${df.columns.join(' | ')}\n${separator}\n${preview}`;
769
+ previewDiv.textContent = lines.join('\n');
692
770
  }
693
771
  }
694
772
  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());
@@ -603,15 +604,12 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
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. The Row column shows the index for each row.
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">
613
611
  <div style="font-weight: 600; margin-bottom: 0.5rem; color: hsl(var(--foreground));">
614
- Preview (first 10 rows)
612
+ Preview
615
613
  </div>
616
614
  <div id="${this._id}-preview" class="jux-reshape-preview"></div>
617
615
  </div>
@@ -648,7 +646,6 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
648
646
  if (sheetNames.length > 1) {
649
647
  this._renderMultiSheet(sheets, this._rawFileData!.file.name);
650
648
  } else {
651
- this._showReshapeWarning = false;
652
649
  this._setDataFrame(sheets[sheetNames[0]], this._rawFileData!.file.name);
653
650
  }
654
651
 
@@ -667,32 +664,80 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
667
664
 
668
665
  const headerRowInput = document.getElementById(`${this._id}-header-row`) as HTMLInputElement;
669
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 = headerRow > 0
672
+ ? `Row <strong>${headerRow}</strong> will be used as column headers. ` +
673
+ `The <strong>${headerRow}</strong> row${headerRow > 1 ? 's' : ''} above will be skipped.`
674
+ : `Row <strong>0</strong> (first row) will be used as column headers.`;
675
+ };
670
676
 
671
677
  const updatePreview = async () => {
672
678
  const headerRow = parseInt(headerRowInput?.value) || 0;
679
+ updateHint(headerRow);
680
+
673
681
  try {
674
- const sheets = await this._driver.streamFileMultiSheet(this._rawFileData!.file, {
682
+ // First, get raw data (headerRow=0) to show skipped rows
683
+ const rawSheets = await this._driver.streamFileMultiSheet(this._rawFileData!.file, {
684
+ headerRow: 0,
685
+ maxSheetSize: headerRow + 15
686
+ });
687
+ const rawSheet = Object.values(rawSheets)[0];
688
+
689
+ // Then, get parsed data with the chosen header row
690
+ const parsedSheets = await this._driver.streamFileMultiSheet(this._rawFileData!.file, {
675
691
  headerRow,
676
- maxSheetSize: headerRow + 20
692
+ maxSheetSize: headerRow + 15
677
693
  });
694
+ const parsedSheet = Object.values(parsedSheets)[0];
678
695
 
679
- const firstSheet = Object.values(sheets)[0];
680
- if (!firstSheet) {
696
+ if (!rawSheet && !parsedSheet) {
681
697
  if (previewDiv) previewDiv.textContent = 'No data found';
682
698
  return;
683
699
  }
684
700
 
685
- const dataRows = firstSheet.toRows().slice(0, 10);
686
- const colHeader = 'Row'.padEnd(6) + firstSheet.columns.map(c => String(c).padEnd(20)).join(' | ');
687
- const separator = '─'.repeat(Math.min(colHeader.length, 120));
688
- const preview = dataRows.map((row, i) => {
689
- const rowIdx = String(headerRow + 1 + i).padEnd(6);
690
- const cols = Object.values(row).map(v => String(v ?? '').padEnd(20)).join(' | ');
691
- return `${rowIdx}${cols}`;
692
- }).join('\n');
701
+ const colWidth = 22;
702
+ const idxWidth = 6;
703
+ const lines: string[] = [];
704
+
705
+ // Show skipped rows (rows before the header)
706
+ if (rawSheet && headerRow > 0) {
707
+ 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
+ const rawCols = rawSheet.columns;
711
+ lines.push(`${'Idx'.padEnd(idxWidth)}${rawCols.map(c => String(c).substring(0, colWidth - 2).padEnd(colWidth)).join('| ')}`);
712
+
713
+ const skippedCount = Math.min(headerRow - 1, rawRows.length);
714
+ for (let i = 0; i < skippedCount; i++) {
715
+ 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}`);
719
+ }
720
+
721
+ // Separator: everything above is skipped
722
+ lines.push(`${'─'.repeat(6)}${'── skipped '.padEnd(colWidth * Math.min(rawCols.length, 5), '─')}`);
723
+ }
724
+
725
+ // Show header row and data rows from parsed result
726
+ if (parsedSheet) {
727
+ const headerLine = `${'▶'.padEnd(idxWidth)}${parsedSheet.columns.map(c => String(c).substring(0, colWidth - 2).padEnd(colWidth)).join('| ')}`;
728
+ lines.push(headerLine);
729
+ lines.push('═'.repeat(Math.min(headerLine.length, 140)));
730
+
731
+ const dataRows = parsedSheet.toRows().slice(0, 8);
732
+ 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('| ');
735
+ lines.push(`${rowIdx}${cols}`);
736
+ });
737
+ }
693
738
 
694
739
  if (previewDiv) {
695
- previewDiv.textContent = `${'Row'.padEnd(6)}Columns: ${firstSheet.columns.join(' | ')}\n${separator}\n${preview}`;
740
+ previewDiv.textContent = lines.join('\n');
696
741
  }
697
742
  } catch (err: any) {
698
743
  if (previewDiv) previewDiv.textContent = `Error: ${err.message}`;
@@ -735,7 +780,8 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
735
780
  <label style="display: block; font-weight: 600; margin-bottom: 0.5rem;">Skip Rows Before Header</label>
736
781
  <input type="number" id="${this._id}-skip-rows" class="jux-input-element" value="0" min="0" max="50" style="width: 100%;" />
737
782
  </div>
738
- <div class="jux-reshape-preview-container">
783
+ <div id="${this._id}-reshape-hint" class="jux-reshape-hint"></div>
784
+ <div class="jux-reshape-preview-container" style="margin-top: 1rem;">
739
785
  <div style="font-weight: 600; margin-bottom: 0.5rem; color: hsl(var(--foreground));">Preview</div>
740
786
  <div id="${this._id}-preview" class="jux-reshape-preview"></div>
741
787
  </div>
@@ -775,7 +821,6 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
775
821
  });
776
822
 
777
823
  await this._driver.store(this._rawFileData.file.name, df, { source: this._rawFileData.file.name });
778
- this._showReshapeWarning = false;
779
824
  this._setDataFrame(df, this._rawFileData.file.name);
780
825
 
781
826
  this._reshapeModal!.closeModal();
@@ -794,6 +839,7 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
794
839
  const headerRowInput = document.getElementById(`${this._id}-header-row`) as HTMLInputElement;
795
840
  const skipRowsInput = document.getElementById(`${this._id}-skip-rows`) as HTMLInputElement;
796
841
  const previewDiv = document.getElementById(`${this._id}-preview`)!;
842
+ const hintDiv = document.getElementById(`${this._id}-reshape-hint`)!;
797
843
 
798
844
  if (this._rawFileData?.text) {
799
845
  const detected = (this._driver as any)._detectDelimiter(this._rawFileData.text);
@@ -803,6 +849,17 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
803
849
  if (headerRowInput) headerRowInput.value = String(detectedHeaderRow);
804
850
  }
805
851
 
852
+ const updateHint = () => {
853
+ if (!hintDiv) return;
854
+ const headerRow = parseInt(headerRowInput?.value) || 0;
855
+ const skipRows = parseInt(skipRowsInput?.value) || 0;
856
+ const totalSkipped = headerRow + skipRows;
857
+ hintDiv.innerHTML = totalSkipped > 0
858
+ ? `Row <strong>${headerRow + skipRows}</strong> will be used as column headers. ` +
859
+ `<strong>${totalSkipped}</strong> row${totalSkipped > 1 ? 's' : ''} above will be skipped.`
860
+ : `Row <strong>0</strong> (first row) will be used as column headers.`;
861
+ };
862
+
806
863
  const updatePreview = () => {
807
864
  if (!this._rawFileData?.text) return;
808
865
 
@@ -810,26 +867,61 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
810
867
  const headerRow = parseInt(headerRowInput?.value) || 0;
811
868
  const skipRows = parseInt(skipRowsInput?.value) || 0;
812
869
 
870
+ updateHint();
871
+
813
872
  try {
873
+ const colWidth = 22;
874
+ const idxWidth = 6;
875
+ const lines: string[] = [];
876
+ const totalOffset = headerRow + skipRows;
877
+
878
+ // Parse raw (no header offset) to show skipped rows
879
+ if (totalOffset > 0) {
880
+ const rawDf = this._driver.parseCSV(this._rawFileData.text, {
881
+ delimiter: delim,
882
+ headerRow: 0,
883
+ skipRows: 0,
884
+ hasHeader: true,
885
+ maxRows: totalOffset + 1
886
+ });
887
+
888
+ const rawCols = rawDf.columns;
889
+ lines.push(`${'Idx'.padEnd(idxWidth)}${rawCols.map(c => String(c).substring(0, colWidth - 2).padEnd(colWidth)).join('| ')}`);
890
+
891
+ const rawRows = rawDf.toRows();
892
+ const skippedCount = Math.min(totalOffset - 1, rawRows.length);
893
+ for (let i = 0; i < skippedCount; i++) {
894
+ 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}`);
898
+ }
899
+
900
+ lines.push(`${'─'.repeat(6)}${'── skipped '.padEnd(colWidth * Math.min(rawCols.length, 5), '─')}`);
901
+ }
902
+
903
+ // Parse with actual settings for header + data rows
814
904
  const df = this._driver.parseCSV(this._rawFileData.text, {
815
905
  delimiter: delim,
816
906
  headerRow,
817
907
  skipRows,
818
908
  hasHeader: true,
819
- maxRows: 10
909
+ maxRows: 8
820
910
  });
821
911
 
912
+ const headerLine = `${'▶'.padEnd(idxWidth)}${df.columns.map(c => String(c).substring(0, colWidth - 2).padEnd(colWidth)).join('| ')}`;
913
+ lines.push(headerLine);
914
+ lines.push('═'.repeat(Math.min(headerLine.length, 140)));
915
+
822
916
  const dataRows = df.toRows();
823
- const colHeader = 'Row'.padEnd(6) + df.columns.map(c => String(c).padEnd(20)).join(' | ');
824
- const separator = '─'.repeat(Math.min(colHeader.length, 120));
825
- const preview = dataRows.map((row, i) => {
826
- const rowIdx = String(headerRow + skipRows + 1 + i).padEnd(6);
827
- const cols = Object.values(row).map(v => String(v ?? '').padEnd(20)).join(' | ');
828
- return `${rowIdx}${cols}`;
829
- }).join('\n');
917
+ 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('| ');
920
+ lines.push(`${rowIdx}${cols}`);
921
+ });
830
922
 
831
923
  if (previewDiv) {
832
- previewDiv.textContent = `${'Row'.padEnd(6)}Columns: ${df.columns.join(' | ')}\n${separator}\n${preview}`;
924
+ previewDiv.textContent = lines.join('\n');
833
925
  }
834
926
  } catch (err: any) {
835
927
  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.192",
3
+ "version": "1.1.194",
4
4
  "type": "module",
5
5
  "description": "A JavaScript UX authorship platform",
6
6
  "main": "index.js",