@bendyline/squisq-formats 2.4.5 → 2.5.0

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.
@@ -0,0 +1,1197 @@
1
+ import {
2
+ extractPlainText,
3
+ inlineToPlainText
4
+ } from "./chunk-AVOZAKGP.js";
5
+ import {
6
+ createPackage
7
+ } from "./chunk-ILCJ3WFD.js";
8
+ import {
9
+ escapeXml,
10
+ xmlDeclaration
11
+ } from "./chunk-JU2RHXUB.js";
12
+ import {
13
+ CONTENT_TYPE_XLSX_STYLES,
14
+ CONTENT_TYPE_XLSX_WORKBOOK,
15
+ CONTENT_TYPE_XLSX_WORKSHEET,
16
+ NS_R,
17
+ NS_SML,
18
+ REL_OFFICE_DOCUMENT,
19
+ REL_STYLES,
20
+ REL_WORKSHEET,
21
+ baseDirOf,
22
+ getPartRelationships,
23
+ getPartXml,
24
+ openPackage,
25
+ requireMainPartPath
26
+ } from "./chunk-S5PCVMKU.js";
27
+
28
+ // src/xlsx/index.ts
29
+ import { markdownToDoc } from "@bendyline/squisq/doc";
30
+
31
+ // src/xlsx/cells.ts
32
+ var MAX_COL_INDEX = 16383;
33
+ var MAX_ROW_INDEX = 1048575;
34
+ var EMPTY_CELL = Object.freeze({ text: "", kind: "empty" });
35
+ function isOccupied(cell) {
36
+ return cell.text !== "" || cell.formula !== void 0;
37
+ }
38
+ function columnLetter(index) {
39
+ let n = index + 1;
40
+ let letters = "";
41
+ while (n > 0) {
42
+ const rem = (n - 1) % 26;
43
+ letters = String.fromCharCode(65 + rem) + letters;
44
+ n = Math.floor((n - 1) / 26);
45
+ }
46
+ return letters;
47
+ }
48
+ function columnIndexFromLetters(letters) {
49
+ let n = 0;
50
+ for (const ch of letters.toUpperCase()) n = n * 26 + (ch.charCodeAt(0) - 64);
51
+ return n - 1;
52
+ }
53
+ function colIndex(ref) {
54
+ const m = /^\$?([A-Za-z]+)/.exec(ref);
55
+ if (!m) return 0;
56
+ return columnIndexFromLetters(m[1]);
57
+ }
58
+ function parseCellRef(ref) {
59
+ const m = /^\$?([A-Za-z]{1,3})\$?([0-9]{1,7})$/.exec(ref.trim());
60
+ if (!m) return null;
61
+ const col = columnIndexFromLetters(m[1]);
62
+ const row = Number.parseInt(m[2], 10) - 1;
63
+ if (!Number.isFinite(row) || row < 0 || row > MAX_ROW_INDEX) return null;
64
+ if (col < 0 || col > MAX_COL_INDEX) return null;
65
+ return { row, col };
66
+ }
67
+ function formatCellRef(row, col) {
68
+ return `${columnLetter(col)}${row + 1}`;
69
+ }
70
+ function parseRangeRef(ref) {
71
+ const parts = ref.split(":");
72
+ if (parts.length > 2) return null;
73
+ const start = parseCellRef(parts[0] ?? "");
74
+ if (!start) return null;
75
+ const end = parts.length === 2 ? parseCellRef(parts[1]) : start;
76
+ if (!end) return null;
77
+ return {
78
+ top: Math.min(start.row, end.row),
79
+ left: Math.min(start.col, end.col),
80
+ bottom: Math.max(start.row, end.row),
81
+ right: Math.max(start.col, end.col)
82
+ };
83
+ }
84
+ var REF_AT = /(\$?)([A-Za-z]{1,3})(\$?)([0-9]{1,7})/y;
85
+ var IDENT_CHAR = /[A-Za-z0-9_.]/;
86
+ function translateFormula(text, dRow, dCol) {
87
+ if (dRow === 0 && dCol === 0) return text;
88
+ let out = "";
89
+ let i = 0;
90
+ while (i < text.length) {
91
+ const ch = text[i];
92
+ if (ch === '"') {
93
+ const start = i;
94
+ i++;
95
+ while (i < text.length) {
96
+ if (text[i] === '"') {
97
+ if (text[i + 1] === '"') i += 2;
98
+ else {
99
+ i++;
100
+ break;
101
+ }
102
+ } else i++;
103
+ }
104
+ out += text.slice(start, i);
105
+ continue;
106
+ }
107
+ if (ch === "'") {
108
+ const start = i;
109
+ i++;
110
+ while (i < text.length) {
111
+ if (text[i] === "'") {
112
+ if (text[i + 1] === "'") i += 2;
113
+ else {
114
+ i++;
115
+ break;
116
+ }
117
+ } else i++;
118
+ }
119
+ out += text.slice(start, i);
120
+ continue;
121
+ }
122
+ if (ch === "[") {
123
+ const start = i;
124
+ let depth = 0;
125
+ while (i < text.length) {
126
+ if (text[i] === "[") depth++;
127
+ else if (text[i] === "]") {
128
+ depth--;
129
+ if (depth === 0) {
130
+ i++;
131
+ break;
132
+ }
133
+ }
134
+ i++;
135
+ }
136
+ out += text.slice(start, i);
137
+ continue;
138
+ }
139
+ REF_AT.lastIndex = i;
140
+ const m = REF_AT.exec(text);
141
+ if (m) {
142
+ const before = i > 0 ? text[i - 1] : "";
143
+ const after = text[i + m[0].length] ?? "";
144
+ const col = columnIndexFromLetters(m[2]);
145
+ const row = Number.parseInt(m[4], 10) - 1;
146
+ const isRef = !IDENT_CHAR.test(before) && !IDENT_CHAR.test(after) && after !== "(" && col <= MAX_COL_INDEX && row >= 0 && row <= MAX_ROW_INDEX;
147
+ if (isRef) {
148
+ const nextCol = m[1] === "$" ? col : col + dCol;
149
+ const nextRow = m[3] === "$" ? row : row + dRow;
150
+ if (nextCol < 0 || nextCol > MAX_COL_INDEX || nextRow < 0 || nextRow > MAX_ROW_INDEX) {
151
+ out += "#REF!";
152
+ } else {
153
+ out += `${m[1]}${columnLetter(nextCol)}${m[3]}${nextRow + 1}`;
154
+ }
155
+ i += m[0].length;
156
+ continue;
157
+ }
158
+ }
159
+ out += ch;
160
+ i++;
161
+ }
162
+ return out;
163
+ }
164
+
165
+ // src/xlsx/regions.ts
166
+ var DEFAULT_MAX_REGIONS = 64;
167
+ var DEFAULT_MAX_CANDIDATES = 2e3;
168
+ var DEFAULT_MIN_REGION_CELLS = 2;
169
+ var DEFAULT_MAX_MASK_CELLS = 4e6;
170
+ var MAX_MERGE_PASSES = 8;
171
+ function throwIfAborted(signal) {
172
+ signal?.throwIfAborted();
173
+ }
174
+ function intersects(a, b) {
175
+ return a.left <= b.right && b.left <= a.right && a.top <= b.bottom && b.top <= a.bottom;
176
+ }
177
+ function union(a, b) {
178
+ return {
179
+ top: Math.min(a.top, b.top),
180
+ left: Math.min(a.left, b.left),
181
+ bottom: Math.max(a.bottom, b.bottom),
182
+ right: Math.max(a.right, b.right)
183
+ };
184
+ }
185
+ function area(rect) {
186
+ return (rect.bottom - rect.top + 1) * (rect.right - rect.left + 1);
187
+ }
188
+ function sliceRect(grid, rect, empty) {
189
+ const out = [];
190
+ for (let r = rect.top; r <= rect.bottom; r++) {
191
+ const row = grid[r] ?? [];
192
+ const cells = [];
193
+ for (let c = rect.left; c <= rect.right; c++) cells.push(row[c] ?? empty);
194
+ out.push(cells);
195
+ }
196
+ return out;
197
+ }
198
+ function detectRegions(grid, merges = [], options = {}) {
199
+ const signal = options.signal;
200
+ const maxRegions = options.maxRegionsPerSheet ?? DEFAULT_MAX_REGIONS;
201
+ const maxCandidates = options.maxRegionCandidates ?? DEFAULT_MAX_CANDIDATES;
202
+ const minCells = options.minRegionCells ?? DEFAULT_MIN_REGION_CELLS;
203
+ const maxMaskCells = options.maxMaskCells ?? DEFAULT_MAX_MASK_CELLS;
204
+ const warnings = [];
205
+ const rows = grid.length;
206
+ let cols = 0;
207
+ for (const row of grid) cols = Math.max(cols, row.length);
208
+ if (rows === 0 || cols === 0) {
209
+ return { regions: [], strays: [], warnings, degraded: false };
210
+ }
211
+ if (rows * cols > maxMaskCells) {
212
+ warnings.push(
213
+ `Used range spans ${rows}\xD7${cols} cells, past the ${maxMaskCells}-cell detection limit; imported as a single grid instead.`
214
+ );
215
+ return { regions: [], strays: [], warnings, degraded: true };
216
+ }
217
+ const mask = new Uint8Array(rows * cols);
218
+ for (let r = 0; r < rows; r++) {
219
+ throwIfAborted(signal);
220
+ const row = grid[r];
221
+ for (let c = 0; c < row.length; c++) {
222
+ if (isOccupied(row[c])) mask[r * cols + c] = 1;
223
+ }
224
+ }
225
+ for (const merge of merges) {
226
+ const anchor = grid[merge.top]?.[merge.left];
227
+ if (!anchor || !isOccupied(anchor)) continue;
228
+ const bottom = Math.min(merge.bottom, rows - 1);
229
+ const right = Math.min(merge.right, cols - 1);
230
+ for (let r = Math.max(0, merge.top); r <= bottom; r++) {
231
+ for (let c = Math.max(0, merge.left); c <= right; c++) mask[r * cols + c] = 1;
232
+ }
233
+ }
234
+ const seen = new Uint8Array(rows * cols);
235
+ let rects = [];
236
+ const stack = [];
237
+ for (let r0 = 0; r0 < rows; r0++) {
238
+ throwIfAborted(signal);
239
+ for (let c0 = 0; c0 < cols; c0++) {
240
+ const start = r0 * cols + c0;
241
+ if (mask[start] === 0 || seen[start] === 1) continue;
242
+ if (rects.length >= maxCandidates) {
243
+ warnings.push(
244
+ `Sheet has more than ${maxCandidates} data islands; imported as a single grid instead.`
245
+ );
246
+ return { regions: [], strays: [], warnings, degraded: true };
247
+ }
248
+ seen[start] = 1;
249
+ stack.length = 0;
250
+ stack.push(start);
251
+ let top = r0;
252
+ let bottom = r0;
253
+ let left = c0;
254
+ let right = c0;
255
+ while (stack.length > 0) {
256
+ const idx = stack.pop();
257
+ const r = idx / cols | 0;
258
+ const c = idx - r * cols;
259
+ if (r < top) top = r;
260
+ if (r > bottom) bottom = r;
261
+ if (c < left) left = c;
262
+ if (c > right) right = c;
263
+ for (let dr = -1; dr <= 1; dr++) {
264
+ const nr = r + dr;
265
+ if (nr < 0 || nr >= rows) continue;
266
+ for (let dc = -1; dc <= 1; dc++) {
267
+ const nc = c + dc;
268
+ if (nc < 0 || nc >= cols) continue;
269
+ const nIdx = nr * cols + nc;
270
+ if (mask[nIdx] === 0 || seen[nIdx] === 1) continue;
271
+ seen[nIdx] = 1;
272
+ stack.push(nIdx);
273
+ }
274
+ }
275
+ }
276
+ rects.push({ top, left, bottom, right });
277
+ }
278
+ }
279
+ for (let pass = 0; pass < MAX_MERGE_PASSES; pass++) {
280
+ throwIfAborted(signal);
281
+ const out = [];
282
+ let changed = false;
283
+ for (const rect of rects) {
284
+ let current = rect;
285
+ let hit = -1;
286
+ for (let i = 0; i < out.length; i++) {
287
+ if (!intersects(out[i], current)) continue;
288
+ current = union(out[i], current);
289
+ if (hit < 0) {
290
+ hit = i;
291
+ out[i] = current;
292
+ } else {
293
+ out[hit] = current;
294
+ out.splice(i, 1);
295
+ i--;
296
+ }
297
+ changed = true;
298
+ }
299
+ if (hit < 0) out.push(current);
300
+ }
301
+ rects = out;
302
+ if (!changed) break;
303
+ }
304
+ rects.sort((a, b) => a.top - b.top || a.left - b.left);
305
+ const titles = /* @__PURE__ */ new Map();
306
+ const absorbed = /* @__PURE__ */ new Set();
307
+ for (let i = 0; i < rects.length; i++) {
308
+ const body = rects[i];
309
+ if (absorbed.has(i) || area(body) < minCells) continue;
310
+ for (let j = 0; j < rects.length; j++) {
311
+ if (j === i || absorbed.has(j) || titles.has(i)) continue;
312
+ const cap = rects[j];
313
+ if (cap.top !== cap.bottom) continue;
314
+ if (cap.bottom >= body.top) continue;
315
+ if (body.top - cap.bottom > 2) continue;
316
+ if (cap.left < body.left || cap.right > body.right) continue;
317
+ const sole = soleCell(grid, cap);
318
+ if (sole === null) continue;
319
+ titles.set(i, sole);
320
+ absorbed.add(j);
321
+ }
322
+ }
323
+ const regions = [];
324
+ const strayRects = [];
325
+ for (let i = 0; i < rects.length; i++) {
326
+ if (absorbed.has(i)) continue;
327
+ let rect = rects[i];
328
+ if (area(rect) < minCells) {
329
+ strayRects.push(rect);
330
+ continue;
331
+ }
332
+ if (regions.length >= maxRegions) {
333
+ strayRects.push(rect);
334
+ continue;
335
+ }
336
+ let caption = titles.get(i);
337
+ if (caption === void 0) {
338
+ const peeled = peelCaptionRow(grid, rect);
339
+ if (peeled) {
340
+ caption = peeled.caption;
341
+ rect = peeled.rect;
342
+ }
343
+ }
344
+ regions.push(
345
+ caption === void 0 ? { rect } : { rect, title: caption.text, titleCell: { row: caption.row, col: caption.col } }
346
+ );
347
+ }
348
+ const overflow = rects.filter((r, i) => !absorbed.has(i) && area(r) >= minCells).length;
349
+ if (overflow > maxRegions) {
350
+ warnings.push(
351
+ `Sheet has ${overflow} data islands; the first ${maxRegions} became tables and the rest were folded into the loose-cells table.`
352
+ );
353
+ }
354
+ const strays = [];
355
+ for (const rect of strayRects) {
356
+ for (let r = rect.top; r <= rect.bottom; r++) {
357
+ for (let c = rect.left; c <= rect.right; c++) {
358
+ const cell = grid[r]?.[c];
359
+ if (cell && isOccupied(cell)) strays.push({ row: r, col: c, cell });
360
+ }
361
+ }
362
+ }
363
+ strays.sort((a, b) => a.row - b.row || a.col - b.col);
364
+ return { regions, strays, warnings, degraded: false };
365
+ }
366
+ function soleCell(grid, rect) {
367
+ let found = null;
368
+ for (let r = rect.top; r <= rect.bottom; r++) {
369
+ for (let c = rect.left; c <= rect.right; c++) {
370
+ const cell = grid[r]?.[c];
371
+ if (!cell || cell.text === "") continue;
372
+ if (found !== null) return null;
373
+ found = { text: cell.text, row: r, col: c };
374
+ }
375
+ }
376
+ return found;
377
+ }
378
+ function peelCaptionRow(grid, rect) {
379
+ if (rect.bottom - rect.top < 2) return null;
380
+ const caption = soleCell(grid, { ...rect, bottom: rect.top });
381
+ if (caption === null) return null;
382
+ let below = 0;
383
+ for (let c = rect.left; c <= rect.right; c++) {
384
+ if ((grid[rect.top + 1]?.[c]?.text ?? "") !== "") below++;
385
+ }
386
+ if (below < 2) return null;
387
+ return { caption, rect: { ...rect, top: rect.top + 1 } };
388
+ }
389
+
390
+ // src/xlsx/import.ts
391
+ var XLSX_MAIN_PART = "xl/workbook.xml";
392
+ function attrNS(el, ns, local, fallback) {
393
+ return el.getAttributeNS(ns, local) ?? el.getAttribute(fallback);
394
+ }
395
+ function resolveTarget(baseDir, target) {
396
+ if (target.startsWith("/")) return target.replace(/^\//, "");
397
+ const stack = baseDir ? baseDir.split("/") : [];
398
+ for (const seg of target.split("/")) {
399
+ if (seg === "..") stack.pop();
400
+ else if (seg !== ".") stack.push(seg);
401
+ }
402
+ return stack.join("/");
403
+ }
404
+ async function readWorkbook(pkg, mainPart) {
405
+ const wb = await getPartXml(pkg, mainPart);
406
+ if (!wb) {
407
+ throw new Error(`Invalid XLSX package: workbook part "${mainPart}" could not be parsed.`);
408
+ }
409
+ const rels = await getPartRelationships(pkg, mainPart);
410
+ const relById = new Map(rels.map((r) => [r.id, r.target]));
411
+ const out = [];
412
+ const sheetEls = wb.getElementsByTagNameNS(NS_SML, "sheet");
413
+ for (let i = 0; i < sheetEls.length; i++) {
414
+ const el = sheetEls[i];
415
+ const name = el.getAttribute("name") ?? `Sheet${i + 1}`;
416
+ const rid = attrNS(el, NS_R, "id", "r:id");
417
+ const target = rid ? relById.get(rid) : void 0;
418
+ if (target) out.push({ name, path: resolveTarget(baseDirOf(mainPart), target) });
419
+ }
420
+ const workbookPr = wb.getElementsByTagNameNS(NS_SML, "workbookPr")[0];
421
+ const date1904Value = workbookPr?.getAttribute("date1904");
422
+ return {
423
+ sheets: out,
424
+ date1904: date1904Value === "1" || date1904Value === "true"
425
+ };
426
+ }
427
+ function isPhonetic(el, root) {
428
+ for (let node = el.parentNode; node && node !== root; node = node.parentNode) {
429
+ if (node.nodeType !== 1) continue;
430
+ const parent = node;
431
+ if (parent.localName === "rPh" && parent.namespaceURI === NS_SML) return true;
432
+ }
433
+ return false;
434
+ }
435
+ function runVertAlign(run) {
436
+ const rPr = run.getElementsByTagNameNS(NS_SML, "rPr")[0];
437
+ if (!rPr) return null;
438
+ const va = rPr.getElementsByTagNameNS(NS_SML, "vertAlign")[0];
439
+ const val = va?.getAttribute("val");
440
+ return val === "superscript" || val === "subscript" ? val : null;
441
+ }
442
+ function readStringItem(root) {
443
+ const tEls = root.getElementsByTagNameNS(NS_SML, "t");
444
+ const rich = [];
445
+ let text = "";
446
+ let formatted = false;
447
+ for (let i = 0; i < tEls.length; i++) {
448
+ const t = tEls[i];
449
+ if (isPhonetic(t, root)) continue;
450
+ const value = t.textContent ?? "";
451
+ if (value === "") continue;
452
+ text += value;
453
+ const parent = t.parentNode;
454
+ const run = parent && parent.localName === "r" ? parent : null;
455
+ const vertAlign = run ? runVertAlign(run) : null;
456
+ if (vertAlign) {
457
+ formatted = true;
458
+ rich.push({ type: vertAlign, children: [{ type: "text", value }] });
459
+ } else {
460
+ rich.push({ type: "text", value });
461
+ }
462
+ }
463
+ return formatted ? { text, rich: mergeAdjacentText(rich) } : { text };
464
+ }
465
+ function mergeAdjacentText(nodes) {
466
+ const out = [];
467
+ for (const node of nodes) {
468
+ const prev = out[out.length - 1];
469
+ if (node.type === "text" && prev?.type === "text") prev.value += node.value;
470
+ else out.push(node);
471
+ }
472
+ return out;
473
+ }
474
+ async function readSharedStrings(pkg) {
475
+ const doc = await getPartXml(pkg, "xl/sharedStrings.xml");
476
+ if (!doc) return [];
477
+ const siEls = doc.getElementsByTagNameNS(NS_SML, "si");
478
+ const out = [];
479
+ for (let i = 0; i < siEls.length; i++) out.push(readStringItem(siEls[i]));
480
+ return out;
481
+ }
482
+ var BUILTIN_NUMBER_FORMATS = Object.freeze({
483
+ 9: "0%",
484
+ 10: "0.00%",
485
+ 14: "mm-dd-yy",
486
+ 15: "d-mmm-yy",
487
+ 16: "d-mmm",
488
+ 17: "mmm-yy",
489
+ 18: "h:mm AM/PM",
490
+ 19: "h:mm:ss AM/PM",
491
+ 20: "h:mm",
492
+ 21: "h:mm:ss",
493
+ 22: "m/d/yy h:mm",
494
+ 45: "mm:ss",
495
+ 46: "[h]:mm:ss",
496
+ 47: "mmss.0"
497
+ });
498
+ async function readCellStyles(pkg) {
499
+ const doc = await getPartXml(pkg, "xl/styles.xml");
500
+ if (!doc) return [];
501
+ const custom = /* @__PURE__ */ new Map();
502
+ const numFmtEls = doc.getElementsByTagNameNS(NS_SML, "numFmt");
503
+ for (let i = 0; i < numFmtEls.length; i++) {
504
+ const el = numFmtEls[i];
505
+ const id = Number.parseInt(el.getAttribute("numFmtId") ?? "", 10);
506
+ const code = el.getAttribute("formatCode");
507
+ if (Number.isFinite(id) && code) custom.set(id, code);
508
+ }
509
+ const cellXfs = doc.getElementsByTagNameNS(NS_SML, "cellXfs")[0];
510
+ if (!cellXfs) return [];
511
+ const xfEls = cellXfs.getElementsByTagNameNS(NS_SML, "xf");
512
+ const out = [];
513
+ for (let i = 0; i < xfEls.length; i++) {
514
+ const numFmtId = Number.parseInt(xfEls[i].getAttribute("numFmtId") ?? "0", 10);
515
+ out.push({
516
+ numFmtId,
517
+ formatCode: custom.get(numFmtId) ?? BUILTIN_NUMBER_FORMATS[numFmtId] ?? "General"
518
+ });
519
+ }
520
+ return out;
521
+ }
522
+ function normalizeFormatCode(formatCode) {
523
+ return formatCode.split(";", 1)[0].replace(/"(?:[^"]|"")*"/g, "").replace(/\\./g, "").replace(/[_*]./g, "").replace(/\[(?!h+\]|m+\]|s+\])[^\]]*\]/gi, "").toLowerCase();
524
+ }
525
+ function numberFormatKind(formatCode) {
526
+ const normalized = normalizeFormatCode(formatCode);
527
+ if (normalized === "general") return "general";
528
+ if (normalized.includes("%")) return "percent";
529
+ if (/^0+$/.test(normalized)) return "zero-pad";
530
+ const hasDate = /[yd]/.test(normalized);
531
+ const hasTime = /[hs]|\[[hms]+\]/.test(normalized);
532
+ if (hasDate && hasTime) return "datetime";
533
+ if (hasDate) return "date";
534
+ if (hasTime) return "time";
535
+ return "general";
536
+ }
537
+ function twoDigits(value) {
538
+ return String(value).padStart(2, "0");
539
+ }
540
+ function excelDateText(serial, date1904) {
541
+ const wholeDays = Math.floor(serial);
542
+ if (!date1904 && wholeDays === 60) return "1900-02-29";
543
+ const epoch = date1904 ? Date.UTC(1904, 0, 1) : Date.UTC(1899, 11, 31);
544
+ const adjustedDays = !date1904 && wholeDays > 60 ? wholeDays - 1 : wholeDays;
545
+ const date = new Date(epoch + adjustedDays * 864e5);
546
+ if (!Number.isFinite(date.getTime())) return null;
547
+ return `${date.getUTCFullYear()}-${twoDigits(date.getUTCMonth() + 1)}-${twoDigits(date.getUTCDate())}`;
548
+ }
549
+ function excelTimeText(serial, includeSeconds, elapsedHours) {
550
+ const totalSeconds = Math.round((serial - Math.floor(serial)) * 86400);
551
+ const hours = elapsedHours ? Math.floor(serial * 24) : Math.floor(totalSeconds / 3600) % 24;
552
+ const minutes = Math.floor(totalSeconds / 60) % 60;
553
+ const seconds = totalSeconds % 60;
554
+ return `${twoDigits(hours)}:${twoDigits(minutes)}${includeSeconds ? `:${twoDigits(seconds)}` : ""}`;
555
+ }
556
+ function formattedNumberText(raw, style, date1904) {
557
+ if (!style) return raw;
558
+ const value = Number(raw);
559
+ if (!Number.isFinite(value)) return raw;
560
+ const normalized = normalizeFormatCode(style.formatCode);
561
+ const kind = numberFormatKind(style.formatCode);
562
+ if (kind === "date") return excelDateText(value, date1904) ?? raw;
563
+ if (kind === "time") {
564
+ return excelTimeText(value, /s/.test(normalized), /\[[h]+\]/.test(normalized));
565
+ }
566
+ if (kind === "datetime") {
567
+ const date = excelDateText(value, date1904);
568
+ return date ? `${date} ${excelTimeText(value, /s/.test(normalized), false)}` : raw;
569
+ }
570
+ if (kind === "percent") {
571
+ const decimals = /0\.(0+)%/.exec(normalized)?.[1]?.length ?? 0;
572
+ return `${(value * 100).toFixed(decimals)}%`;
573
+ }
574
+ if (kind === "zero-pad" && Number.isInteger(value)) {
575
+ const width = normalized.length;
576
+ const sign = value < 0 ? "-" : "";
577
+ return `${sign}${String(Math.abs(value)).padStart(width, "0")}`;
578
+ }
579
+ return raw;
580
+ }
581
+ function readFormula(cell, row, col, ctx) {
582
+ const fEls = cell.getElementsByTagNameNS(NS_SML, "f");
583
+ const f = fEls.length ? fEls[0] : null;
584
+ if (!f) return "";
585
+ const text = (f.textContent ?? "").trim();
586
+ if (f.getAttribute("t") !== "shared") return text;
587
+ const si = f.getAttribute("si");
588
+ if (si === null) return text;
589
+ if (text !== "") {
590
+ ctx.sharedFormulas.set(si, { text, row, col });
591
+ return text;
592
+ }
593
+ const master = ctx.sharedFormulas.get(si);
594
+ if (!master) return "";
595
+ try {
596
+ return translateFormula(master.text, row - master.row, col - master.col);
597
+ } catch {
598
+ return "";
599
+ }
600
+ }
601
+ function readCell(cell, row, col, ctx) {
602
+ const formula = readFormula(cell, row, col, ctx);
603
+ const withFormula = (text2, kind2) => {
604
+ const out = { text: text2, kind: text2 === "" ? "empty" : kind2 };
605
+ if (formula !== "") out.formula = formula;
606
+ return out;
607
+ };
608
+ const stringCell = (item) => {
609
+ const out = withFormula(item.text, "string");
610
+ if (item.rich && out.kind !== "empty") out.richText = item.rich;
611
+ return out;
612
+ };
613
+ const t = cell.getAttribute("t");
614
+ if (t === "inlineStr") {
615
+ const is = cell.getElementsByTagNameNS(NS_SML, "is")[0];
616
+ return stringCell(is ? readStringItem(is) : { text: "" });
617
+ }
618
+ const vEls = cell.getElementsByTagNameNS(NS_SML, "v");
619
+ const v = vEls.length ? vEls[0].textContent ?? "" : "";
620
+ if (v === "") return withFormula("", "empty");
621
+ if (t === "s") return stringCell(ctx.shared[Number.parseInt(v, 10)] ?? { text: "" });
622
+ if (t === "b") return withFormula(v === "1" ? "TRUE" : "FALSE", "bool");
623
+ if (t === "e") return withFormula(v, "error");
624
+ if (t === "str") return withFormula(v, "string");
625
+ const style = ctx.styles[Number.parseInt(cell.getAttribute("s") ?? "0", 10)];
626
+ const text = formattedNumberText(v, style, ctx.date1904);
627
+ const dateLike = style ? numberFormatKind(style.formatCode) : "general";
628
+ const kind = dateLike === "date" || dateLike === "time" || dateLike === "datetime" ? "date" : "number";
629
+ return withFormula(text, kind);
630
+ }
631
+ async function sheetToCells(pkg, path, shared, styles, date1904) {
632
+ const doc = await getPartXml(pkg, path);
633
+ if (!doc) return { cells: [], merges: [] };
634
+ const ctx = { shared, styles, date1904, sharedFormulas: /* @__PURE__ */ new Map() };
635
+ const rowEls = doc.getElementsByTagNameNS(NS_SML, "row");
636
+ const byIndex = /* @__PURE__ */ new Map();
637
+ let maxContentIdx = -1;
638
+ let fallbackIdx = 0;
639
+ for (let r = 0; r < rowEls.length; r++) {
640
+ const rowEl = rowEls[r];
641
+ const rowRef = Number.parseInt(rowEl.getAttribute("r") ?? "", 10);
642
+ const rowIdx = Number.isFinite(rowRef) && rowRef > 0 ? rowRef - 1 : fallbackIdx;
643
+ fallbackIdx = rowIdx + 1;
644
+ const cells = rowEl.getElementsByTagNameNS(NS_SML, "c");
645
+ const rowArr = [];
646
+ for (let c = 0; c < cells.length; c++) {
647
+ const cell = cells[c];
648
+ const ref = cell.getAttribute("r");
649
+ const idx = ref ? colIndex(ref) : rowArr.length;
650
+ while (rowArr.length < idx) rowArr.push(EMPTY_CELL);
651
+ rowArr[idx] = readCell(cell, rowIdx, idx, ctx);
652
+ }
653
+ byIndex.set(rowIdx, rowArr);
654
+ if (rowArr.some(isOccupied)) maxContentIdx = Math.max(maxContentIdx, rowIdx);
655
+ }
656
+ const grid = [];
657
+ for (let i = 0; i <= maxContentIdx; i++) grid.push(byIndex.get(i) ?? []);
658
+ const merges = [];
659
+ const mergeEls = doc.getElementsByTagNameNS(NS_SML, "mergeCell");
660
+ for (let i = 0; i < mergeEls.length; i++) {
661
+ const rect = parseRangeRef(mergeEls[i].getAttribute("ref") ?? "");
662
+ if (rect) merges.push(rect);
663
+ }
664
+ return { cells: grid, merges };
665
+ }
666
+ function inlineGridToTable(grid) {
667
+ const maxCols = grid.reduce((m, r) => Math.max(m, r.length), 1);
668
+ const rows = grid.map((cells, rowIdx) => {
669
+ const children = [];
670
+ for (let c = 0; c < maxCols; c++) {
671
+ children.push({
672
+ type: "tableCell",
673
+ ...rowIdx === 0 ? { isHeader: true } : {},
674
+ children: cells[c] ?? []
675
+ });
676
+ }
677
+ return { type: "tableRow", children };
678
+ });
679
+ return { type: "table", children: rows };
680
+ }
681
+ function textGridToTable(grid) {
682
+ return inlineGridToTable(
683
+ grid.map((row) => row.map((value) => value ? [{ type: "text", value }] : []))
684
+ );
685
+ }
686
+ function cellsToTable(cells) {
687
+ return inlineGridToTable(cells.map((row) => row.map(cellInline)));
688
+ }
689
+ function inferHeader(cells) {
690
+ if (cells.length < 2) return false;
691
+ const first = cells[0];
692
+ if (first.length === 0) return false;
693
+ return first.every((cell) => cell.kind === "string");
694
+ }
695
+ function formulasTable(cells, rect) {
696
+ if (!cells.some((row) => row.some((cell) => cell.formula))) return null;
697
+ const header = [];
698
+ for (let c = rect.left; c <= rect.right; c++) header.push(columnLetter(c));
699
+ const body = cells.map((row) => row.map((cell) => cell.formula ? `=${cell.formula}` : ""));
700
+ return textGridToTable([header, ...body]);
701
+ }
702
+ function cellInline(cell) {
703
+ return cell.richText ?? (cell.text ? [{ type: "text", value: cell.text }] : []);
704
+ }
705
+ function textInline(value) {
706
+ return value ? [{ type: "text", value }] : [];
707
+ }
708
+ function looseTable(strays) {
709
+ const withFormula = strays.some((s) => s.cell.formula);
710
+ const header = withFormula ? ["Cell", "Value", "Formula"] : ["Cell", "Value"];
711
+ const body = strays.map((s) => {
712
+ const ref = textInline(formatCellRef(s.row, s.col));
713
+ const value = cellInline(s.cell);
714
+ return withFormula ? [ref, value, textInline(s.cell.formula ? `=${s.cell.formula}` : "")] : [ref, value];
715
+ });
716
+ return inlineGridToTable([header.map(textInline), ...body]);
717
+ }
718
+ function annotatedHeading(depth, text, params) {
719
+ return {
720
+ type: "heading",
721
+ depth,
722
+ children: [{ type: "text", value: text }],
723
+ templateAnnotation: { template: "dataTable", params }
724
+ };
725
+ }
726
+ async function xlsxToMarkdownDoc(data, options = {}) {
727
+ const pkg = await openPackage(data, options);
728
+ const mainPart = requireMainPartPath(pkg, XLSX_MAIN_PART, "XLSX");
729
+ const [{ sheets, date1904 }, shared, styles] = await Promise.all([
730
+ readWorkbook(pkg, mainPart),
731
+ readSharedStrings(pkg),
732
+ readCellStyles(pkg)
733
+ ]);
734
+ let selected = sheets;
735
+ if (options.sheet !== void 0) {
736
+ const picked = typeof options.sheet === "number" ? sheets[options.sheet] : sheets.find((s) => s.name === options.sheet);
737
+ selected = picked ? [picked] : [];
738
+ }
739
+ const children = [];
740
+ const single = selected.length === 1 && options.sheet !== void 0;
741
+ const useRegions = options.regions !== false;
742
+ const withFormulas = options.formulas !== false;
743
+ for (const sheet of selected) {
744
+ const { cells, merges } = await sheetToCells(pkg, sheet.path, shared, styles, date1904);
745
+ if (!single) {
746
+ children.push({ type: "heading", depth: 1, children: [{ type: "text", value: sheet.name }] });
747
+ }
748
+ if (cells.length === 0) continue;
749
+ if (!useRegions) {
750
+ children.push(cellsToTable(cells));
751
+ continue;
752
+ }
753
+ const plan = detectRegions(cells, merges, {
754
+ ...options.maxRegionsPerSheet !== void 0 ? { maxRegionsPerSheet: options.maxRegionsPerSheet } : {},
755
+ ...options.minRegionCells !== void 0 ? { minRegionCells: options.minRegionCells } : {},
756
+ ...options.signal ? { signal: options.signal } : {}
757
+ });
758
+ for (const warning of plan.warnings) console.warn(`XLSX import: ${sheet.name}: ${warning}`);
759
+ if (plan.degraded || plan.regions.length === 0 && plan.strays.length === 0) {
760
+ children.push(cellsToTable(cells));
761
+ continue;
762
+ }
763
+ const depth = single ? 1 : 2;
764
+ for (const region of plan.regions) {
765
+ const slice = sliceRect(cells, region.rect, EMPTY_CELL);
766
+ const anchor = formatCellRef(region.rect.top, region.rect.left);
767
+ const title = region.title ?? `${sheet.name} \u2014 ${anchor}`;
768
+ children.push(
769
+ annotatedHeading(depth, title, {
770
+ sheet: sheet.name,
771
+ anchor,
772
+ // Omit the common case: `headerRow=true` is what a GFM table already
773
+ // says. (`headerRow`, not `header` — `dataTable` already declares a
774
+ // `headers` input, and two params one letter apart in the same
775
+ // annotation is a trap for anyone reading or editing the markdown.)
776
+ ...inferHeader(slice) ? {} : { headerRow: "false" },
777
+ // A caption promoted into the heading has left its cell behind;
778
+ // record where, so the reverse path can put the text back.
779
+ ...region.titleCell ? { titleAnchor: formatCellRef(region.titleCell.row, region.titleCell.col) } : {}
780
+ }),
781
+ cellsToTable(slice)
782
+ );
783
+ if (!withFormulas) continue;
784
+ const formulas = formulasTable(slice, region.rect);
785
+ if (!formulas) continue;
786
+ children.push(
787
+ annotatedHeading(depth, `${title} \u2014 formulas`, {
788
+ sheet: sheet.name,
789
+ anchor,
790
+ role: "formulas"
791
+ }),
792
+ formulas
793
+ );
794
+ }
795
+ if (plan.strays.length > 0) {
796
+ children.push(
797
+ annotatedHeading(depth, `${sheet.name} \u2014 loose cells`, {
798
+ sheet: sheet.name,
799
+ role: "loose"
800
+ }),
801
+ looseTable(plan.strays)
802
+ );
803
+ }
804
+ }
805
+ return { type: "document", children };
806
+ }
807
+
808
+ // src/xlsx/export.ts
809
+ import { docToMarkdown } from "@bendyline/squisq/doc";
810
+
811
+ // src/xlsx/workbookPlan.ts
812
+ function hasRichCellContent(nodes) {
813
+ return nodes.some(
814
+ (n) => n.type === "superscript" || n.type === "subscript" || "children" in n && Array.isArray(n.children) && hasRichCellContent(n.children)
815
+ );
816
+ }
817
+ var KEY_STRIDE = MAX_COL_INDEX + 1;
818
+ function cellKey(row, col) {
819
+ return row * KEY_STRIDE + col;
820
+ }
821
+ function keyRow(key) {
822
+ return Math.floor(key / KEY_STRIDE);
823
+ }
824
+ function keyCol(key) {
825
+ return key - keyRow(key) * KEY_STRIDE;
826
+ }
827
+ function tableToGrid(table) {
828
+ return table.children.map(
829
+ (row) => row.children.map((cell) => extractPlainText(cell.children))
830
+ );
831
+ }
832
+ function tableToRichGrid(table) {
833
+ return table.children.map(
834
+ (row) => row.children.map((cell) => hasRichCellContent(cell.children) ? cell.children : void 0)
835
+ );
836
+ }
837
+ function readRole(raw) {
838
+ return raw === "formulas" || raw === "loose" ? raw : "values";
839
+ }
840
+ function collectEntries(nodes) {
841
+ const entries = [];
842
+ let pending = null;
843
+ for (const node of nodes) {
844
+ if (node.type === "heading") {
845
+ pending = node;
846
+ continue;
847
+ }
848
+ if (node.type !== "table") continue;
849
+ const params = pending?.templateAnnotation?.params ?? {};
850
+ const sheetKey = typeof params.sheet === "string" && params.sheet !== "" ? params.sheet : null;
851
+ entries.push({
852
+ table: node,
853
+ headingText: pending ? extractPlainText(pending.children) : "",
854
+ params,
855
+ sheetKey,
856
+ role: readRole(params.role)
857
+ });
858
+ pending = null;
859
+ }
860
+ return entries;
861
+ }
862
+ function groupEntries(entries) {
863
+ const groups = [];
864
+ const bySheet = /* @__PURE__ */ new Map();
865
+ for (const entry of entries) {
866
+ if (entry.sheetKey !== null) {
867
+ let group = bySheet.get(entry.sheetKey);
868
+ if (!group) {
869
+ group = { candidate: entry.sheetKey, entries: [], anchored: true };
870
+ bySheet.set(entry.sheetKey, group);
871
+ groups.push(group);
872
+ }
873
+ group.entries.push(entry);
874
+ continue;
875
+ }
876
+ groups.push({ candidate: entry.headingText || null, entries: [entry], anchored: false });
877
+ }
878
+ return groups;
879
+ }
880
+ function place(cells, row, col, cell, clashes) {
881
+ const key = cellKey(row, col);
882
+ if (cells.has(key)) clashes.push(formatCellRef(row, col));
883
+ cells.set(key, cell);
884
+ }
885
+ function overlayFormula(cells, row, col, formula) {
886
+ const key = cellKey(row, col);
887
+ const existing = cells.get(key);
888
+ cells.set(key, { text: existing?.text ?? "", formula });
889
+ }
890
+ function formulaSource(raw) {
891
+ const trimmed = raw.trim();
892
+ return trimmed.startsWith("=") ? trimmed.slice(1) : trimmed;
893
+ }
894
+ function placeValues(entry, cells, warnings, sheetName, clashes) {
895
+ const grid = tableToGrid(entry.table);
896
+ const richGrid = tableToRichGrid(entry.table);
897
+ const anchorRaw = entry.params.anchor ?? "A1";
898
+ let anchor = parseCellRef(anchorRaw);
899
+ if (!anchor) {
900
+ warnings.push(
901
+ `Sheet "${sheetName}": anchor "${anchorRaw}" is not a valid cell reference; placed at A1 instead.`
902
+ );
903
+ anchor = { row: 0, col: 0 };
904
+ }
905
+ const height = grid.length;
906
+ const width = grid.reduce((m, r) => Math.max(m, r.length), 0);
907
+ if (anchor.row + height - 1 > MAX_ROW_INDEX || anchor.col + width - 1 > MAX_COL_INDEX) {
908
+ warnings.push(
909
+ `Sheet "${sheetName}": a table anchored at ${anchorRaw} runs past the end of the sheet; placed at A1 instead.`
910
+ );
911
+ anchor = { row: 0, col: 0 };
912
+ }
913
+ const titleRef = entry.params.titleAnchor;
914
+ if (titleRef !== void 0 && entry.headingText !== "") {
915
+ const at = parseCellRef(titleRef);
916
+ if (at) place(cells, at.row, at.col, { text: entry.headingText }, clashes);
917
+ }
918
+ for (let r = 0; r < grid.length; r++) {
919
+ const row = grid[r];
920
+ for (let c = 0; c < row.length; c++) {
921
+ const text = row[c];
922
+ if (text === "") continue;
923
+ const rich = richGrid[r]?.[c];
924
+ place(cells, anchor.row + r, anchor.col + c, rich ? { text, rich } : { text }, clashes);
925
+ }
926
+ }
927
+ }
928
+ function placeFormulas(entry, cells, warnings, sheetName) {
929
+ const grid = tableToGrid(entry.table);
930
+ if (grid.length < 2) return;
931
+ const anchorRaw = entry.params.anchor ?? "A1";
932
+ const anchor = parseCellRef(anchorRaw);
933
+ if (!anchor) {
934
+ warnings.push(
935
+ `Sheet "${sheetName}": formulas block anchor "${anchorRaw}" is not a valid cell reference; skipped.`
936
+ );
937
+ return;
938
+ }
939
+ const columns = grid[0].map((letters) => {
940
+ const trimmed = letters.trim();
941
+ if (!/^[A-Za-z]{1,3}$/.test(trimmed)) return -1;
942
+ const col = columnIndexFromLetters(trimmed);
943
+ return col >= 0 && col <= MAX_COL_INDEX ? col : -1;
944
+ });
945
+ for (let r = 1; r < grid.length; r++) {
946
+ const row = grid[r];
947
+ for (let c = 0; c < row.length; c++) {
948
+ const raw = row[c];
949
+ if (raw.trim() === "") continue;
950
+ const mapped = columns[c];
951
+ const col = mapped !== void 0 && mapped >= 0 ? mapped : anchor.col + c;
952
+ const targetRow = anchor.row + r - 1;
953
+ if (targetRow > MAX_ROW_INDEX) continue;
954
+ overlayFormula(cells, targetRow, col, formulaSource(raw));
955
+ }
956
+ }
957
+ }
958
+ function placeLoose(entry, cells, warnings, sheetName, clashes) {
959
+ const grid = tableToGrid(entry.table);
960
+ const richGrid = tableToRichGrid(entry.table);
961
+ let skipped = 0;
962
+ for (let r = 1; r < grid.length; r++) {
963
+ const row = grid[r];
964
+ const ref = (row[0] ?? "").trim();
965
+ if (ref === "") continue;
966
+ const at = parseCellRef(ref);
967
+ if (!at) {
968
+ skipped++;
969
+ continue;
970
+ }
971
+ const text = row[1] ?? "";
972
+ const formula = formulaSource(row[2] ?? "");
973
+ const rich = richGrid[r]?.[1];
974
+ const planned = { text };
975
+ if (formula !== "") planned.formula = formula;
976
+ if (rich) planned.rich = rich;
977
+ place(cells, at.row, at.col, planned, clashes);
978
+ }
979
+ if (skipped > 0) {
980
+ warnings.push(
981
+ `Sheet "${sheetName}": ${skipped} loose-cell row(s) had an invalid cell reference and were skipped.`
982
+ );
983
+ }
984
+ }
985
+ function planWorkbook(doc, options) {
986
+ const warnings = [];
987
+ const groups = groupEntries(collectEntries(doc.children));
988
+ const used = /* @__PURE__ */ new Set();
989
+ const sheets = [];
990
+ groups.forEach((group, index) => {
991
+ const fallback = `${options.sheetNamePrefix}${index + 1}`;
992
+ const name = options.sanitize(group.candidate ?? fallback, used, fallback);
993
+ const cells = /* @__PURE__ */ new Map();
994
+ const clashes = [];
995
+ for (const entry of group.entries) {
996
+ if (entry.role === "values") placeValues(entry, cells, warnings, name, clashes);
997
+ }
998
+ for (const entry of group.entries) {
999
+ if (entry.role === "formulas") placeFormulas(entry, cells, warnings, name);
1000
+ }
1001
+ for (const entry of group.entries) {
1002
+ if (entry.role === "loose") placeLoose(entry, cells, warnings, name, clashes);
1003
+ }
1004
+ if (clashes.length > 0) {
1005
+ const shown = clashes.slice(0, 5).join(", ");
1006
+ const rest = clashes.length > 5 ? ", and more" : "";
1007
+ warnings.push(
1008
+ `Sheet "${name}": ${clashes.length} overlapping cell(s) (${shown}${rest}); the later block won.`
1009
+ );
1010
+ }
1011
+ sheets.push({ name, cells, anchored: group.anchored });
1012
+ });
1013
+ let cellCount = 0;
1014
+ for (const sheet of sheets) cellCount += sheet.cells.size;
1015
+ return { sheets, warnings, cellCount };
1016
+ }
1017
+
1018
+ // src/xlsx/export.ts
1019
+ var NUMERIC_RE = /^-?\d+(\.\d+)?$/;
1020
+ function isSafeNumericCell(value) {
1021
+ if (!NUMERIC_RE.test(value)) return false;
1022
+ const unsigned = value.startsWith("-") ? value.slice(1) : value;
1023
+ const [integer] = unsigned.split(".");
1024
+ if (integer.length > 1 && integer.startsWith("0")) return false;
1025
+ const significantDigits = unsigned.replace(".", "").replace(/^0+/, "");
1026
+ if (significantDigits.length > 15) return false;
1027
+ return Number.isFinite(Number(value));
1028
+ }
1029
+ function cleanSheetName(raw) {
1030
+ return raw.replace(/[[\]:*?/\\]/g, "").trim().slice(0, 31).trim().replace(/^'+|'+$/g, "");
1031
+ }
1032
+ function sanitizeSheetName(candidate, used, fallback) {
1033
+ let base = cleanSheetName(candidate) || cleanSheetName(fallback) || "Sheet";
1034
+ let name = base;
1035
+ let n = 2;
1036
+ while (used.has(name.toLocaleLowerCase("en-US"))) {
1037
+ const suffix = String(n++);
1038
+ base = base.slice(0, 31 - suffix.length);
1039
+ name = `${base}${suffix}`;
1040
+ }
1041
+ used.add(name.toLocaleLowerCase("en-US"));
1042
+ return name;
1043
+ }
1044
+ var ERROR_VALUE_RE = /^#[A-Z0-9_/]+[!?]?$/;
1045
+ function cellXml(cell, ref, inferNumericCells) {
1046
+ const { text, formula } = cell;
1047
+ if (formula !== void 0 && formula !== "") {
1048
+ const f = `<f>${escapeXml(formula)}</f>`;
1049
+ if (text === "") return `<c r="${ref}">${f}</c>`;
1050
+ if (isSafeNumericCell(text)) return `<c r="${ref}">${f}<v>${escapeXml(text)}</v></c>`;
1051
+ if (ERROR_VALUE_RE.test(text)) return `<c r="${ref}" t="e">${f}<v>${escapeXml(text)}</v></c>`;
1052
+ return `<c r="${ref}" t="str">${f}<v>${escapeXml(text)}</v></c>`;
1053
+ }
1054
+ if (inferNumericCells && isSafeNumericCell(text)) {
1055
+ return `<c r="${ref}"><v>${escapeXml(text)}</v></c>`;
1056
+ }
1057
+ if (cell.rich) {
1058
+ return `<c r="${ref}" t="inlineStr"><is>${richRunsXml(cell.rich)}</is></c>`;
1059
+ }
1060
+ return `<c r="${ref}" t="inlineStr"><is><t xml:space="preserve">${escapeXml(text)}</t></is></c>`;
1061
+ }
1062
+ function richRunsXml(nodes) {
1063
+ const runs = [];
1064
+ const emit = (text, vertAlign) => {
1065
+ if (text === "") return;
1066
+ const rPr = vertAlign ? `<rPr><vertAlign val="${vertAlign}"/></rPr>` : "";
1067
+ runs.push(`<r>${rPr}<t xml:space="preserve">${escapeXml(text)}</t></r>`);
1068
+ };
1069
+ const walk = (list, vertAlign) => {
1070
+ for (const node of list) {
1071
+ if (node.type === "superscript" || node.type === "subscript") {
1072
+ walk(node.children, node.type === "superscript" ? "superscript" : "subscript");
1073
+ } else if ("children" in node && Array.isArray(node.children)) {
1074
+ walk(node.children, vertAlign);
1075
+ } else {
1076
+ emit(inlineToPlainText(node), vertAlign);
1077
+ }
1078
+ }
1079
+ };
1080
+ walk(nodes, null);
1081
+ return runs.join("");
1082
+ }
1083
+ function worksheetXml(sheet, inferNumericCells) {
1084
+ const keys = [...sheet.cells.keys()].sort((a, b) => a - b);
1085
+ const rows = [];
1086
+ let maxRow = 0;
1087
+ let maxCol = 0;
1088
+ let i = 0;
1089
+ while (i < keys.length) {
1090
+ const rowIdx = keyRow(keys[i]);
1091
+ let cellsXml = "";
1092
+ while (i < keys.length && keyRow(keys[i]) === rowIdx) {
1093
+ const key = keys[i];
1094
+ const col = keyCol(key);
1095
+ if (col > maxCol) maxCol = col;
1096
+ cellsXml += cellXml(
1097
+ sheet.cells.get(key),
1098
+ `${columnLetter(col)}${rowIdx + 1}`,
1099
+ inferNumericCells
1100
+ );
1101
+ i++;
1102
+ }
1103
+ if (rowIdx > maxRow) maxRow = rowIdx;
1104
+ rows.push(`<row r="${rowIdx + 1}">${cellsXml}</row>`);
1105
+ }
1106
+ const dimension = keys.length > 0 ? `A1:${columnLetter(maxCol)}${maxRow + 1}` : "A1";
1107
+ return `${xmlDeclaration()}
1108
+ <worksheet xmlns="${NS_SML}" xmlns:r="${NS_R}"><dimension ref="${dimension}"/><sheetData>${rows.join("")}</sheetData></worksheet>`;
1109
+ }
1110
+ function workbookXml(sheets, hasFormulas) {
1111
+ const sheetEls = sheets.map(
1112
+ (sheet, i) => `<sheet name="${escapeXml(sheet.name)}" sheetId="${i + 1}" r:id="rId${i + 1}"/>`
1113
+ ).join("");
1114
+ const calcPr = hasFormulas ? `<calcPr calcId="0" fullCalcOnLoad="1"/>` : "";
1115
+ return `${xmlDeclaration()}
1116
+ <workbook xmlns="${NS_SML}" xmlns:r="${NS_R}"><sheets>${sheetEls}</sheets>${calcPr}</workbook>`;
1117
+ }
1118
+ function stylesXml() {
1119
+ return `${xmlDeclaration()}
1120
+ <styleSheet xmlns="${NS_SML}"><fonts count="1"><font><sz val="11"/><name val="Calibri"/></font></fonts><fills count="1"><fill><patternFill patternType="none"/></fill></fills><borders count="1"><border/></borders><cellStyleXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0"/></cellStyleXfs><cellXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/></cellXfs></styleSheet>`;
1121
+ }
1122
+ async function markdownDocToXlsx(doc, options = {}) {
1123
+ options.signal?.throwIfAborted();
1124
+ const prefix = cleanSheetName(options.sheetNamePrefix ?? "Sheet") || "Sheet";
1125
+ const maxCells = options.maxCells ?? 1e5;
1126
+ if (!Number.isSafeInteger(maxCells) || maxCells < 0) {
1127
+ throw new RangeError("maxCells must be a non-negative safe integer");
1128
+ }
1129
+ const plan = planWorkbook(doc, { sheetNamePrefix: prefix, sanitize: sanitizeSheetName });
1130
+ for (const warning of plan.warnings) options.onWarning?.(warning);
1131
+ if (plan.cellCount > maxCells) {
1132
+ throw new RangeError(`XLSX export exceeds the ${maxCells}-cell safety limit`);
1133
+ }
1134
+ let sheets = plan.sheets;
1135
+ if (sheets.length === 0) {
1136
+ sheets = [
1137
+ {
1138
+ name: sanitizeSheetName(`${prefix}1`, /* @__PURE__ */ new Set(), "Sheet1"),
1139
+ cells: /* @__PURE__ */ new Map(),
1140
+ anchored: false
1141
+ }
1142
+ ];
1143
+ }
1144
+ const anchored = sheets.some((sheet) => sheet.anchored);
1145
+ const inferNumericCells = options.inferNumericCells ?? anchored;
1146
+ const hasFormulas = sheets.some((sheet) => {
1147
+ for (const cell of sheet.cells.values()) if (cell.formula) return true;
1148
+ return false;
1149
+ });
1150
+ const pkg = createPackage();
1151
+ sheets.forEach((sheet, i) => {
1152
+ if ((i & 31) === 0) options.signal?.throwIfAborted();
1153
+ const sheetPath = `xl/worksheets/sheet${i + 1}.xml`;
1154
+ pkg.addPart(sheetPath, worksheetXml(sheet, inferNumericCells), CONTENT_TYPE_XLSX_WORKSHEET);
1155
+ pkg.addRelationship("xl/workbook.xml", {
1156
+ id: `rId${i + 1}`,
1157
+ type: REL_WORKSHEET,
1158
+ target: `worksheets/sheet${i + 1}.xml`
1159
+ });
1160
+ });
1161
+ pkg.addPart("xl/styles.xml", stylesXml(), CONTENT_TYPE_XLSX_STYLES);
1162
+ pkg.addRelationship("xl/workbook.xml", {
1163
+ id: `rId${sheets.length + 1}`,
1164
+ type: REL_STYLES,
1165
+ target: "styles.xml"
1166
+ });
1167
+ pkg.addPart("xl/workbook.xml", workbookXml(sheets, hasFormulas), CONTENT_TYPE_XLSX_WORKBOOK);
1168
+ pkg.addRelationship("", {
1169
+ id: "rId1",
1170
+ type: REL_OFFICE_DOCUMENT,
1171
+ target: "xl/workbook.xml"
1172
+ });
1173
+ if (options.title || options.author) {
1174
+ pkg.setCoreProperties({
1175
+ title: options.title,
1176
+ creator: options.author,
1177
+ created: (/* @__PURE__ */ new Date()).toISOString(),
1178
+ modified: (/* @__PURE__ */ new Date()).toISOString()
1179
+ });
1180
+ }
1181
+ return pkg.toArrayBuffer();
1182
+ }
1183
+ async function docToXlsx(doc, options) {
1184
+ return markdownDocToXlsx(docToMarkdown(doc), options);
1185
+ }
1186
+
1187
+ // src/xlsx/index.ts
1188
+ async function xlsxToDoc(data, options) {
1189
+ return markdownToDoc(await xlsxToMarkdownDoc(data, options));
1190
+ }
1191
+
1192
+ export {
1193
+ xlsxToMarkdownDoc,
1194
+ markdownDocToXlsx,
1195
+ docToXlsx,
1196
+ xlsxToDoc
1197
+ };