juxscript 1.1.181 → 1.1.183

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.
@@ -16,6 +16,8 @@ export interface DataFrameOptions {
16
16
  icon?: string;
17
17
  maxSheetSize?: number;
18
18
  sheetChunkSize?: number;
19
+ maxFileSize?: number;
20
+ showReshapeWarning?: boolean;
19
21
  style?: string;
20
22
  class?: string;
21
23
  }
@@ -40,6 +42,9 @@ export declare class DataFrameComponent extends BaseComponent<DataFrameState> {
40
42
  private _icon;
41
43
  private _maxSheetSize;
42
44
  private _sheetChunkSize;
45
+ private _maxFileSize;
46
+ private _showReshapeWarning;
47
+ private _rawFileData;
43
48
  constructor(id: string, options?: DataFrameOptions);
44
49
  protected getTriggerEvents(): readonly string[];
45
50
  protected getCallbackEvents(): readonly string[];
@@ -80,6 +85,10 @@ export declare class DataFrameComponent extends BaseComponent<DataFrameState> {
80
85
  * ✅ NEW: Set chunk size for processing large sheets
81
86
  */
82
87
  sheetChunkSize(v: number): this;
88
+ /**
89
+ * ✅ NEW: Set max file size in MB
90
+ */
91
+ maxFileSize(mb: number): this;
83
92
  /**
84
93
  * ✅ FIXED: Render multiple Excel sheets as tabs
85
94
  */
@@ -88,6 +97,7 @@ export declare class DataFrameComponent extends BaseComponent<DataFrameState> {
88
97
  private _setDataFrame;
89
98
  private _updateTable;
90
99
  private _showFilterInput;
100
+ private _showReshapeModal;
91
101
  update(prop: string, value: any): void;
92
102
  render(targetId?: string | HTMLElement | BaseComponent<any>): this;
93
103
  }
@@ -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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"}
@@ -32,6 +32,9 @@ export class DataFrameComponent extends BaseComponent {
32
32
  this._icon = '';
33
33
  this._maxSheetSize = 100000; // ✅ Default 100k rows
34
34
  this._sheetChunkSize = 10000; // ✅ Default 10k chunk
35
+ this._maxFileSize = 50; // ✅ Default 50MB
36
+ this._showReshapeWarning = true;
37
+ this._rawFileData = null; // ✅ Store for re-parsing
35
38
  this._driver = new TabularDriver(options.dbName ?? 'jux-dataframes', options.storeName ?? 'frames');
36
39
  this._showStatus = options.showStatus ?? true;
37
40
  this._icon = options.icon ?? '';
@@ -45,6 +48,8 @@ export class DataFrameComponent extends BaseComponent {
45
48
  };
46
49
  this._maxSheetSize = options.maxSheetSize ?? 100000;
47
50
  this._sheetChunkSize = options.sheetChunkSize ?? 10000;
51
+ this._maxFileSize = options.maxFileSize ?? 50;
52
+ this._showReshapeWarning = options.showReshapeWarning ?? true;
48
53
  }
49
54
  getTriggerEvents() { return TRIGGER_EVENTS; }
50
55
  getCallbackEvents() { return CALLBACK_EVENTS; }
@@ -87,6 +92,12 @@ export class DataFrameComponent extends BaseComponent {
87
92
  if (!files || files.length === 0)
88
93
  return;
89
94
  const file = files[0];
95
+ // ✅ Check file size
96
+ const fileSizeMB = file.size / (1024 * 1024);
97
+ if (fileSizeMB > this._maxFileSize) {
98
+ this._updateStatus(`❌ File too large (${fileSizeMB.toFixed(1)}MB). Max: ${this._maxFileSize}MB`, 'error');
99
+ return;
100
+ }
90
101
  this.state.loading = true;
91
102
  this._updateStatus('⏳ Parsing ' + file.name + '...', 'loading');
92
103
  try {
@@ -112,8 +123,13 @@ export class DataFrameComponent extends BaseComponent {
112
123
  }
113
124
  }
114
125
  else {
115
- // CSV/TSV: use existing streaming
116
- const df = await this._driver.streamFile(file);
126
+ // CSV/TSV: Store raw text for reshaping
127
+ const text = await file.text();
128
+ this._rawFileData = { file, text };
129
+ const df = this._driver.parseCSV(text, {
130
+ autoDetectDelimiter: true,
131
+ hasHeader: true
132
+ });
117
133
  await this._driver.store(file.name, df, { source: file.name });
118
134
  this._setDataFrame(df, file.name);
119
135
  }
@@ -230,6 +246,13 @@ export class DataFrameComponent extends BaseComponent {
230
246
  this._sheetChunkSize = v;
231
247
  return this;
232
248
  }
249
+ /**
250
+ * ✅ NEW: Set max file size in MB
251
+ */
252
+ maxFileSize(mb) {
253
+ this._maxFileSize = mb;
254
+ return this;
255
+ }
233
256
  /* ═══════════════════════════════════════════════════
234
257
  * MULTI-SHEET RENDERING
235
258
  * ═══════════════════════════════════════════════════ */
@@ -370,8 +393,17 @@ export class DataFrameComponent extends BaseComponent {
370
393
  }
371
394
  this._updateTable();
372
395
  this._updateStatus(`${sourceName} — ${this._df.height} rows × ${this._df.width} cols`, 'success');
373
- if (this._tableOptions.filterable) {
374
- this._showFilterInput();
396
+ // ✅ Add reshape warning button if CSV and enabled
397
+ if (this._showReshapeWarning && this._rawFileData?.text) {
398
+ const statusEl = document.getElementById(`${this._id}-status`);
399
+ if (statusEl) {
400
+ const settingsBtn = document.createElement('button');
401
+ settingsBtn.textContent = 'Settings';
402
+ settingsBtn.className = 'jux-button jux-button-sm jux-button-ghost';
403
+ settingsBtn.style.marginLeft = '0.5rem';
404
+ settingsBtn.addEventListener('click', () => this._showReshapeModal());
405
+ statusEl.appendChild(settingsBtn);
406
+ }
375
407
  }
376
408
  this._triggerCallback('load', this._df, null, this);
377
409
  }
@@ -449,6 +481,10 @@ export class DataFrameComponent extends BaseComponent {
449
481
  this._table?.rows(filtered.toRows());
450
482
  });
451
483
  }
484
+ _showReshapeModal() {
485
+ // TODO: Implement reshape modal for re-parsing CSV with different options
486
+ console.warn('Reshape modal not yet implemented');
487
+ }
452
488
  update(prop, value) { }
453
489
  /* ═══════════════════════════════════════════════════
454
490
  * RENDER
@@ -22,6 +22,8 @@ export interface DataFrameOptions {
22
22
  icon?: string;
23
23
  maxSheetSize?: number; // ✅ NEW: Max rows per sheet (default: 100k)
24
24
  sheetChunkSize?: number; // ✅ NEW: Chunk size for large sheets (default: 10k)
25
+ maxFileSize?: number; // ✅ NEW: Max file size in MB (default: 50MB)
26
+ showReshapeWarning?: boolean; // ✅ NEW: Show warning when data looks malformed
25
27
  style?: string;
26
28
  class?: string;
27
29
  }
@@ -55,6 +57,9 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
55
57
  private _icon: string = '';
56
58
  private _maxSheetSize: number = 100000; // ✅ Default 100k rows
57
59
  private _sheetChunkSize: number = 10000; // ✅ Default 10k chunk
60
+ private _maxFileSize: number = 50; // ✅ Default 50MB
61
+ private _showReshapeWarning: boolean = true;
62
+ private _rawFileData: { file: File; text?: string } | null = null; // ✅ Store for re-parsing
58
63
 
59
64
  constructor(id: string, options: DataFrameOptions = {}) {
60
65
  super(id, {
@@ -87,6 +92,8 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
87
92
  };
88
93
  this._maxSheetSize = options.maxSheetSize ?? 100000;
89
94
  this._sheetChunkSize = options.sheetChunkSize ?? 10000;
95
+ this._maxFileSize = options.maxFileSize ?? 50;
96
+ this._showReshapeWarning = options.showReshapeWarning ?? true;
90
97
  }
91
98
 
92
99
  protected getTriggerEvents(): readonly string[] { return TRIGGER_EVENTS; }
@@ -126,6 +133,14 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
126
133
  upload.bind('change', async (files: File[]) => {
127
134
  if (!files || files.length === 0) return;
128
135
  const file = files[0];
136
+
137
+ // ✅ Check file size
138
+ const fileSizeMB = file.size / (1024 * 1024);
139
+ if (fileSizeMB > this._maxFileSize) {
140
+ this._updateStatus(`❌ File too large (${fileSizeMB.toFixed(1)}MB). Max: ${this._maxFileSize}MB`, 'error');
141
+ return;
142
+ }
143
+
129
144
  this.state.loading = true;
130
145
  this._updateStatus('⏳ Parsing ' + file.name + '...', 'loading');
131
146
 
@@ -154,8 +169,15 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
154
169
  this._setDataFrame(sheets[sheetNames[0]], file.name);
155
170
  }
156
171
  } else {
157
- // CSV/TSV: use existing streaming
158
- const df = await this._driver.streamFile(file);
172
+ // CSV/TSV: Store raw text for reshaping
173
+ const text = await file.text();
174
+ this._rawFileData = { file, text };
175
+
176
+ const df = this._driver.parseCSV(text, {
177
+ autoDetectDelimiter: true,
178
+ hasHeader: true
179
+ });
180
+
159
181
  await this._driver.store(file.name, df, { source: file.name });
160
182
  this._setDataFrame(df, file.name);
161
183
  }
@@ -284,6 +306,14 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
284
306
  return this;
285
307
  }
286
308
 
309
+ /**
310
+ * ✅ NEW: Set max file size in MB
311
+ */
312
+ maxFileSize(mb: number): this {
313
+ this._maxFileSize = mb;
314
+ return this;
315
+ }
316
+
287
317
  /* ═══════════════════════════════════════════════════
288
318
  * MULTI-SHEET RENDERING
289
319
  * ═══════════════════════════════════════════════════ */
@@ -463,8 +493,17 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
463
493
  'success'
464
494
  );
