@elabs-ai/components-viewer 4.1.0 → 5.0.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.
- package/dist/{code-adapter-ADZ4UOGN.js → adapters/code/code-adapter.js} +15 -64
- package/dist/adapters/code/code-adapter.js.map +1 -0
- package/dist/adapters/code/code-language.js +10 -0
- package/dist/adapters/code/code-language.js.map +1 -0
- package/dist/adapters/code/code-manifest.js +10 -0
- package/dist/adapters/code/code-manifest.js.map +1 -0
- package/dist/adapters/code/code-theme.js +8 -0
- package/dist/adapters/code/code-theme.js.map +1 -0
- package/dist/{csv-adapter-7HOFZ5Z6.js → adapters/csv/csv-adapter.js} +13 -9
- package/dist/adapters/csv/csv-adapter.js.map +1 -0
- package/dist/adapters/csv/csv-manifest.js +9 -0
- package/dist/adapters/csv/csv-manifest.js.map +1 -0
- package/dist/{docx-adapter-5CQDHWTD.js → adapters/docx/docx-adapter.js} +17 -175
- package/dist/adapters/docx/docx-adapter.js.map +1 -0
- package/dist/adapters/docx/docx-manifest.js +9 -0
- package/dist/adapters/docx/docx-manifest.js.map +1 -0
- package/dist/adapters/docx/docx-model.js +21 -0
- package/dist/adapters/docx/docx-model.js.map +1 -0
- package/dist/{image-adapter-WOHZR24J.js → adapters/image/image-adapter.js} +4 -3
- package/dist/adapters/image/image-adapter.js.map +1 -0
- package/dist/adapters/image/image-manifest.js +9 -0
- package/dist/adapters/image/image-manifest.js.map +1 -0
- package/dist/adapters/index.js +56 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/{json-adapter-ZUW5GQHE.js → adapters/json/json-adapter.js} +4 -3
- package/dist/adapters/json/json-adapter.js.map +1 -0
- package/dist/adapters/json/json-manifest.js +9 -0
- package/dist/adapters/json/json-manifest.js.map +1 -0
- package/dist/{markdown-adapter-YC6WTBS4.js → adapters/markdown/markdown-adapter.js} +14 -36
- package/dist/adapters/markdown/markdown-adapter.js.map +1 -0
- package/dist/adapters/markdown/markdown-manifest.js +9 -0
- package/dist/adapters/markdown/markdown-manifest.js.map +1 -0
- package/dist/adapters/markdown/markdown-marks.js +10 -0
- package/dist/adapters/markdown/markdown-marks.js.map +1 -0
- package/dist/{media-adapter-MCTB4GBH.js → adapters/media/media-adapter.js} +4 -3
- package/dist/adapters/media/media-adapter.js.map +1 -0
- package/dist/adapters/media/media-manifest.js +9 -0
- package/dist/adapters/media/media-manifest.js.map +1 -0
- package/dist/{pdf-adapter-5PMKEXUD.js → adapters/pdf/pdf-adapter.js} +31 -81
- package/dist/adapters/pdf/pdf-adapter.js.map +1 -0
- package/dist/adapters/pdf/pdf-engine.js +14 -0
- package/dist/adapters/pdf/pdf-engine.js.map +1 -0
- package/dist/adapters/pdf/pdf-manifest.js +9 -0
- package/dist/adapters/pdf/pdf-manifest.js.map +1 -0
- package/dist/adapters/pdf/pdf-text.js +20 -0
- package/dist/adapters/pdf/pdf-text.js.map +1 -0
- package/dist/{pptx-adapter-BONWFHYT.js → adapters/pptx/pptx-adapter.js} +34 -135
- package/dist/adapters/pptx/pptx-adapter.js.map +1 -0
- package/dist/adapters/pptx/pptx-manifest.js +9 -0
- package/dist/adapters/pptx/pptx-manifest.js.map +1 -0
- package/dist/adapters/pptx/pptx-model.js +37 -0
- package/dist/adapters/pptx/pptx-model.js.map +1 -0
- package/dist/{text-adapter-NFNWB5W3.js → adapters/text/text-adapter.js} +8 -5
- package/dist/adapters/text/text-adapter.js.map +1 -0
- package/dist/adapters/text/text-manifest.js +9 -0
- package/dist/adapters/text/text-manifest.js.map +1 -0
- package/dist/{xlsx-adapter-O3XIVLFC.js → adapters/xlsx/xlsx-adapter.js} +12 -8
- package/dist/adapters/xlsx/xlsx-adapter.js.map +1 -0
- package/dist/adapters/xlsx/xlsx-manifest.js +9 -0
- package/dist/adapters/xlsx/xlsx-manifest.js.map +1 -0
- package/dist/chunk-3HKQFFZW.js +31 -0
- package/dist/chunk-3HKQFFZW.js.map +1 -0
- package/dist/chunk-5VJWILZ7.js +182 -0
- package/dist/chunk-5VJWILZ7.js.map +1 -0
- package/dist/{chunk-UL43NGUG.js → chunk-5YE2MUS2.js} +1 -1
- package/dist/{chunk-5OMONA2G.js → chunk-6FVJ4D7W.js} +3 -15
- package/dist/chunk-6FVJ4D7W.js.map +1 -0
- package/dist/{chunk-RGWDIQJR.js → chunk-7EASJBQM.js} +2 -2
- package/dist/chunk-7PGGKQVY.js +26 -0
- package/dist/chunk-7PGGKQVY.js.map +1 -0
- package/dist/{chunk-XBOR2YXS.js → chunk-AK7F3IZE.js} +9 -30
- package/dist/chunk-AK7F3IZE.js.map +1 -0
- package/dist/chunk-AOYEL7O3.js +34 -0
- package/dist/chunk-AOYEL7O3.js.map +1 -0
- package/dist/chunk-BDUFH66C.js +61 -0
- package/dist/chunk-BDUFH66C.js.map +1 -0
- package/dist/chunk-BWMXMHVZ.js +107 -0
- package/dist/chunk-BWMXMHVZ.js.map +1 -0
- package/dist/chunk-CIT2HHRI.js +9 -0
- package/dist/chunk-CIT2HHRI.js.map +1 -0
- package/dist/chunk-CLOI4HBA.js +18 -0
- package/dist/chunk-CLOI4HBA.js.map +1 -0
- package/dist/chunk-CMYBLW7D.js +21 -0
- package/dist/chunk-CMYBLW7D.js.map +1 -0
- package/dist/chunk-CO2VJRSU.js +18 -0
- package/dist/chunk-CO2VJRSU.js.map +1 -0
- package/dist/{chunk-3U3MESBU.js → chunk-EA72HA4I.js} +2 -2
- package/dist/{chunk-KOMDRWOU.js → chunk-FDT4QOIG.js} +2 -2
- package/dist/chunk-FQHZEQUH.js +90 -0
- package/dist/chunk-FQHZEQUH.js.map +1 -0
- package/dist/chunk-G27UJZWH.js +82 -0
- package/dist/chunk-G27UJZWH.js.map +1 -0
- package/dist/{chunk-BCF3RWRL.js → chunk-GH4UBYST.js} +2 -2
- package/dist/{chunk-2NQ4RSJ3.js → chunk-GNBD53AT.js} +2 -2
- package/dist/{chunk-VO2273Z2.js → chunk-HOQZ7OPH.js} +2 -18
- package/dist/chunk-HOQZ7OPH.js.map +1 -0
- package/dist/{chunk-GGR7BNW4.js → chunk-HUUJIFFT.js} +2 -2
- package/dist/{chunk-FELIAGKI.js → chunk-I5XD2NJ6.js} +2 -2
- package/dist/chunk-IB4N72FA.js +21 -0
- package/dist/chunk-IB4N72FA.js.map +1 -0
- package/dist/{chunk-4OFQYALG.js → chunk-IQ6Z4X3K.js} +8 -68
- package/dist/chunk-IQ6Z4X3K.js.map +1 -0
- package/dist/{chunk-AORNMH77.js → chunk-JHFC2KBI.js} +1 -5
- package/dist/chunk-JHFC2KBI.js.map +1 -0
- package/dist/chunk-LRPZ373O.js +93 -0
- package/dist/chunk-LRPZ373O.js.map +1 -0
- package/dist/chunk-M77ICVWP.js +24 -0
- package/dist/chunk-M77ICVWP.js.map +1 -0
- package/dist/chunk-MJUH32ZK.js +24 -0
- package/dist/chunk-MJUH32ZK.js.map +1 -0
- package/dist/chunk-PAIVK4N6.js +162 -0
- package/dist/chunk-PAIVK4N6.js.map +1 -0
- package/dist/chunk-PKYDKXCO.js +129 -0
- package/dist/chunk-PKYDKXCO.js.map +1 -0
- package/dist/chunk-QIPKBOZJ.js +59 -0
- package/dist/chunk-QIPKBOZJ.js.map +1 -0
- package/dist/chunk-SASY77ZL.js +29 -0
- package/dist/chunk-SASY77ZL.js.map +1 -0
- package/dist/{chunk-SLXRUYT3.js → chunk-SBG7WPGO.js} +2 -44
- package/dist/chunk-SBG7WPGO.js.map +1 -0
- package/dist/chunk-T4Y4YRHJ.js +733 -0
- package/dist/chunk-T4Y4YRHJ.js.map +1 -0
- package/dist/chunk-TLCNGQKL.js +37 -0
- package/dist/chunk-TLCNGQKL.js.map +1 -0
- package/dist/chunk-US5KT3TR.js +141 -0
- package/dist/chunk-US5KT3TR.js.map +1 -0
- package/dist/{chunk-HMG2ERXH.js → chunk-YA72QE57.js} +2 -2
- package/dist/{chunk-H3OAON3D.js → chunk-YWHFMCS4.js} +2 -2
- package/dist/{chunk-UJIJECEF.js → chunk-ZAVXZRIO.js} +2 -2
- package/dist/components/grid-text.js +19 -0
- package/dist/components/grid-text.js.map +1 -0
- package/dist/components/marked-text.js +10 -0
- package/dist/components/marked-text.js.map +1 -0
- package/dist/components/sheet-table.js +14 -0
- package/dist/components/sheet-table.js.map +1 -0
- package/dist/core/errors.js +18 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/highlight-marks.js +10 -0
- package/dist/core/highlight-marks.js.map +1 -0
- package/dist/core/highlight-resolve.js +10 -0
- package/dist/core/highlight-resolve.js.map +1 -0
- package/dist/core/highlight.js +12 -0
- package/dist/core/highlight.js.map +1 -0
- package/dist/core/registry.js +12 -0
- package/dist/core/registry.js.map +1 -0
- package/dist/core/scroll-host.js +10 -0
- package/dist/core/scroll-host.js.map +1 -0
- package/dist/core/text-index.js +14 -0
- package/dist/core/text-index.js.map +1 -0
- package/dist/core/types.js +8 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/use-highlight-scroll.js +11 -0
- package/dist/core/use-highlight-scroll.js.map +1 -0
- package/dist/core/use-page-control.js +9 -0
- package/dist/core/use-page-control.js.map +1 -0
- package/dist/core/use-paged-scroll.js +10 -0
- package/dist/core/use-paged-scroll.js.map +1 -0
- package/dist/core/use-viewport-size.js +10 -0
- package/dist/core/use-viewport-size.js.map +1 -0
- package/dist/core/zoom.js +16 -0
- package/dist/core/zoom.js.map +1 -0
- package/dist/file-viewer/file-viewer-context.js +11 -0
- package/dist/file-viewer/file-viewer-context.js.map +1 -0
- package/dist/file-viewer/file-viewer-find.js +13 -0
- package/dist/file-viewer/file-viewer-find.js.map +1 -0
- package/dist/file-viewer/file-viewer-pager.js +10 -0
- package/dist/file-viewer/file-viewer-pager.js.map +1 -0
- package/dist/file-viewer/file-viewer-zoom.js +13 -0
- package/dist/file-viewer/file-viewer-zoom.js.map +1 -0
- package/dist/file-viewer/file-viewer.js +48 -0
- package/dist/file-viewer/file-viewer.js.map +1 -0
- package/dist/index.js +78 -1268
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
- package/src/__contract__/file-viewer.contract.test.tsx +49 -0
- package/src/file-viewer/file-viewer-recipes.stories.tsx +406 -0
- package/src/file-viewer/file-viewer.stories.tsx +2 -2
- package/src/file-viewer/file-viewer.test.tsx +35 -1
- package/src/file-viewer/file-viewer.tsx +16 -3
- package/dist/chunk-4OFQYALG.js.map +0 -1
- package/dist/chunk-5OMONA2G.js.map +0 -1
- package/dist/chunk-AORNMH77.js.map +0 -1
- package/dist/chunk-SLXRUYT3.js.map +0 -1
- package/dist/chunk-VO2273Z2.js.map +0 -1
- package/dist/chunk-XBOR2YXS.js.map +0 -1
- package/dist/code-adapter-ADZ4UOGN.js.map +0 -1
- package/dist/csv-adapter-7HOFZ5Z6.js.map +0 -1
- package/dist/docx-adapter-5CQDHWTD.js.map +0 -1
- package/dist/image-adapter-WOHZR24J.js.map +0 -1
- package/dist/json-adapter-ZUW5GQHE.js.map +0 -1
- package/dist/markdown-adapter-YC6WTBS4.js.map +0 -1
- package/dist/media-adapter-MCTB4GBH.js.map +0 -1
- package/dist/pdf-adapter-5PMKEXUD.js.map +0 -1
- package/dist/pptx-adapter-BONWFHYT.js.map +0 -1
- package/dist/text-adapter-NFNWB5W3.js.map +0 -1
- package/dist/xlsx-adapter-O3XIVLFC.js.map +0 -1
- /package/dist/{chunk-UL43NGUG.js.map → chunk-5YE2MUS2.js.map} +0 -0
- /package/dist/{chunk-RGWDIQJR.js.map → chunk-7EASJBQM.js.map} +0 -0
- /package/dist/{chunk-3U3MESBU.js.map → chunk-EA72HA4I.js.map} +0 -0
- /package/dist/{chunk-KOMDRWOU.js.map → chunk-FDT4QOIG.js.map} +0 -0
- /package/dist/{chunk-BCF3RWRL.js.map → chunk-GH4UBYST.js.map} +0 -0
- /package/dist/{chunk-2NQ4RSJ3.js.map → chunk-GNBD53AT.js.map} +0 -0
- /package/dist/{chunk-GGR7BNW4.js.map → chunk-HUUJIFFT.js.map} +0 -0
- /package/dist/{chunk-FELIAGKI.js.map → chunk-I5XD2NJ6.js.map} +0 -0
- /package/dist/{chunk-HMG2ERXH.js.map → chunk-YA72QE57.js.map} +0 -0
- /package/dist/{chunk-H3OAON3D.js.map → chunk-YWHFMCS4.js.map} +0 -0
- /package/dist/{chunk-UJIJECEF.js.map → chunk-ZAVXZRIO.js.map} +0 -0
|
@@ -1,149 +1,48 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
"use client";
|
|
3
3
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
PPTX_NOTES_LINE,
|
|
5
|
+
PPTX_TITLE_LINE,
|
|
6
|
+
notesTarget,
|
|
7
|
+
orderSlidePaths,
|
|
8
|
+
parseNotes,
|
|
9
|
+
parseSlide,
|
|
10
|
+
slidesToTextWithMap
|
|
11
|
+
} from "../../chunk-US5KT3TR.js";
|
|
12
|
+
import {
|
|
13
|
+
usePageControl
|
|
14
|
+
} from "../../chunk-MJUH32ZK.js";
|
|
15
|
+
import {
|
|
16
|
+
usePagedScroll
|
|
17
|
+
} from "../../chunk-IQ6Z4X3K.js";
|
|
6
18
|
import {
|
|
7
|
-
usePageControl,
|
|
8
|
-
usePagedScroll,
|
|
9
19
|
useViewportSize
|
|
10
|
-
} from "
|
|
20
|
+
} from "../../chunk-3HKQFFZW.js";
|
|
21
|
+
import "../../chunk-7PGGKQVY.js";
|
|
22
|
+
import {
|
|
23
|
+
pptxManifest
|
|
24
|
+
} from "../../chunk-FDT4QOIG.js";
|
|
11
25
|
import {
|
|
12
26
|
MarkedText
|
|
13
|
-
} from "
|
|
27
|
+
} from "../../chunk-GNBD53AT.js";
|
|
14
28
|
import {
|
|
15
|
-
createTextIndexBuilder,
|
|
16
29
|
spanAt
|
|
17
|
-
} from "
|
|
30
|
+
} from "../../chunk-5YE2MUS2.js";
|
|
18
31
|
import {
|
|
19
|
-
toMarkRanges,
|
|
20
32
|
useScrollActiveHighlightIntoView
|
|
21
|
-
} from "
|
|
33
|
+
} from "../../chunk-IB4N72FA.js";
|
|
34
|
+
import {
|
|
35
|
+
toMarkRanges
|
|
36
|
+
} from "../../chunk-HOQZ7OPH.js";
|
|
22
37
|
import {
|
|
23
38
|
ViewerError,
|
|
24
39
|
toViewerError
|
|
25
|
-
} from "
|
|
40
|
+
} from "../../chunk-JHFC2KBI.js";
|
|
41
|
+
import "../../chunk-CIT2HHRI.js";
|
|
26
42
|
|
|
27
43
|
// src/adapters/pptx/pptx-adapter.tsx
|
|
28
44
|
import { cn, ProseHeading, useLocale } from "@elabs-ai/components-ui";
|
|
29
45
|
import { useCallback, useEffect, useMemo, useRef } from "react";
|
|
30
|
-
|
|
31
|
-
// src/adapters/pptx/pptx-model.ts
|
|
32
|
-
var DRAWING_NS = "http://schemas.openxmlformats.org/drawingml/2006/main";
|
|
33
|
-
var PRESENTATION_NS = "http://schemas.openxmlformats.org/presentationml/2006/main";
|
|
34
|
-
function orderSlidePaths(paths) {
|
|
35
|
-
return paths.filter((path) => /^ppt\/slides\/slide\d+\.xml$/.test(path)).sort((left, right) => slideNumber(left) - slideNumber(right));
|
|
36
|
-
}
|
|
37
|
-
function slideNumber(path) {
|
|
38
|
-
const match = /(\d+)\.xml$/.exec(path);
|
|
39
|
-
return match?.[1] ? Number(match[1]) : 0;
|
|
40
|
-
}
|
|
41
|
-
function textOf(node) {
|
|
42
|
-
const runs = node.getElementsByTagNameNS(DRAWING_NS, "t");
|
|
43
|
-
let text = "";
|
|
44
|
-
for (const run of Array.from(runs)) text += run.textContent ?? "";
|
|
45
|
-
return text.trim();
|
|
46
|
-
}
|
|
47
|
-
function linesOf(shape) {
|
|
48
|
-
const lines = [];
|
|
49
|
-
for (const paragraph of Array.from(shape.getElementsByTagNameNS(DRAWING_NS, "p"))) {
|
|
50
|
-
const text = textOf(paragraph);
|
|
51
|
-
if (!text) continue;
|
|
52
|
-
const properties = paragraph.getElementsByTagNameNS(DRAWING_NS, "pPr")[0];
|
|
53
|
-
const level = Number(properties?.getAttribute("lvl") ?? 0);
|
|
54
|
-
lines.push({ text, level: Number.isFinite(level) ? level : 0 });
|
|
55
|
-
}
|
|
56
|
-
return lines;
|
|
57
|
-
}
|
|
58
|
-
function placeholderType(shape) {
|
|
59
|
-
const placeholder = shape.getElementsByTagNameNS(PRESENTATION_NS, "ph")[0];
|
|
60
|
-
return placeholder?.getAttribute("type") ?? void 0;
|
|
61
|
-
}
|
|
62
|
-
function tableLines(frame) {
|
|
63
|
-
const lines = [];
|
|
64
|
-
for (const row of Array.from(frame.getElementsByTagNameNS(DRAWING_NS, "tr"))) {
|
|
65
|
-
const cells = Array.from(row.getElementsByTagNameNS(DRAWING_NS, "tc")).map(textOf);
|
|
66
|
-
const text = cells.join(" ").trim();
|
|
67
|
-
if (text) lines.push({ text, level: 0 });
|
|
68
|
-
}
|
|
69
|
-
return lines;
|
|
70
|
-
}
|
|
71
|
-
function parseSlide(document, index) {
|
|
72
|
-
const tree = document.getElementsByTagNameNS(PRESENTATION_NS, "spTree")[0];
|
|
73
|
-
const slide = { index, lines: [] };
|
|
74
|
-
if (!tree) return slide;
|
|
75
|
-
for (const node of Array.from(tree.children)) {
|
|
76
|
-
if (node.namespaceURI !== PRESENTATION_NS) continue;
|
|
77
|
-
if (node.localName === "sp") {
|
|
78
|
-
const type = placeholderType(node);
|
|
79
|
-
const lines = linesOf(node);
|
|
80
|
-
if ((type === "title" || type === "ctrTitle") && !slide.title) {
|
|
81
|
-
const title = lines.map((line) => line.text).join(" ");
|
|
82
|
-
if (title) slide.title = title;
|
|
83
|
-
continue;
|
|
84
|
-
}
|
|
85
|
-
slide.lines.push(...lines);
|
|
86
|
-
continue;
|
|
87
|
-
}
|
|
88
|
-
if (node.localName === "graphicFrame") slide.lines.push(...tableLines(node));
|
|
89
|
-
}
|
|
90
|
-
return slide;
|
|
91
|
-
}
|
|
92
|
-
function parseNotes(document) {
|
|
93
|
-
const tree = document.getElementsByTagNameNS(PRESENTATION_NS, "spTree")[0];
|
|
94
|
-
if (!tree) return void 0;
|
|
95
|
-
const lines = [];
|
|
96
|
-
for (const shape of Array.from(tree.getElementsByTagNameNS(PRESENTATION_NS, "sp"))) {
|
|
97
|
-
if (placeholderType(shape) !== "body") continue;
|
|
98
|
-
for (const line of linesOf(shape)) lines.push(line.text);
|
|
99
|
-
}
|
|
100
|
-
const notes = lines.join("\n").trim();
|
|
101
|
-
return notes || void 0;
|
|
102
|
-
}
|
|
103
|
-
var RELATIONSHIP_NS = "http://schemas.openxmlformats.org/package/2006/relationships";
|
|
104
|
-
function resolveRelative(fromPart, target) {
|
|
105
|
-
if (target.startsWith("/")) return target.slice(1);
|
|
106
|
-
const segments = fromPart.split("/").slice(0, -1);
|
|
107
|
-
for (const segment of target.split("/")) {
|
|
108
|
-
if (segment === "." || segment === "") continue;
|
|
109
|
-
if (segment === "..") segments.pop();
|
|
110
|
-
else segments.push(segment);
|
|
111
|
-
}
|
|
112
|
-
return segments.join("/");
|
|
113
|
-
}
|
|
114
|
-
function notesTarget(rels, slidePath) {
|
|
115
|
-
for (const relationship of Array.from(
|
|
116
|
-
rels.getElementsByTagNameNS(RELATIONSHIP_NS, "Relationship")
|
|
117
|
-
)) {
|
|
118
|
-
if (!relationship.getAttribute("Type")?.endsWith("/notesSlide")) continue;
|
|
119
|
-
const target = relationship.getAttribute("Target");
|
|
120
|
-
if (target) return resolveRelative(slidePath, target);
|
|
121
|
-
}
|
|
122
|
-
return void 0;
|
|
123
|
-
}
|
|
124
|
-
var PPTX_LINE_SEPARATOR = "\n";
|
|
125
|
-
var PPTX_SLIDE_SEPARATOR = "\n\n";
|
|
126
|
-
var PPTX_TITLE_LINE = -1;
|
|
127
|
-
var PPTX_NOTES_LINE = -2;
|
|
128
|
-
function slidesToTextWithMap(slides) {
|
|
129
|
-
const builder = createTextIndexBuilder({ separator: PPTX_LINE_SEPARATOR });
|
|
130
|
-
slides.forEach((slide, index) => {
|
|
131
|
-
let separator = index === 0 ? void 0 : PPTX_SLIDE_SEPARATOR;
|
|
132
|
-
const push = (chunk, line) => {
|
|
133
|
-
if (!chunk) return;
|
|
134
|
-
builder.push(chunk, { slide: slide.index, line }, separator);
|
|
135
|
-
separator = void 0;
|
|
136
|
-
};
|
|
137
|
-
push(slide.title, PPTX_TITLE_LINE);
|
|
138
|
-
slide.lines.forEach((line, lineIndex) => {
|
|
139
|
-
push(line.text, lineIndex);
|
|
140
|
-
});
|
|
141
|
-
push(slide.notes, PPTX_NOTES_LINE);
|
|
142
|
-
});
|
|
143
|
-
return builder.build();
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// src/adapters/pptx/pptx-adapter.tsx
|
|
147
46
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
148
47
|
var LEVEL_INDENT = ["ps-0", "ps-4", "ps-8", "ps-12"];
|
|
149
48
|
var SLIDE_MAX_WIDTH = 768;
|
|
@@ -209,7 +108,7 @@ function PptxRenderer({
|
|
|
209
108
|
onPageChange
|
|
210
109
|
}) {
|
|
211
110
|
const deck = doc;
|
|
212
|
-
const [
|
|
111
|
+
const [slideNumber, goToSlide] = usePageControl(pageNumber, onPageChange, deck.slides.length);
|
|
213
112
|
const listRef = useRef(null);
|
|
214
113
|
const viewport = useViewportSize(listRef);
|
|
215
114
|
const marks = useMemo(
|
|
@@ -224,7 +123,7 @@ function PptxRenderer({
|
|
|
224
123
|
const virtualizer = usePagedScroll({
|
|
225
124
|
count: deck.slides.length,
|
|
226
125
|
listRef,
|
|
227
|
-
pageNumber:
|
|
126
|
+
pageNumber: slideNumber,
|
|
228
127
|
goToPage: goToSlide,
|
|
229
128
|
estimateSize,
|
|
230
129
|
sizeKey: frameWidth
|
|
@@ -250,7 +149,7 @@ function PptxRenderer({
|
|
|
250
149
|
const position = deck.slides.findIndex((candidate) => candidate.index === activeSlide);
|
|
251
150
|
if (position >= 0) goToSlide(position + 1);
|
|
252
151
|
}, [activeSlide, deck.slides, goToSlide]);
|
|
253
|
-
useScrollActiveHighlightIntoView(listRef, activeHighlightId,
|
|
152
|
+
useScrollActiveHighlightIntoView(listRef, activeHighlightId, slideNumber);
|
|
254
153
|
return (
|
|
255
154
|
// No viewport of its own: the slide chrome moved to the shell (ADR 0026), so
|
|
256
155
|
// there is no fixed control above a scrolling body and `FileViewerContent`
|
|
@@ -295,7 +194,7 @@ function PptxRenderer({
|
|
|
295
194
|
}
|
|
296
195
|
function PptxSlideView({
|
|
297
196
|
slide,
|
|
298
|
-
slideNumber
|
|
197
|
+
slideNumber,
|
|
299
198
|
baseHeadingLevel,
|
|
300
199
|
marks,
|
|
301
200
|
starts
|
|
@@ -308,9 +207,9 @@ function PptxSlideView({
|
|
|
308
207
|
{
|
|
309
208
|
tabIndex: 0,
|
|
310
209
|
"data-slot": "pptx-slide",
|
|
311
|
-
"data-page":
|
|
210
|
+
"data-page": slideNumber,
|
|
312
211
|
"aria-label": t("viewer.pptx.slide", {
|
|
313
|
-
slide: formatNumber(
|
|
212
|
+
slide: formatNumber(slideNumber)
|
|
314
213
|
}),
|
|
315
214
|
className: "border-border bg-card focus-ring mx-auto flex aspect-video w-full max-w-3xl flex-col gap-3 overflow-auto rounded-md border p-6 shadow-sm",
|
|
316
215
|
children: [
|
|
@@ -348,4 +247,4 @@ var pptx_adapter_default = adapterModule;
|
|
|
348
247
|
export {
|
|
349
248
|
pptx_adapter_default as default
|
|
350
249
|
};
|
|
351
|
-
//# sourceMappingURL=pptx-adapter
|
|
250
|
+
//# sourceMappingURL=pptx-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/adapters/pptx/pptx-adapter.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * PowerPoint adapter — a deck becomes a slide outline you can page through.\n *\n * A `.pptx` is a zip of XML, so there is no PowerPoint library here: jszip opens\n * the container and the platform's own `DOMParser` reads the parts. What is\n * extracted, and why it is an outline rather than a reproduction of the slide\n * canvas, is argued in `pptx-model.ts`.\n *\n * The deck is one continuous column of slides, virtualized over the shell's\n * viewport — the same treatment the PDF pages get, for the same reason: reading\n * a deck end to end is scrolling, not clicking \"next\" 60 times. The slide number\n * follows the scroll, and the pager scrolls.\n */\n\nimport type { ProseHeadingLevel, ResolvedFileSource } from \"@elabs-ai/components-ui\";\nimport { cn, ProseHeading, useLocale } from \"@elabs-ai/components-ui\";\nimport { useCallback, useEffect, useMemo, useRef } from \"react\";\n\nimport { MarkedText } from \"../../components/marked-text\";\nimport { ViewerError, toViewerError } from \"../../core/errors\";\nimport { toMarkRanges, type MarkRanges } from \"../../core/highlight-marks\";\nimport { spanAt, type TextIndex } from \"../../core/text-index\";\nimport { useScrollActiveHighlightIntoView } from \"../../core/use-highlight-scroll\";\nimport { usePageControl } from \"../../core/use-page-control\";\nimport { usePagedScroll } from \"../../core/use-paged-scroll\";\nimport { useViewportSize } from \"../../core/use-viewport-size\";\nimport type {\n AdapterDocument,\n AdapterLoadContext,\n AdapterModule,\n AdapterRendererProps,\n FileAdapter,\n} from \"../../core/types\";\nimport { pptxManifest } from \"./pptx-manifest\";\nimport {\n notesTarget,\n orderSlidePaths,\n parseNotes,\n parseSlide,\n slidesToTextWithMap,\n PPTX_NOTES_LINE,\n PPTX_TITLE_LINE,\n type PptxRef,\n type PptxSlide,\n} from \"./pptx-model\";\n\nexport interface PptxDocument extends AdapterDocument {\n kind: \"pptx\";\n slides: PptxSlide[];\n pageCount: number;\n /** Which slide and line each stretch of `text` came from. */\n textIndex?: TextIndex<PptxRef>;\n}\n\n/** Indent per outline level. Four rungs is as deep as a readable slide ever goes. */\nconst LEVEL_INDENT = [\"ps-0\", \"ps-4\", \"ps-8\", \"ps-12\"] as const;\n\n/** The slide column's own cap, matching the `max-w-3xl` on the frame. */\nconst SLIDE_MAX_WIDTH = 768;\n\n/** Space between two slides in the column, in CSS pixels. */\nconst SLIDE_GAP = 16;\n\n/** Room reserved for a slide's speaker notes before they are measured. */\nconst NOTES_ESTIMATE = 72;\n\nclass PptxAdapter implements FileAdapter {\n async load(source: ResolvedFileSource, context: AdapterLoadContext): Promise<PptxDocument> {\n let buffer: ArrayBuffer;\n try {\n buffer = await source.bytes(context.signal);\n } catch (error) {\n throw toViewerError(error, \"read-failed\", { fileName: source.name });\n }\n\n // Dynamic: the ONLY edge to the optional peer (heavy-deps:check).\n const JSZip = (await import(\"jszip\")).default;\n\n try {\n const zip = await JSZip.loadAsync(buffer);\n const parser = new DOMParser();\n const parse = (xml: string) => parser.parseFromString(xml, \"application/xml\");\n\n const slidePaths = orderSlidePaths(Object.keys(zip.files));\n if (slidePaths.length === 0) {\n throw new ViewerError(\"parse-failed\", \"The presentation contains no slides.\", {\n fileName: source.name,\n });\n }\n\n const slides: PptxSlide[] = [];\n for (const [position, path] of slidePaths.entries()) {\n if (context.signal?.aborted) break;\n const xml = await zip.file(path)?.async(\"string\");\n if (!xml) continue;\n const slide = parseSlide(parse(xml), position + 1);\n\n // Notes are best-effort: a deck with no notes part, or one whose\n // relationships do not resolve, is a complete deck — not a failure.\n const relsXml = await zip\n .file(`${dirOf(path)}/_rels/${baseOf(path)}.rels`)\n ?.async(\"string\");\n const notesPath = relsXml ? notesTarget(parse(relsXml), path) : undefined;\n const notesXml = notesPath ? await zip.file(notesPath)?.async(\"string\") : undefined;\n if (notesXml) slide.notes = parseNotes(parse(notesXml));\n\n slides.push(slide);\n }\n\n const textIndex = slidesToTextWithMap(slides);\n return {\n kind: \"pptx\",\n slides,\n pageCount: slides.length,\n text: textIndex.text,\n textIndex,\n };\n } catch (error) {\n throw toViewerError(error, \"parse-failed\", { fileName: source.name });\n }\n }\n}\n\nfunction dirOf(path: string): string {\n return path.slice(0, path.lastIndexOf(\"/\"));\n}\n\nfunction baseOf(path: string): string {\n return path.slice(path.lastIndexOf(\"/\") + 1);\n}\n\n/* -------------------------------------------------------------------------- */\n/* Renderer */\n/* -------------------------------------------------------------------------- */\n\nfunction PptxRenderer({\n document: doc,\n className,\n baseHeadingLevel = 2,\n highlights,\n activeHighlightId,\n pageNumber,\n onPageChange,\n}: AdapterRendererProps) {\n const deck = doc as PptxDocument;\n // One pager for every paginated format, so a deck's slides and a PDF's pages\n // are driven by the same shell control (ADR 0026). 1-based on the wire; this\n // renderer's array is 0-based, and the conversion stays local.\n const [slideNumber, goToSlide] = usePageControl(pageNumber, onPageChange, deck.slides.length);\n const listRef = useRef<HTMLDivElement>(null);\n const viewport = useViewportSize(listRef);\n\n const marks = useMemo(\n () => toMarkRanges(highlights, deck.text?.length ?? 0),\n [highlights, deck.text],\n );\n\n // A slide frame is 16:9 inside a capped column, so its height follows from the\n // pane's width — no measurement of the slide itself is needed for the first\n // frame. `measureElement` corrects a slide whose outline overflows the ratio,\n // and whose notes are longer than the estimate.\n const frameWidth = Math.min(SLIDE_MAX_WIDTH, (viewport?.width ?? SLIDE_MAX_WIDTH) - SLIDE_GAP);\n const estimateSize = useCallback(\n (index: number) =>\n Math.round((frameWidth * 9) / 16) +\n SLIDE_GAP +\n (deck.slides[index]?.notes ? NOTES_ESTIMATE : 0),\n [frameWidth, deck.slides],\n );\n\n const virtualizer = usePagedScroll({\n count: deck.slides.length,\n listRef,\n pageNumber: slideNumber,\n goToPage: goToSlide,\n estimateSize,\n sizeKey: frameWidth,\n });\n\n // Where each line of the deck begins in the projection, keyed by slide. Built\n // once for the whole deck rather than per slide: several slides are on screen\n // at a time now, and one pass over the spans is cheaper than one pass per\n // visible slide on every scroll.\n const starts = useMemo(() => {\n const map = new Map<number, Map<number, number>>();\n for (const span of deck.textIndex?.spans ?? []) {\n const slide = map.get(span.ref.slide) ?? new Map<number, number>();\n slide.set(span.ref.line, span.start);\n map.set(span.ref.slide, slide);\n }\n return map;\n }, [deck.textIndex]);\n\n // Scrolling to the cited slide is this format's half of \"take me there\". Keyed\n // on the slide NUMBER, so a reader who scrolls away while the same citation is\n // still active is not dragged back (the same trade the PDF column makes).\n const activeSlide = useMemo(() => {\n const active = highlights?.find((highlight) => highlight.id === activeHighlightId);\n if (!active || active.status !== \"resolved\" || !active.range || !deck.textIndex) {\n return undefined;\n }\n return spanAt(deck.textIndex, active.range[0])?.ref.slide;\n }, [highlights, activeHighlightId, deck.textIndex]);\n useEffect(() => {\n if (activeSlide === undefined) return;\n const position = deck.slides.findIndex((candidate) => candidate.index === activeSlide);\n if (position >= 0) goToSlide(position + 1);\n }, [activeSlide, deck.slides, goToSlide]);\n\n // The mark only exists once the cited slide is mounted, so the scroll waits\n // for the slide as well as for the id.\n useScrollActiveHighlightIntoView(listRef, activeHighlightId, slideNumber);\n\n return (\n // No viewport of its own: the slide chrome moved to the shell (ADR 0026), so\n // there is no fixed control above a scrolling body and `FileViewerContent`\n // is the one scroll boundary (`viewer-components.md`).\n <div\n ref={listRef}\n data-slot=\"pptx-slides\"\n className={cn(\"relative w-full\", className)}\n style={{ height: virtualizer.getTotalSize() }}\n >\n {virtualizer.getVirtualItems().map((item) => {\n const slide = deck.slides[item.index];\n if (!slide) return null;\n return (\n <div\n key={item.key}\n data-index={item.index}\n ref={virtualizer.measureElement}\n className=\"absolute inset-x-0 top-0\"\n style={{\n paddingBottom: SLIDE_GAP,\n transform: `translateY(${item.start - virtualizer.options.scrollMargin}px)`,\n }}\n >\n <PptxSlideView\n slide={slide}\n slideNumber={item.index + 1}\n baseHeadingLevel={baseHeadingLevel}\n marks={marks}\n starts={starts.get(slide.index)}\n />\n </div>\n );\n })}\n </div>\n );\n}\n\ninterface PptxSlideViewProps {\n slide: PptxSlide;\n /** 1-based position in the deck, which is what the reader is shown. */\n slideNumber: number;\n baseHeadingLevel: ProseHeadingLevel;\n marks: MarkRanges;\n /** Where each of this slide's lines begins in the projection. */\n starts?: Map<number, number>;\n}\n\n/** One slide: the 16:9 frame, its outline, and any speaker notes beneath it. */\nfunction PptxSlideView({\n slide,\n slideNumber,\n baseHeadingLevel,\n marks,\n starts,\n}: PptxSlideViewProps) {\n const { t, formatNumber } = useLocale();\n const isEmpty = !slide.title && slide.lines.length === 0;\n\n return (\n <>\n {/* The slide surface: `bg-card` above the pane's ground, `aspect-video`\n so a deck still reads as a deck — this is the shape of the thing,\n which is the one part of the layout an outline can honestly keep. */}\n {/* A slide that overflows its 16:9 frame scrolls, and a scrollable\n region with no focusable content cannot be reached by keyboard\n (WCAG 2.1.1). `tabIndex={0}` makes the slide itself the stop that\n arrow keys and Page Up/Down drive. This is the one nested scroller the\n rule allows: a fixed-ratio frame whose content may not fit it. */}\n <section\n tabIndex={0}\n data-slot=\"pptx-slide\"\n data-page={slideNumber}\n aria-label={t(\"viewer.pptx.slide\", {\n slide: formatNumber(slideNumber),\n })}\n className=\"border-border bg-card focus-ring mx-auto flex aspect-video w-full max-w-3xl flex-col gap-3 overflow-auto rounded-md border p-6 shadow-sm\"\n >\n {/* A slide title is the deck's top rung, so it sits at the host's base.\n The stand-in for an untitled slide is OUR text, not the deck's, so\n it is not in the projection and cannot be cited. */}\n <ProseHeading level={baseHeadingLevel}>\n {slide.title === undefined ? (\n t(\"viewer.pptx.untitled\")\n ) : (\n <MarkedText text={slide.title} marks={marks} start={starts?.get(PPTX_TITLE_LINE)} />\n )}\n </ProseHeading>\n {isEmpty ? (\n <p className=\"text-muted-foreground text-body\">{t(\"viewer.pptx.empty\")}</p>\n ) : (\n <ul className=\"text-body space-y-1.5\">\n {slide.lines.map((line, lineIndex) => (\n <li\n key={lineIndex}\n className={cn(\"whitespace-pre-wrap\", LEVEL_INDENT[Math.min(line.level, 3)])}\n >\n <MarkedText text={line.text} marks={marks} start={starts?.get(lineIndex)} />\n </li>\n ))}\n </ul>\n )}\n </section>\n\n {slide.notes && (\n <section\n aria-label={t(\"viewer.pptx.notes\")}\n className=\"mx-auto mt-3 w-full max-w-3xl space-y-1\"\n >\n <h3 className=\"text-meta text-muted-foreground\">{t(\"viewer.pptx.notes\")}</h3>\n <p className=\"text-body text-muted-foreground whitespace-pre-wrap\">\n <MarkedText text={slide.notes} marks={marks} start={starts?.get(PPTX_NOTES_LINE)} />\n </p>\n </section>\n )}\n </>\n );\n}\n\nconst adapterModule: AdapterModule = {\n manifest: pptxManifest,\n create: () => new PptxAdapter(),\n Renderer: PptxRenderer,\n};\n\nexport default adapterModule;\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import {
|
|
3
|
+
DRAWING_NS,
|
|
4
|
+
PPTX_LINE_SEPARATOR,
|
|
5
|
+
PPTX_NOTES_LINE,
|
|
6
|
+
PPTX_SLIDE_SEPARATOR,
|
|
7
|
+
PPTX_TITLE_LINE,
|
|
8
|
+
PRESENTATION_NS,
|
|
9
|
+
RELATIONSHIP_NS,
|
|
10
|
+
notesTarget,
|
|
11
|
+
orderSlidePaths,
|
|
12
|
+
parseNotes,
|
|
13
|
+
parseSlide,
|
|
14
|
+
resolveRelative,
|
|
15
|
+
slideNumber,
|
|
16
|
+
slidesToText,
|
|
17
|
+
slidesToTextWithMap
|
|
18
|
+
} from "../../chunk-US5KT3TR.js";
|
|
19
|
+
import "../../chunk-5YE2MUS2.js";
|
|
20
|
+
export {
|
|
21
|
+
DRAWING_NS,
|
|
22
|
+
PPTX_LINE_SEPARATOR,
|
|
23
|
+
PPTX_NOTES_LINE,
|
|
24
|
+
PPTX_SLIDE_SEPARATOR,
|
|
25
|
+
PPTX_TITLE_LINE,
|
|
26
|
+
PRESENTATION_NS,
|
|
27
|
+
RELATIONSHIP_NS,
|
|
28
|
+
notesTarget,
|
|
29
|
+
orderSlidePaths,
|
|
30
|
+
parseNotes,
|
|
31
|
+
parseSlide,
|
|
32
|
+
resolveRelative,
|
|
33
|
+
slideNumber,
|
|
34
|
+
slidesToText,
|
|
35
|
+
slidesToTextWithMap
|
|
36
|
+
};
|
|
37
|
+
//# sourceMappingURL=pptx-model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -2,14 +2,17 @@
|
|
|
2
2
|
"use client";
|
|
3
3
|
import {
|
|
4
4
|
textManifest
|
|
5
|
-
} from "
|
|
5
|
+
} from "../../chunk-ZAVXZRIO.js";
|
|
6
6
|
import {
|
|
7
|
-
toMarkRanges,
|
|
8
7
|
useScrollActiveHighlightIntoView
|
|
9
|
-
} from "
|
|
8
|
+
} from "../../chunk-IB4N72FA.js";
|
|
9
|
+
import {
|
|
10
|
+
toMarkRanges
|
|
11
|
+
} from "../../chunk-HOQZ7OPH.js";
|
|
10
12
|
import {
|
|
11
13
|
toViewerError
|
|
12
|
-
} from "
|
|
14
|
+
} from "../../chunk-JHFC2KBI.js";
|
|
15
|
+
import "../../chunk-CIT2HHRI.js";
|
|
13
16
|
|
|
14
17
|
// src/adapters/text/text-adapter.tsx
|
|
15
18
|
import { useMemo, useRef } from "react";
|
|
@@ -78,4 +81,4 @@ export {
|
|
|
78
81
|
TEXT_CHARACTER_LIMIT,
|
|
79
82
|
text_adapter_default as default
|
|
80
83
|
};
|
|
81
|
-
//# sourceMappingURL=text-adapter
|
|
84
|
+
//# sourceMappingURL=text-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/adapters/text/text-adapter.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * Plain-text adapter — the fallback for anything readable as UTF-8.\n *\n * Deliberately does NOT do SYNTAX highlighting — that is the separate `code`\n * adapter, registered at a higher priority for code extensions. Keeping them\n * apart means a 40 MB log file never drags a tokenizer into the bundle, and a\n * consumer can drop the tokenizer without losing plain text. (Citation and\n * find-in-document marks are a different thing entirely and DO render here.)\n *\n * Large files are truncated at load, not at render: putting ten million\n * characters into one `<pre>` locks the main thread with no way back. The cut is\n * reported so the UI can say so rather than silently lying about the content.\n */\n\nimport { useMemo, useRef } from \"react\";\n\nimport { MatchHighlight, cn, useLocale } from \"@elabs-ai/components-ui\";\nimport type { ResolvedFileSource } from \"@elabs-ai/components-ui\";\n\nimport { toViewerError } from \"../../core/errors\";\nimport { toMarkRanges } from \"../../core/highlight-marks\";\nimport { useScrollActiveHighlightIntoView } from \"../../core/use-highlight-scroll\";\nimport type {\n AdapterDocument,\n AdapterLoadContext,\n AdapterModule,\n AdapterRendererProps,\n FileAdapter,\n} from \"../../core/types\";\nimport { textManifest } from \"./text-manifest\";\n\n/**\n * Characters kept before truncation. ~2M is comfortably past any file a person\n * reads in a preview pane and still renders in one frame.\n */\nexport const TEXT_CHARACTER_LIMIT = 2_000_000;\n\nexport interface TextDocument extends AdapterDocument {\n kind: \"text\";\n text: string;\n /** Total characters in the file, when more than what is shown. */\n totalCharacters?: number;\n}\n\nclass TextAdapter implements FileAdapter {\n async load(source: ResolvedFileSource, context: AdapterLoadContext): Promise<TextDocument> {\n let raw: string;\n try {\n raw = await source.text(context.signal);\n } catch (error) {\n throw toViewerError(error, \"read-failed\", { fileName: source.name });\n }\n\n if (raw.length <= TEXT_CHARACTER_LIMIT) return { kind: \"text\", text: raw };\n return {\n kind: \"text\",\n text: raw.slice(0, TEXT_CHARACTER_LIMIT),\n totalCharacters: raw.length,\n // So a passage the shell cannot find is reported as \"past the part we\n // previewed\" rather than \"not in this document\" — different news.\n textTruncated: true,\n };\n }\n}\n\nfunction TextRenderer({\n document: doc,\n className,\n highlights,\n activeHighlightId,\n}: AdapterRendererProps) {\n const text = doc as TextDocument;\n const { t, formatNumber } = useLocale();\n const container = useRef<HTMLDivElement>(null);\n const { ranges, activeIndex } = useMemo(\n () => toMarkRanges(highlights, text.text.length),\n [highlights, text.text.length],\n );\n useScrollActiveHighlightIntoView(container, activeHighlightId);\n\n return (\n // No `overflow-auto` here: `FileViewerContent` is the scroll boundary, and\n // nesting a second one clips the text above the outer pane's own padding.\n <div ref={container} className={cn(\"flex flex-col gap-2\", className)}>\n {text.totalCharacters !== undefined && (\n // Not an error — the file is fine, we are showing part of it. A status\n // region, so AT hears it once rather than on every scroll.\n <p role=\"status\" className=\"text-meta text-muted-foreground\">\n {t(\"viewer.text.truncated\", {\n shown: formatNumber(text.text.length),\n total: formatNumber(text.totalCharacters),\n })}\n </p>\n )}\n <pre className=\"text-code font-mono break-words whitespace-pre-wrap\">\n {ranges.length === 0 ? (\n text.text\n ) : (\n // `MatchHighlight` renders the WHOLE string with the matches wrapped,\n // so nothing is lost when a highlight is present — a screen reader\n // still reads the file continuously.\n <MatchHighlight text={text.text} ranges={ranges} activeIndex={activeIndex} />\n )}\n </pre>\n </div>\n );\n}\n\nconst adapterModule: AdapterModule = {\n manifest: textManifest,\n create: () => new TextAdapter(),\n Renderer: TextRenderer,\n};\n\nexport default adapterModule;\n"],"mappings":";;;;;;;;;;;;;;;;;AAgBA,SAAS,SAAS,cAAc;AAEhC,SAAS,gBAAgB,IAAI,iBAAiB;AAmE1C,SAII,KAJJ;AAhDG,IAAM,uBAAuB;AASpC,IAAM,cAAN,MAAyC;AAAA,EACvC,MAAM,KAAK,QAA4B,SAAoD;AACzF,QAAI;AACJ,QAAI;AACF,YAAM,MAAM,OAAO,KAAK,QAAQ,MAAM;AAAA,IACxC,SAAS,OAAO;AACd,YAAM,cAAc,OAAO,eAAe,EAAE,UAAU,OAAO,KAAK,CAAC;AAAA,IACrE;AAEA,QAAI,IAAI,UAAU,qBAAsB,QAAO,EAAE,MAAM,QAAQ,MAAM,IAAI;AACzE,WAAO;AAAA,MACL,MAAM;AAAA,MACN,MAAM,IAAI,MAAM,GAAG,oBAAoB;AAAA,MACvC,iBAAiB,IAAI;AAAA;AAAA;AAAA,MAGrB,eAAe;AAAA,IACjB;AAAA,EACF;AACF;AAEA,SAAS,aAAa;AAAA,EACpB,UAAU;AAAA,EACV;AAAA,EACA;AAAA,EACA;AACF,GAAyB;AACvB,QAAM,OAAO;AACb,QAAM,EAAE,GAAG,aAAa,IAAI,UAAU;AACtC,QAAM,YAAY,OAAuB,IAAI;AAC7C,QAAM,EAAE,QAAQ,YAAY,IAAI;AAAA,IAC9B,MAAM,aAAa,YAAY,KAAK,KAAK,MAAM;AAAA,IAC/C,CAAC,YAAY,KAAK,KAAK,MAAM;AAAA,EAC/B;AACA,mCAAiC,WAAW,iBAAiB;AAE7D;AAAA;AAAA;AAAA,IAGE,qBAAC,SAAI,KAAK,WAAW,WAAW,GAAG,uBAAuB,SAAS,GAChE;AAAA,WAAK,oBAAoB;AAAA;AAAA,MAGxB,oBAAC,OAAE,MAAK,UAAS,WAAU,mCACxB,YAAE,yBAAyB;AAAA,QAC1B,OAAO,aAAa,KAAK,KAAK,MAAM;AAAA,QACpC,OAAO,aAAa,KAAK,eAAe;AAAA,MAC1C,CAAC,GACH;AAAA,MAEF,oBAAC,SAAI,WAAU,uDACZ,iBAAO,WAAW,IACjB,KAAK;AAAA;AAAA;AAAA;AAAA,QAKL,oBAAC,kBAAe,MAAM,KAAK,MAAM,QAAgB,aAA0B;AAAA,SAE/E;AAAA,OACF;AAAA;AAEJ;AAEA,IAAM,gBAA+B;AAAA,EACnC,UAAU;AAAA,EACV,QAAQ,MAAM,IAAI,YAAY;AAAA,EAC9B,UAAU;AACZ;AAEA,IAAO,uBAAQ;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -2,22 +2,26 @@
|
|
|
2
2
|
"use client";
|
|
3
3
|
import {
|
|
4
4
|
xlsxManifest
|
|
5
|
-
} from "
|
|
5
|
+
} from "../../chunk-7EASJBQM.js";
|
|
6
|
+
import {
|
|
7
|
+
SheetTable
|
|
8
|
+
} from "../../chunk-AK7F3IZE.js";
|
|
6
9
|
import {
|
|
7
|
-
SheetTable,
|
|
8
10
|
gridToText
|
|
9
|
-
} from "
|
|
10
|
-
import "
|
|
11
|
+
} from "../../chunk-TLCNGQKL.js";
|
|
12
|
+
import "../../chunk-GNBD53AT.js";
|
|
11
13
|
import {
|
|
12
14
|
spanAt
|
|
13
|
-
} from "
|
|
15
|
+
} from "../../chunk-5YE2MUS2.js";
|
|
16
|
+
import "../../chunk-IB4N72FA.js";
|
|
14
17
|
import {
|
|
15
18
|
toMarkRanges
|
|
16
|
-
} from "
|
|
19
|
+
} from "../../chunk-HOQZ7OPH.js";
|
|
17
20
|
import {
|
|
18
21
|
ViewerError,
|
|
19
22
|
toViewerError
|
|
20
|
-
} from "
|
|
23
|
+
} from "../../chunk-JHFC2KBI.js";
|
|
24
|
+
import "../../chunk-CIT2HHRI.js";
|
|
21
25
|
|
|
22
26
|
// src/adapters/xlsx/xlsx-adapter.tsx
|
|
23
27
|
import { cn, Tabs, TabsContent, TabsList, TabsTrigger, useLocale } from "@elabs-ai/components-ui";
|
|
@@ -164,4 +168,4 @@ export {
|
|
|
164
168
|
xlsx_adapter_default as default,
|
|
165
169
|
looksLikeWorkbook
|
|
166
170
|
};
|
|
167
|
-
//# sourceMappingURL=xlsx-adapter
|
|
171
|
+
//# sourceMappingURL=xlsx-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/adapters/xlsx/xlsx-adapter.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * Spreadsheet adapter — a workbook becomes sheets of rows, and rows become a\n * real `Table`.\n *\n * SheetJS is only ever asked for the DATA. Its `sheet_to_html` helper exists and\n * is not used: it returns a markup string with its own inline styling, which is\n * exactly the \"adapter emits HTML\" failure this package was built to avoid\n * (ADR 0024 §3). `sheet_to_json({ header: 1 })` gives raw cell values, which the\n * shared `SheetTable` renders with brand-ui components — so a spreadsheet\n * inherits the theme, the density dial and the keyboard semantics.\n *\n * **`xlsx` is an optional peer with two known advisories.** The npm build\n * (`0.18.5`) carries a prototype-pollution and a ReDoS advisory, both fixed only\n * in SheetJS's self-hosted CDN builds, which are not published to npm. That is a\n * documented, deliberate trade (`docs/CONSUMING.md` §6): a consumer who never\n * opens a spreadsheet never installs it and is never exposed. Treat any workbook\n * opened here as untrusted input.\n */\n\nimport type { ResolvedFileSource } from \"@elabs-ai/components-ui\";\nimport { cn, Tabs, TabsContent, TabsList, TabsTrigger, useLocale } from \"@elabs-ai/components-ui\";\nimport { useEffect, useMemo, useState } from \"react\";\n\nimport { gridToText, type GridRef } from \"../../components/grid-text\";\nimport { SheetTable } from \"../../components/sheet-table\";\nimport { ViewerError, toViewerError } from \"../../core/errors\";\nimport { toMarkRanges } from \"../../core/highlight-marks\";\nimport { spanAt, type TextIndex } from \"../../core/text-index\";\nimport type {\n AdapterDocument,\n AdapterLoadContext,\n AdapterModule,\n AdapterRendererProps,\n FileAdapter,\n} from \"../../core/types\";\nimport { xlsxManifest } from \"./xlsx-manifest\";\n\n/**\n * Rows rendered per sheet before truncation. Same bound, same reason, as the CSV\n * adapter: a preview pane is for looking, and a real analysis grid is\n * `DataTable` in `@elabs-ai/components-data`.\n */\nexport const XLSX_ROW_LIMIT = 5_000;\n\nexport interface SheetModel {\n /** The tab's own name, as authored in the workbook. */\n name: string;\n /** Header cells, taken from the sheet's first row. */\n columns: string[];\n /** Body rows, already capped at {@link XLSX_ROW_LIMIT}. */\n rows: string[][];\n /** Total body rows in the sheet, when more than what is shown. */\n totalRows?: number;\n}\n\nexport interface XlsxDocument extends AdapterDocument {\n kind: \"xlsx\";\n sheets: SheetModel[];\n /** Which sheet and row each stretch of `text` came from. */\n textIndex?: TextIndex<GridRef>;\n}\n\n/**\n * Does this look like a workbook container at all?\n *\n * SheetJS sniffs its input and will happily read arbitrary text as a one-column\n * CSV — so a damaged `.xlsx` comes back as a sheet holding one junk cell instead\n * of failing. That is worse than an error: it looks like the file opened. Both\n * containers this adapter claims have a fixed signature — OOXML/ODS is a zip\n * (`PK\\x03\\x04`), legacy `.xls` is a CFB compound file — so anything else is\n * rejected before SheetJS gets a chance to be helpful.\n */\nexport function looksLikeWorkbook(bytes: Uint8Array): boolean {\n const zip = [0x50, 0x4b, 0x03, 0x04];\n const cfb = [0xd0, 0xcf, 0x11, 0xe0];\n return [zip, cfb].some((signature) => signature.every((byte, index) => bytes[index] === byte));\n}\n\n/** SheetJS returns whatever the cell held — a date, a number, a formula result. */\nfunction toCellText(value: unknown): string {\n if (value === null || value === undefined) return \"\";\n if (value instanceof Date) return value.toISOString().slice(0, 10);\n return String(value);\n}\n\nclass XlsxAdapter implements FileAdapter {\n async load(source: ResolvedFileSource, context: AdapterLoadContext): Promise<XlsxDocument> {\n let buffer: ArrayBuffer;\n try {\n buffer = await source.bytes(context.signal);\n } catch (error) {\n throw toViewerError(error, \"read-failed\", { fileName: source.name });\n }\n\n const bytes = new Uint8Array(buffer);\n if (!looksLikeWorkbook(bytes)) {\n throw new ViewerError(\"parse-failed\", \"The file is not a spreadsheet container.\", {\n fileName: source.name,\n });\n }\n\n // Dynamic: the ONLY edge to the optional peer (heavy-deps:check).\n const XLSX = await import(\"xlsx\");\n\n try {\n // `cellDates` so a date column reads as a date rather than as Excel's\n // serial day number, which is meaningless to a person looking at it.\n const workbook = XLSX.read(bytes, { type: \"array\", cellDates: true });\n const sheets = workbook.SheetNames.map((name) => {\n const sheet = workbook.Sheets[name];\n const grid: unknown[][] = sheet\n ? XLSX.utils.sheet_to_json(sheet, { header: 1, blankrows: false, defval: \"\" })\n : [];\n const [header = [], ...body] = grid;\n const columns = header.map(toCellText);\n const rows = body.slice(0, XLSX_ROW_LIMIT).map((row) => row.map(toCellText));\n return body.length > XLSX_ROW_LIMIT\n ? { name, columns, rows, totalRows: body.length }\n : { name, columns, rows };\n });\n\n if (sheets.length === 0) {\n throw new ViewerError(\"parse-failed\", \"The workbook contains no sheets.\", {\n fileName: source.name,\n });\n }\n\n // The projection is built by the shared grid builder rather than joined by\n // hand, so the text a citation is addressed against and the map back into\n // the cells cannot drift apart (`core/text-index.ts`).\n const textIndex = gridToText(sheets);\n return {\n kind: \"xlsx\",\n sheets,\n pageCount: sheets.length,\n text: textIndex.text,\n textIndex,\n };\n } catch (error) {\n throw toViewerError(error, \"parse-failed\", { fileName: source.name });\n }\n }\n}\n\nfunction XlsxRenderer({\n document: doc,\n className,\n highlights,\n activeHighlightId,\n}: AdapterRendererProps) {\n const workbook = doc as XlsxDocument;\n const { t } = useLocale();\n const [first] = workbook.sheets;\n\n const marks = useMemo(\n () => toMarkRanges(highlights, workbook.text?.length ?? 0),\n [highlights, workbook.text],\n );\n\n // Where each row of each sheet begins in the projection. Built once per\n // document rather than per tab, because switching sheets must not re-walk the\n // spans of every OTHER sheet to answer a question about this one.\n const starts = useMemo(() => {\n const bySheet = new Map<number, Map<number, number>>();\n for (const span of workbook.textIndex?.spans ?? []) {\n let rows = bySheet.get(span.ref.sheet);\n if (!rows) {\n rows = new Map<number, number>();\n bySheet.set(span.ref.sheet, rows);\n }\n rows.set(span.ref.row, span.start);\n }\n return bySheet;\n }, [workbook.textIndex]);\n const rowStartFor = (sheet: number) => (row: number) => starts.get(sheet)?.get(row);\n\n // Turning to the cited SHEET is this format's half of \"take me there\" — the\n // row scroll inside it belongs to `SheetTable`. Keyed on the sheet NAME, so a\n // reader who switches tabs while the same citation stays active is not\n // dragged back (the PDF pager makes the same trade).\n const activeSheet = useMemo(() => {\n const active = highlights?.find((highlight) => highlight.id === activeHighlightId);\n if (!active || active.status !== \"resolved\" || !active.range || !workbook.textIndex) {\n return undefined;\n }\n return spanAt(workbook.textIndex, active.range[0])?.ref.sheet;\n }, [highlights, activeHighlightId, workbook.textIndex]);\n const activeSheetName =\n activeSheet === undefined ? undefined : workbook.sheets[activeSheet]?.name;\n\n const [pinned, setPinned] = useState<string>();\n useEffect(() => {\n if (activeSheetName !== undefined) setPinned(activeSheetName);\n }, [activeSheetName]);\n\n // A one-sheet workbook is the common case, and a tab strip holding a single\n // tab is chrome that decides nothing — so it is not rendered.\n if (!first) return null;\n if (workbook.sheets.length === 1) {\n return (\n <SheetTable\n columns={first.columns}\n rows={first.rows}\n totalRows={first.totalRows}\n className={className}\n marks={marks}\n rowStart={rowStartFor(0)}\n activeHighlightId={activeHighlightId}\n />\n );\n }\n\n return (\n <Tabs\n value={pinned ?? first.name}\n onValueChange={setPinned}\n className={cn(\"flex h-full min-h-0 flex-col gap-2\", className)}\n >\n <TabsList aria-label={t(\"viewer.sheet.tabs\")} className=\"shrink-0 self-start\">\n {workbook.sheets.map((sheet) => (\n <TabsTrigger key={sheet.name} value={sheet.name}>\n {sheet.name}\n </TabsTrigger>\n ))}\n </TabsList>\n {workbook.sheets.map((sheet, index) => (\n <TabsContent key={sheet.name} value={sheet.name} className=\"mt-0 min-h-0 flex-1\">\n <SheetTable\n columns={sheet.columns}\n rows={sheet.rows}\n totalRows={sheet.totalRows}\n marks={marks}\n rowStart={rowStartFor(index)}\n activeHighlightId={activeHighlightId}\n />\n </TabsContent>\n ))}\n </Tabs>\n );\n}\n\nconst adapterModule: AdapterModule = {\n manifest: xlsxManifest,\n create: () => new XlsxAdapter(),\n Renderer: XlsxRenderer,\n};\n\nexport default adapterModule;\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import {
|
|
3
|
+
findScrollHost
|
|
4
|
+
} from "./chunk-7PGGKQVY.js";
|
|
5
|
+
|
|
6
|
+
// src/core/use-viewport-size.ts
|
|
7
|
+
import { useEffect, useState } from "react";
|
|
8
|
+
function useViewportSize(ref) {
|
|
9
|
+
const [size, setSize] = useState();
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
const host = findScrollHost(ref.current) ?? ref.current?.parentElement ?? ref.current;
|
|
12
|
+
if (!host || typeof ResizeObserver === "undefined") return;
|
|
13
|
+
const measure = () => {
|
|
14
|
+
const { clientWidth: width, clientHeight: height } = host;
|
|
15
|
+
if (width === 0 || height === 0) return;
|
|
16
|
+
setSize(
|
|
17
|
+
(current) => current && Math.abs(current.width - width) < 1 && Math.abs(current.height - height) < 1 ? current : { width, height }
|
|
18
|
+
);
|
|
19
|
+
};
|
|
20
|
+
measure();
|
|
21
|
+
const observer = new ResizeObserver(measure);
|
|
22
|
+
observer.observe(host);
|
|
23
|
+
return () => observer.disconnect();
|
|
24
|
+
}, [ref]);
|
|
25
|
+
return size;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export {
|
|
29
|
+
useViewportSize
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=chunk-3HKQFFZW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/use-viewport-size.ts"],"sourcesContent":["\"use client\";\n\n/**\n * The size of the box a renderer scrolls inside, kept current.\n *\n * Needed because a fit-to-width scale is a function of the VIEWPORT, not of the\n * document: only the renderer can compute it, and only by measuring. Measures\n * the container rather than the window (`window.innerWidth` would be wrong the\n * moment a viewer sits in a resizable pane, a dialog or a split workspace).\n */\n\nimport { useEffect, useState, type RefObject } from \"react\";\n\nimport { findScrollHost } from \"./scroll-host\";\n\nexport interface ViewportSize {\n width: number;\n height: number;\n}\n\n/**\n * @param ref An element inside the scrolling box — typically the renderer's root.\n * @returns The scroll host's content size, or `undefined` until it is measured.\n */\nexport function useViewportSize(ref: RefObject<HTMLElement | null>): ViewportSize | undefined {\n const [size, setSize] = useState<ViewportSize>();\n\n useEffect(() => {\n const host = findScrollHost(ref.current) ?? ref.current?.parentElement ?? ref.current;\n if (!host || typeof ResizeObserver === \"undefined\") return;\n\n const measure = () => {\n const { clientWidth: width, clientHeight: height } = host;\n // A hidden pane measures zero. Re-fitting to a zero-width viewport would\n // rasterize a zero-width page and clear the canvas — and nothing would\n // measure again when the pane is shown, because its size did not change\n // from the observer's point of view. Ignoring the reading keeps the last\n // good one. (The guard extend-hq/ui's `useElementWidth` carries too.)\n if (width === 0 || height === 0) return;\n setSize((current) =>\n current && Math.abs(current.width - width) < 1 && Math.abs(current.height - height) < 1\n ? current\n : { width, height },\n );\n };\n\n measure();\n const observer = new ResizeObserver(measure);\n observer.observe(host);\n return () => observer.disconnect();\n }, [ref]);\n\n return size;\n}\n"],"mappings":";;;;;;AAWA,SAAS,WAAW,gBAAgC;AAa7C,SAAS,gBAAgB,KAA8D;AAC5F,QAAM,CAAC,MAAM,OAAO,IAAI,SAAuB;AAE/C,YAAU,MAAM;AACd,UAAM,OAAO,eAAe,IAAI,OAAO,KAAK,IAAI,SAAS,iBAAiB,IAAI;AAC9E,QAAI,CAAC,QAAQ,OAAO,mBAAmB,YAAa;AAEpD,UAAM,UAAU,MAAM;AACpB,YAAM,EAAE,aAAa,OAAO,cAAc,OAAO,IAAI;AAMrD,UAAI,UAAU,KAAK,WAAW,EAAG;AACjC;AAAA,QAAQ,CAAC,YACP,WAAW,KAAK,IAAI,QAAQ,QAAQ,KAAK,IAAI,KAAK,KAAK,IAAI,QAAQ,SAAS,MAAM,IAAI,IAClF,UACA,EAAE,OAAO,OAAO;AAAA,MACtB;AAAA,IACF;AAEA,YAAQ;AACR,UAAM,WAAW,IAAI,eAAe,OAAO;AAC3C,aAAS,QAAQ,IAAI;AACrB,WAAO,MAAM,SAAS,WAAW;AAAA,EACnC,GAAG,CAAC,GAAG,CAAC;AAER,SAAO;AACT;","names":[]}
|