@innertia-solutions/innertia-nuxt 0.1.49 → 0.1.51

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.
@@ -60,10 +60,13 @@ const viewerType = computed(() => {
60
60
  if (mime === 'text/markdown' || ['md', 'markdown'].includes(ext)) return 'markdown'
61
61
  if (mime === 'text/csv' || ['csv', 'tsv'].includes(ext)) return 'csv'
62
62
 
63
- if (mime.startsWith('text/') || ['log','yml','yaml','env','sh','js','ts','tsx','jsx','css','html','xml','php','py','rb','go','rs','java','sql','toml','ini'].includes(ext)) return 'text'
63
+ // Office por EXTENSIÓN antes del check de text/*: un .xls/.xlsx/.doc/.docx
64
+ // suele llegar con mime mal detectado (p.ej. text/plain u octet-stream) y aun
65
+ // así debe usar el renderer rico (Excel con pestañas / docx), no el de texto.
66
+ if (['xlsx', 'xls'].includes(ext) || mime.includes('spreadsheet') || mime.includes('excel')) return 'xlsx'
67
+ if (['docx', 'doc'].includes(ext) || mime.includes('wordprocessing') || mime.includes('msword')) return 'docx'
64
68
 
65
- if (['xlsx','xls'].includes(ext) || mime.includes('spreadsheet') || mime.includes('excel')) return 'xlsx'
66
- if (['docx','doc'].includes(ext) || mime.includes('wordprocessing') || mime.includes('msword')) return 'docx'
69
+ if (mime.startsWith('text/') || ['log','yml','yaml','env','sh','js','ts','tsx','jsx','css','html','xml','php','py','rb','go','rs','java','sql','toml','ini'].includes(ext)) return 'text'
67
70
 
68
71
  return 'unknown'
69
72
  })
@@ -271,30 +274,88 @@ function buildSheet(ws, workbook) {
271
274
  return { name: ws.name, cols, rows, firstRow: 1, imageMap }
272
275
  }
273
276
 
277
+ /** Detecta el delimitador más probable de un texto tabular (tab / ; / ,). */
278
+ function detectDelimiter(text) {
279
+ const line = text.split(/\r?\n/).find(l => l.trim()) ?? ''
280
+ const counts = { '\t': (line.match(/\t/g) || []).length, ';': (line.match(/;/g) || []).length, ',': (line.match(/,/g) || []).length }
281
+ const max = Math.max(counts['\t'], counts[';'], counts[','])
282
+ if (max === 0) return null
283
+ return counts['\t'] === max ? '\t' : (counts[';'] === max ? ';' : ',')
284
+ }
285
+
286
+ /** Parser genérico de texto delimitado con soporte de comillas. */
287
+ function parseDelimited(text, delim) {
288
+ const rows = []
289
+ let row = [], cell = '', inQ = false
290
+ for (let i = 0; i < text.length; i++) {
291
+ const c = text[i]
292
+ if (inQ) {
293
+ if (c === '"') { if (text[i + 1] === '"') { cell += '"'; i++ } else inQ = false }
294
+ else cell += c
295
+ } else {
296
+ if (c === '"') inQ = true
297
+ else if (c === delim) { row.push(cell); cell = '' }
298
+ else if (c === '\n') { row.push(cell); rows.push(row); row = []; cell = '' }
299
+ else if (c === '\r') continue
300
+ else cell += c
301
+ }
302
+ }
303
+ if (cell.length || row.length) { row.push(cell); rows.push(row) }
304
+ return rows
305
+ }
306
+
307
+ /** Construye una sheet (misma forma que buildSheet) desde texto delimitado. */
308
+ function buildSheetFromDelimited(text, name = 'Hoja 1') {
309
+ const delim = detectDelimiter(text) ?? '\t'
310
+ const raw = parseDelimited(text, delim).filter(r => r.length && !(r.length === 1 && r[0] === ''))
311
+ const colCount = raw.reduce((m, r) => Math.max(m, r.length), 0)
312
+ const cols = []
313
+ for (let c = 0; c < colCount; c++) cols.push(colLetter(c))
314
+ const rows = raw.map((r, ri) => {
315
+ const rowData = []
316
+ for (let c = 0; c < colCount; c++) {
317
+ rowData.push({ v: r[c] ?? '', align: 'left', t: 'string', address: colLetter(c) + (ri + 1) })
318
+ }
319
+ return rowData
320
+ })
321
+ return { name, cols, rows, firstRow: 1, imageMap: {} }
322
+ }
323
+
274
324
  const loadXlsx = async () => {
275
325
  if (viewerType.value !== 'xlsx') return
276
326
  isLoadingXlsx.value = true
277
327
  xlsxError.value = null
278
328
  xlsxActiveIdx.value = 0
329
+ let buf = null
279
330
  try {
331
+ const res = await fetch(src.value)
332
+ if (!res.ok) throw new Error(`HTTP ${res.status}`)
333
+ buf = await res.arrayBuffer()
334
+
280
335
  const mod = await import('exceljs').catch(() => null)
281
336
  const ExcelJS = pickModule(mod, 'Workbook')
282
337
  ?? (mod?.default?.Workbook ? mod.default : null)
283
338
  ?? mod
284
- if (!ExcelJS?.Workbook) {
285
- console.warn('[FilesPreview] exceljs module shape:', mod)
286
- throw new Error('Para previsualizar .xlsx instalá la dep: pnpm add exceljs')
287
- }
288
- const res = await fetch(src.value)
289
- if (!res.ok) throw new Error(`HTTP ${res.status}`)
290
- const buf = await res.arrayBuffer()
339
+ if (!ExcelJS?.Workbook) throw new Error('exceljs no disponible')
340
+
291
341
  const wb = new ExcelJS.Workbook()
292
342
  await wb.xlsx.load(buf)
293
343
  const sheets = wb.worksheets.map(ws => buildSheet(ws, wb))
294
344
  xlsxWorkbook.value = { sheets }
295
345
  } catch (e) {
296
- console.error('[FilesPreview] xlsx error', e)
297
- xlsxError.value = e.message
346
+ // Fallback: el archivo no es un .xlsx OOXML válido (típico: un CSV/TSV exportado
347
+ // con extensión .xls, o mime mal detectado). Lo renderizamos como tabla delimitada.
348
+ try {
349
+ const text = new TextDecoder('utf-8').decode(buf ?? new ArrayBuffer(0))
350
+ if (text && text.trim()) {
351
+ xlsxWorkbook.value = { sheets: [buildSheetFromDelimited(text)] }
352
+ } else {
353
+ throw e
354
+ }
355
+ } catch (_) {
356
+ console.error('[FilesPreview] xlsx error', e)
357
+ xlsxError.value = 'No se pudo previsualizar la hoja de cálculo.'
358
+ }
298
359
  } finally {
299
360
  isLoadingXlsx.value = false
300
361
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@innertia-solutions/innertia-nuxt",
3
- "version": "0.1.49",
3
+ "version": "0.1.51",
4
4
  "description": "Innertia Solutions — Nuxt layer unificada: core, auth, multitenancy, theme y app contexts",
5
5
  "keywords": [
6
6
  "nuxt",