465
495
 
466
- if (this._tableOptions.filterable) {
467
- this._showFilterInput();
496
+ // ✅ Add reshape warning button if CSV and enabled
497
+ if (this._showReshapeWarning && this._rawFileData?.text) {
498
+ const statusEl = document.getElementById(`${this._id}-status`);
499
+ if (statusEl) {
500
+ const settingsBtn = document.createElement('button');
501
+ settingsBtn.textContent = 'Settings';
502
+ settingsBtn.className = 'jux-button jux-button-sm jux-button-ghost';
503
+ settingsBtn.style.marginLeft = '0.5rem';
504
+ settingsBtn.addEventListener('click', () => this._showReshapeModal());
505
+ statusEl.appendChild(settingsBtn);
506
+ }
468
507
  }
469
508
 
470
509
  this._triggerCallback('load', this._df, null, this);
@@ -557,6 +596,11 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
557
596
  });
558
597
  }
559
598
 
599
+ private _showReshapeModal(): void {
600
+ // TODO: Implement reshape modal for re-parsing CSV with different options
601
+ console.warn('Reshape modal not yet implemented');
602
+ }
603
+
560
604
  update(prop: string, value: any): void { }
561
605
 
562
606
  /* ═══════════════════════════════════════════════════
@@ -20,6 +20,8 @@ export interface ParseOptions {
20
20
  sheet?: string | number;
21
21
  maxSheetSize?: number;
22
22
  sheetChunkSize?: number;
23
+ headerRow?: number;
24
+ autoDetectDelimiter?: boolean;
23
25
  }
24
26
  export declare class TabularDriver {
25
27
  private _dbName;
@@ -27,6 +29,16 @@ export declare class TabularDriver {
27
29
  private _db;
28
30
  constructor(dbName?: string, storeName?: string);
29
31
  open(): Promise<IDBDatabase>;
32
+ /**
33
+ * ✅ NEW: Auto-detect delimiter from first N lines of CSV
34
+ * Checks for: , | \t ;
35
+ */
36
+ private _detectDelimiter;
37
+ /**
38
+ * ✅ NEW: Detect which row contains the header
39
+ * Looks for first row with mostly string values
40
+ */
41
+ private _detectHeaderRow;
30
42
  /**
31
43
  * Parse a CSV/TSV string into a DataFrame
32
44
  */
@@ -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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"}
@@ -28,17 +28,85 @@ export class TabularDriver {
28
28
  /* ═══════════════════════════════════════════════════
29
29
  * CSV / TSV PARSING
30
30
  * ═══════════════════════════════════════════════════ */
31
+ /**
32
+ * ✅ NEW: Auto-detect delimiter from first N lines of CSV
33
+ * Checks for: , | \t ;
34
+ */
35
+ _detectDelimiter(text, sampleLines = 5) {
36
+ const lines = this._splitLines(text).slice(0, sampleLines).filter(l => l.trim());
37
+ if (lines.length === 0)
38
+ return ',';
39
+ const delimiters = [',', '|', '\t', ';'];
40
+ const scores = {};
41
+ delimiters.forEach(delim => {
42
+ const counts = lines.map(line => {
43
+ // Count occurrences of delimiter NOT inside quotes
44
+ let count = 0;
45
+ let inQuotes = false;
46
+ for (let i = 0; i < line.length; i++) {
47
+ if (line[i] === '"')
48
+ inQuotes = !inQuotes;
49
+ if (!inQuotes && line[i] === delim)
50
+ count++;
51
+ }
52
+ return count;
53
+ });
54
+ // Delimiter should have consistent count across lines
55
+ const avg = counts.reduce((sum, c) => sum + c, 0) / counts.length;
56
+ const variance = counts.reduce((sum, c) => sum + Math.pow(c - avg, 2), 0) / counts.length;
57
+ // Score: high count, low variance
58
+ scores[delim] = avg > 0 ? avg / (1 + variance) : 0;
59
+ });
60
+ // Return delimiter with highest score
61
+ const best = Object.entries(scores).sort((a, b) => b[1] - a[1])[0];
62
+ return best[0];
63
+ }
64
+ /**
65
+ * ✅ NEW: Detect which row contains the header
66
+ * Looks for first row with mostly string values
67
+ */
68
+ _detectHeaderRow(text, delimiter, maxCheck = 10) {
69
+ const lines = this._splitLines(text).slice(0, maxCheck).filter(l => l.trim());
70
+ for (let i = 0; i < lines.length; i++) {
71
+ const values = this._parseLine(lines[i], delimiter);
72
+ // Skip if mostly empty
73
+ const nonEmpty = values.filter(v => v.trim()).length;
74
+ if (nonEmpty < values.length * 0.5)
75
+ continue;
76
+ // Check if mostly non-numeric (likely headers)
77
+ const nonNumeric = values.filter(v => {
78
+ const trimmed = v.trim();
79
+ return isNaN(Number(trimmed)) && trimmed !== '';
80
+ }).length;
81
+ if (nonNumeric >= values.length * 0.7) {
82
+ return i;
83
+ }
84
+ }
85
+ return 0; // Fallback to first row
86
+ }
31
87
  /**
32
88
  * Parse a CSV/TSV string into a DataFrame
33
89
  */
34
90
  parseCSV(text, options = {}) {
35
- const { delimiter = ',', hasHeader = true, maxRows, skipRows = 0, columns: selectCols } = options;
91
+ const { delimiter: userDelimiter, hasHeader = true, maxRows, skipRows: userSkipRows = 0, columns: selectCols, headerRow: userHeaderRow, autoDetectDelimiter = true } = options;
92
+ // ✅ Auto-detect delimiter if not provided
93
+ const delimiter = userDelimiter || (autoDetectDelimiter ? this._detectDelimiter(text) : ',');
94
+ // ✅ Auto-detect header row if not provided
95
+ const headerRow = userHeaderRow !== undefined ? userHeaderRow : (hasHeader ? this._detectHeaderRow(text, delimiter) : -1);
36
96
  const lines = this._splitLines(text);
37
- let startIdx = skipRows;
38
97
  let headers;
39
- if (hasHeader && lines.length > startIdx) {
40
- headers = this._parseLine(lines[startIdx], delimiter);
41
- startIdx++;
98
+ // Skip rows before header
99
+ let startIdx = userSkipRows;
100
+ if (hasHeader && headerRow >= 0) {
101
+ const headerLine = lines[headerRow + userSkipRows];
102
+ if (headerLine) {
103
+ headers = this._parseLine(headerLine, delimiter);
104
+ startIdx = headerRow + userSkipRows + 1;
105
+ }
106
+ else {
107
+ const firstLine = this._parseLine(lines[startIdx] || '', delimiter);
108
+ headers = firstLine.map((_, i) => `col_${i}`);
109
+ }
42
110
  }
43
111
  else {
44
112
  const firstLine = this._parseLine(lines[startIdx] || '', delimiter);
@@ -22,6 +22,8 @@ export interface ParseOptions {
22
22
  sheet?: string | number;
23
23
  maxSheetSize?: number; // ✅ NEW: Max rows per sheet to prevent memory issues
24
24
  sheetChunkSize?: number; // ✅ NEW: Process Excel in chunks
25
+ headerRow?: number; // ✅ NEW: Which row contains headers (default: 0)
26
+ autoDetectDelimiter?: boolean; // ✅ NEW: Auto-detect delimiter from first lines
25
27
  }
26
28
 
27
29
  export class TabularDriver {
@@ -62,25 +64,105 @@ export class TabularDriver {
62
64
  * CSV / TSV PARSING
63
65
  * ═══════════════════════════════════════════════════ */
64
66
 
67
+ /**
68
+ * ✅ NEW: Auto-detect delimiter from first N lines of CSV
69
+ * Checks for: , | \t ;
70
+ */
71
+ private _detectDelimiter(text: string, sampleLines: number = 5): string {
72
+ const lines = this._splitLines(text).slice(0, sampleLines).filter(l => l.trim());
73
+ if (lines.length === 0) return ',';
74
+
75
+ const delimiters = [',', '|', '\t', ';'];
76
+ const scores: Record<string, number> = {};
77
+
78
+ delimiters.forEach(delim => {
79
+ const counts = lines.map(line => {
80
+ // Count occurrences of delimiter NOT inside quotes
81
+ let count = 0;
82
+ let inQuotes = false;
83
+ for (let i = 0; i < line.length; i++) {
84
+ if (line[i] === '"') inQuotes = !inQuotes;
85
+ if (!inQuotes && line[i] === delim) count++;
86
+ }
87
+ return count;
88
+ });
89
+
90
+ // Delimiter should have consistent count across lines
91
+ const avg = counts.reduce((sum, c) => sum + c, 0) / counts.length;
92
+ const variance = counts.reduce((sum, c) => sum + Math.pow(c - avg, 2), 0) / counts.length;
93
+
94
+ // Score: high count, low variance
95
+ scores[delim] = avg > 0 ? avg / (1 + variance) : 0;
96
+ });
97
+
98
+ // Return delimiter with highest score
99
+ const best = Object.entries(scores).sort((a, b) => b[1] - a[1])[0];
100
+ return best[0];
101
+ }
102
+
103
+ /**
104
+ * ✅ NEW: Detect which row contains the header
105
+ * Looks for first row with mostly string values
106
+ */
107
+ private _detectHeaderRow(text: string, delimiter: string, maxCheck: number = 10): number {
108
+ const lines = this._splitLines(text).slice(0, maxCheck).filter(l => l.trim());
109
+
110
+ for (let i = 0; i < lines.length; i++) {
111
+ const values = this._parseLine(lines[i], delimiter);
112
+
113
+ // Skip if mostly empty
114
+ const nonEmpty = values.filter(v => v.trim()).length;
115
+ if (nonEmpty < values.length * 0.5) continue;
116
+
117
+ // Check if mostly non-numeric (likely headers)
118
+ const nonNumeric = values.filter(v => {
119
+ const trimmed = v.trim();
120
+ return isNaN(Number(trimmed)) && trimmed !== '';
121
+ }).length;
122
+
123
+ if (nonNumeric >= values.length * 0.7) {
124
+ return i;
125
+ }
126
+ }
127
+
128
+ return 0; // Fallback to first row
129
+ }
130
+
65
131
  /**
66
132
  * Parse a CSV/TSV string into a DataFrame
67
133
  */
68
134
  parseCSV(text: string, options: ParseOptions = {}): DataFrame {
69
135
  const {
70
- delimiter = ',',
136
+ delimiter: userDelimiter,
71
137
  hasHeader = true,
72
138
  maxRows,
73
- skipRows = 0,
74
- columns: selectCols
139
+ skipRows: userSkipRows = 0,
140
+ columns: selectCols,
141
+ headerRow: userHeaderRow,
142
+ autoDetectDelimiter = true
75
143
  } = options;
76
144
 
145
+ // ✅ Auto-detect delimiter if not provided
146
+ const delimiter = userDelimiter || (autoDetectDelimiter ? this._detectDelimiter(text) : ',');
147
+
148
+ // ✅ Auto-detect header row if not provided
149
+ const headerRow = userHeaderRow !== undefined ? userHeaderRow : (hasHeader ? this._detectHeaderRow(text, delimiter) : -1);
150
+
77
151
  const lines = this._splitLines(text);
78
- let startIdx = skipRows;
79
152
  let headers: string[];
80
153
 
81
- if (hasHeader && lines.length > startIdx) {
82
- headers = this._parseLine(lines[startIdx], delimiter);
83
- startIdx++;
154
+ // Skip rows before header
155
+ let startIdx = userSkipRows;
156
+
157
+ if (hasHeader && headerRow >= 0) {
158
+ const headerLine = lines[headerRow + userSkipRows];
159
+ if (headerLine) {
160
+ headers = this._parseLine(headerLine, delimiter);
161
+ startIdx = headerRow + userSkipRows + 1;
162
+ } else {
163
+ const firstLine = this._parseLine(lines[startIdx] || '', delimiter);
164
+ headers = firstLine.map((_, i) => `col_${i}`);
165
+ }
84
166
  } else {
85
167
  const firstLine = this._parseLine(lines[startIdx] || '', delimiter);
86
168
  headers = firstLine.map((_, i) => `col_${i}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "juxscript",
3
- "version": "1.1.181",
3
+ "version": "1.1.183",
4
4
  "type": "module",
5
5
  "description": "A JavaScript UX authorship platform",
6
6
  "main": "index.js",