@l1yp/file-viewer 0.1.1 → 0.1.3
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/README.md +1 -0
- package/dist/FileViewer.js +2 -0
- package/dist/FileViewer.js.map +1 -0
- package/dist/FileViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/FileViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/AppxViewer.js +2 -0
- package/dist/components/AppxViewer.js.map +1 -0
- package/dist/components/AppxViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/AppxViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/ArchiveBrowser.js +2 -0
- package/dist/components/ArchiveBrowser.js.map +1 -0
- package/dist/components/ArchiveBrowser.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/ArchiveBrowser.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/AudioViewer.js +2 -0
- package/dist/components/AudioViewer.js.map +1 -0
- package/dist/components/AudioViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/AudioViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/BinObjBrowser.js +2 -0
- package/dist/components/BinObjBrowser.js.map +1 -0
- package/dist/components/BinObjBrowser.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/BinObjBrowser.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/CertViewer.js +2 -0
- package/dist/components/CertViewer.js.map +1 -0
- package/dist/components/CertViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/CertViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/ClassViewer.js +2 -0
- package/dist/components/ClassViewer.js.map +1 -0
- package/dist/components/ClassViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/ClassViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/CodeBlock.js +2 -0
- package/dist/components/CodeBlock.js.map +1 -0
- package/dist/components/CodeBlock.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/CodeBlock.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/CodeView.js +2 -0
- package/dist/components/CodeView.js.map +1 -0
- package/dist/components/CodeView.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/CodeView.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/CsvViewer.js +2 -0
- package/dist/components/CsvViewer.js.map +1 -0
- package/dist/components/CsvViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/CsvViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/DicomViewer.js +2 -0
- package/dist/components/DicomViewer.js.map +1 -0
- package/dist/components/DicomViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/DicomViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/DocxNavigationPane.js +2 -0
- package/dist/components/DocxNavigationPane.js.map +1 -0
- package/dist/components/DocxNavigationPane.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/DocxNavigationPane.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/DocxViewer.js +2 -0
- package/dist/components/DocxViewer.js.map +1 -0
- package/dist/components/DocxViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/DocxViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/DrawioViewer.js +2 -0
- package/dist/components/DrawioViewer.js.map +1 -0
- package/dist/components/DrawioViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/DrawioViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/DxfViewer.js +2 -0
- package/dist/components/DxfViewer.js.map +1 -0
- package/dist/components/DxfViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/DxfViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/EbookViewer.js +2 -0
- package/dist/components/EbookViewer.js.map +1 -0
- package/dist/components/EbookViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/EbookViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/EmlViewer.js +2 -0
- package/dist/components/EmlViewer.js.map +1 -0
- package/dist/components/EmlViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/EmlViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/EplViewer.js +2 -0
- package/dist/components/EplViewer.js.map +1 -0
- package/dist/components/EplViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/EplViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/FileViewerBody.js +3 -1
- package/dist/components/FileViewerBody.js.map +1 -0
- package/dist/components/FileViewerBody.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/FileViewerBody.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/FileViewerPanel.js +2 -0
- package/dist/components/FileViewerPanel.js.map +1 -0
- package/dist/components/FileViewerPanel.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/FileViewerPanel.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/FontViewer.js +2 -0
- package/dist/components/FontViewer.js.map +1 -0
- package/dist/components/FontViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/FontViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/FrontmatterCard.js +2 -0
- package/dist/components/FrontmatterCard.js.map +1 -0
- package/dist/components/FrontmatterCard.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/FrontmatterCard.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/GeoViewer.js +2 -0
- package/dist/components/GeoViewer.js.map +1 -0
- package/dist/components/GeoViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/GeoViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/HarViewer.js +2 -0
- package/dist/components/HarViewer.js.map +1 -0
- package/dist/components/HarViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/HarViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/HexViewer.js +2 -0
- package/dist/components/HexViewer.js.map +1 -0
- package/dist/components/HexViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/HexViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/HprofViewer.js +2 -0
- package/dist/components/HprofViewer.js.map +1 -0
- package/dist/components/HprofViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/HprofViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/ImageLightbox.js +2 -0
- package/dist/components/ImageLightbox.js.map +1 -0
- package/dist/components/ImageLightbox.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/ImageLightbox.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/InlineText.js +2 -0
- package/dist/components/InlineText.js.map +1 -0
- package/dist/components/JksViewer.js +2 -0
- package/dist/components/JksViewer.js.map +1 -0
- package/dist/components/JksViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/JksViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/JsonlViewer.js +3 -1
- package/dist/components/JsonlViewer.js.map +1 -0
- package/dist/components/JsonlViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/JsonlViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/LogViewer.js +2 -0
- package/dist/components/LogViewer.js.map +1 -0
- package/dist/components/LogViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/LogViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/LrcViewer.js +2 -0
- package/dist/components/LrcViewer.js.map +1 -0
- package/dist/components/LrcViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/LrcViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/MarkdownTable.js +2 -0
- package/dist/components/MarkdownTable.js.map +1 -0
- package/dist/components/MarkdownTable.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/MarkdownTable.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/MarkdownText.js +2 -0
- package/dist/components/MarkdownText.js.map +1 -0
- package/dist/components/MhtViewer.js +2 -0
- package/dist/components/MhtViewer.js.map +1 -0
- package/dist/components/MhtViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/MhtViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/MindMapView.js +2 -0
- package/dist/components/MindMapView.js.map +1 -0
- package/dist/components/MindMapView.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/MindMapView.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/MissingDependency.js +2 -0
- package/dist/components/MissingDependency.js.map +1 -0
- package/dist/components/MissingDependency.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/MissingDependency.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/ModelViewer.js +2 -0
- package/dist/components/ModelViewer.js.map +1 -0
- package/dist/components/ModelViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/ModelViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/MsiViewer.js +2 -0
- package/dist/components/MsiViewer.js.map +1 -0
- package/dist/components/MsiViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/MsiViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/NotebookViewer.js +2 -0
- package/dist/components/NotebookViewer.js.map +1 -0
- package/dist/components/NotebookViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/NotebookViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/PdfNavigationPane.js +9 -0
- package/dist/components/PdfNavigationPane.js.map +1 -0
- package/dist/components/PdfNavigationPane.vue.d.ts +24 -0
- package/dist/components/PdfNavigationPane.vue_vue_type_script_setup_true_lang.js +149 -0
- package/dist/components/PdfNavigationPane.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/PdfViewer.js +3 -1
- package/dist/components/PdfViewer.js.map +1 -0
- package/dist/components/PdfViewer.vue.d.ts +0 -11
- package/dist/components/PdfViewer.vue_vue_type_script_setup_true_lang.js +133 -69
- package/dist/components/PdfViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/PptxNavigationPane.js +2 -0
- package/dist/components/PptxNavigationPane.js.map +1 -0
- package/dist/components/PptxNavigationPane.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/PptxNavigationPane.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/PptxViewer.js +3 -1
- package/dist/components/PptxViewer.js.map +1 -0
- package/dist/components/PptxViewer.vue_vue_type_script_setup_true_lang.js +55 -49
- package/dist/components/PptxViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/PsdViewer.js +2 -0
- package/dist/components/PsdViewer.js.map +1 -0
- package/dist/components/PsdViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/PsdViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/QmcAudioViewer.js +2 -0
- package/dist/components/QmcAudioViewer.js.map +1 -0
- package/dist/components/QmcAudioViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/QmcAudioViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/RasterViewer.js +2 -0
- package/dist/components/RasterViewer.js.map +1 -0
- package/dist/components/RasterViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/RasterViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/RtfViewer.js +2 -0
- package/dist/components/RtfViewer.js.map +1 -0
- package/dist/components/RtfViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/RtfViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/SevenZipBrowser.js +2 -0
- package/dist/components/SevenZipBrowser.js.map +1 -0
- package/dist/components/SevenZipBrowser.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/SevenZipBrowser.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/SqliteViewer.js +2 -0
- package/dist/components/SqliteViewer.js.map +1 -0
- package/dist/components/SqliteViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/SqliteViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/SubtitleViewer.js +2 -0
- package/dist/components/SubtitleViewer.js.map +1 -0
- package/dist/components/SubtitleViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/SubtitleViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/XlsxChart.js +2 -0
- package/dist/components/XlsxChart.js.map +1 -0
- package/dist/components/XlsxChart.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/XlsxChart.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/XlsxViewer.js +2 -0
- package/dist/components/XlsxViewer.js.map +1 -0
- package/dist/components/XlsxViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/XlsxViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/XmindViewer.js +2 -0
- package/dist/components/XmindViewer.js.map +1 -0
- package/dist/components/XmindViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/XmindViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/ZipBrowser.js +2 -0
- package/dist/components/ZipBrowser.js.map +1 -0
- package/dist/components/ZipBrowser.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/ZipBrowser.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/ZoomImage.js +3 -1
- package/dist/components/ZoomImage.js.map +1 -0
- package/dist/components/ZoomImage.vue_vue_type_script_setup_true_lang.js +82 -80
- package/dist/components/ZoomImage.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/epl/EplCodeBlock.js +2 -0
- package/dist/components/epl/EplCodeBlock.js.map +1 -0
- package/dist/components/epl/EplCodeBlock.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/epl/EplCodeBlock.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/epl/EplDoc.js +2 -0
- package/dist/components/epl/EplDoc.js.map +1 -0
- package/dist/components/epl/EplDoc.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/epl/EplDoc.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/epl/EplMethod.js +2 -0
- package/dist/components/epl/EplMethod.js.map +1 -0
- package/dist/components/epl/EplMethod.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/epl/EplMethod.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/epl/EplResource.js +2 -0
- package/dist/components/epl/EplResource.js.map +1 -0
- package/dist/components/epl/EplResource.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/epl/EplResource.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/epl/EplTree.js +2 -0
- package/dist/components/epl/EplTree.js.map +1 -0
- package/dist/components/epl/EplTree.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/epl/EplTree.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/epl/EplVarTable.js +2 -0
- package/dist/components/epl/EplVarTable.js.map +1 -0
- package/dist/components/epl/EplVarTable.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/epl/EplVarTable.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/epl/numbering.js +2 -0
- package/dist/components/epl/numbering.js.map +1 -0
- package/dist/components/icons/Icon.js +2 -0
- package/dist/components/icons/Icon.js.map +1 -0
- package/dist/components/icons/Icon.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/icons/Icon.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/icons/iconPaths.js +2 -0
- package/dist/components/icons/iconPaths.js.map +1 -0
- package/dist/components/optionalViewer.js +2 -0
- package/dist/components/optionalViewer.js.map +1 -0
- package/dist/components/ui/ClampBlock.js +2 -0
- package/dist/components/ui/ClampBlock.js.map +1 -0
- package/dist/composables/useElementNavigation.js +2 -0
- package/dist/composables/useElementNavigation.js.map +1 -0
- package/dist/composables/useFileViewer.js +2 -0
- package/dist/composables/useFileViewer.js.map +1 -0
- package/dist/composables/useLightbox.js +2 -0
- package/dist/composables/useLightbox.js.map +1 -0
- package/dist/composables/useLogRules.js +2 -0
- package/dist/composables/useLogRules.js.map +1 -0
- package/dist/lib/aes.js +2 -0
- package/dist/lib/aes.js.map +1 -0
- package/dist/lib/appx.js +2 -0
- package/dist/lib/appx.js.map +1 -0
- package/dist/lib/archiveHtml.js +2 -0
- package/dist/lib/archiveHtml.js.map +1 -0
- package/dist/lib/asn1.js +2 -0
- package/dist/lib/asn1.js.map +1 -0
- package/dist/lib/binobj.js +2 -0
- package/dist/lib/binobj.js.map +1 -0
- package/dist/lib/brotli.js +2 -0
- package/dist/lib/brotli.js.map +1 -0
- package/dist/lib/bzip2.js +2 -0
- package/dist/lib/bzip2.js.map +1 -0
- package/dist/lib/classfile.js +2 -0
- package/dist/lib/classfile.js.map +1 -0
- package/dist/lib/codePreview.js +2 -0
- package/dist/lib/codePreview.js.map +1 -0
- package/dist/lib/codefold.js +2 -0
- package/dist/lib/codefold.js.map +1 -0
- package/dist/lib/codeformat.js +2 -0
- package/dist/lib/codeformat.js.map +1 -0
- package/dist/lib/csv.js +2 -0
- package/dist/lib/csv.js.map +1 -0
- package/dist/lib/des.js +2 -0
- package/dist/lib/des.js.map +1 -0
- package/dist/lib/diagram.js +2 -0
- package/dist/lib/diagram.js.map +1 -0
- package/dist/lib/dicom.js +2 -0
- package/dist/lib/dicom.js.map +1 -0
- package/dist/lib/dicomCodecs.js +2 -0
- package/dist/lib/dicomCodecs.js.map +1 -0
- package/dist/lib/dicomRender.js +2 -0
- package/dist/lib/dicomRender.js.map +1 -0
- package/dist/lib/docxNavigation.js +2 -0
- package/dist/lib/docxNavigation.js.map +1 -0
- package/dist/lib/dxf.js +2 -0
- package/dist/lib/dxf.js.map +1 -0
- package/dist/lib/eml.js +2 -0
- package/dist/lib/eml.js.map +1 -0
- package/dist/lib/encoding.js +2 -0
- package/dist/lib/encoding.js.map +1 -0
- package/dist/lib/epl/code/codeDataParser.js +2 -0
- package/dist/lib/epl/code/codeDataParser.js.map +1 -0
- package/dist/lib/epl/code/codeLines.js +2 -0
- package/dist/lib/epl/code/codeLines.js.map +1 -0
- package/dist/lib/epl/code/textCode.js +2 -0
- package/dist/lib/epl/code/textCode.js.map +1 -0
- package/dist/lib/epl/eplDocument.js +2 -0
- package/dist/lib/epl/eplDocument.js.map +1 -0
- package/dist/lib/epl/imageList.js +2 -0
- package/dist/lib/epl/imageList.js.map +1 -0
- package/dist/lib/epl/index.js +2 -0
- package/dist/lib/epl/index.js.map +1 -0
- package/dist/lib/epl/io/binaryReader.js +2 -0
- package/dist/lib/epl/io/binaryReader.js.map +1 -0
- package/dist/lib/epl/io/encoding.js +2 -0
- package/dist/lib/epl/io/encoding.js.map +1 -0
- package/dist/lib/epl/model/eplSystemId.js +2 -0
- package/dist/lib/epl/model/eplSystemId.js.map +1 -0
- package/dist/lib/epl/names/eplLibData.js +2 -0
- package/dist/lib/epl/names/eplLibData.js.map +1 -0
- package/dist/lib/epl/names/idToNameMap.js +2 -0
- package/dist/lib/epl/names/idToNameMap.js.map +1 -0
- package/dist/lib/epl/names/libNameProvider.js +2 -0
- package/dist/lib/epl/names/libNameProvider.js.map +1 -0
- package/dist/lib/epl/program.js +2 -0
- package/dist/lib/epl/program.js.map +1 -0
- package/dist/lib/epl/projectFileReader.js +2 -0
- package/dist/lib/epl/projectFileReader.js.map +1 -0
- package/dist/lib/fb2.js +2 -0
- package/dist/lib/fb2.js.map +1 -0
- package/dist/lib/fileType.js +2 -0
- package/dist/lib/fileType.js.map +1 -0
- package/dist/lib/fonts.js +2 -0
- package/dist/lib/fonts.js.map +1 -0
- package/dist/lib/format.js +2 -0
- package/dist/lib/format.js.map +1 -0
- package/dist/lib/frontmatter.js +2 -0
- package/dist/lib/frontmatter.js.map +1 -0
- package/dist/lib/geo.js +2 -0
- package/dist/lib/geo.js.map +1 -0
- package/dist/lib/har.js +2 -0
- package/dist/lib/har.js.map +1 -0
- package/dist/lib/highlight.js +2 -0
- package/dist/lib/highlight.js.map +1 -0
- package/dist/lib/highlightLangs.js +2 -0
- package/dist/lib/highlightLangs.js.map +1 -0
- package/dist/lib/hprof.js +2 -0
- package/dist/lib/hprof.js.map +1 -0
- package/dist/lib/ipynb.js +2 -0
- package/dist/lib/ipynb.js.map +1 -0
- package/dist/lib/jks.js +2 -0
- package/dist/lib/jks.js.map +1 -0
- package/dist/lib/jsonl.js +2 -0
- package/dist/lib/jsonl.js.map +1 -0
- package/dist/lib/lrc.js +2 -0
- package/dist/lib/lrc.js.map +1 -0
- package/dist/lib/lzma.js +2 -0
- package/dist/lib/lzma.js.map +1 -0
- package/dist/lib/markdown/blocks.js +2 -0
- package/dist/lib/markdown/blocks.js.map +1 -0
- package/dist/lib/markdown/inline.js +2 -0
- package/dist/lib/markdown/inline.js.map +1 -0
- package/dist/lib/markdown/math.js +2 -0
- package/dist/lib/markdown/math.js.map +1 -0
- package/dist/lib/markdown/segmentize.js +2 -0
- package/dist/lib/markdown/segmentize.js.map +1 -0
- package/dist/lib/markdown/styles.js +2 -0
- package/dist/lib/markdown/styles.js.map +1 -0
- package/dist/lib/mht.js +2 -0
- package/dist/lib/mht.js.map +1 -0
- package/dist/lib/midi.js +2 -0
- package/dist/lib/midi.js.map +1 -0
- package/dist/lib/mime.js +2 -0
- package/dist/lib/mime.js.map +1 -0
- package/dist/lib/mindmap.js +2 -0
- package/dist/lib/mindmap.js.map +1 -0
- package/dist/lib/mobi.js +2 -0
- package/dist/lib/mobi.js.map +1 -0
- package/dist/lib/msi.js +2 -0
- package/dist/lib/msi.js.map +1 -0
- package/dist/lib/optionalDep.js +2 -0
- package/dist/lib/optionalDep.js.map +1 -0
- package/dist/lib/pdfNavigation.d.ts +11 -0
- package/dist/lib/pdfNavigation.js +32 -0
- package/dist/lib/pdfNavigation.js.map +1 -0
- package/dist/lib/pkcs12.js +2 -0
- package/dist/lib/pkcs12.js.map +1 -0
- package/dist/lib/pptxNavigation.js +2 -0
- package/dist/lib/pptxNavigation.js.map +1 -0
- package/dist/lib/qmc.js +2 -0
- package/dist/lib/qmc.js.map +1 -0
- package/dist/lib/rar.js +2 -0
- package/dist/lib/rar.js.map +1 -0
- package/dist/lib/rawPreview.js +2 -0
- package/dist/lib/rawPreview.js.map +1 -0
- package/dist/lib/rc2.js +2 -0
- package/dist/lib/rc2.js.map +1 -0
- package/dist/lib/resolveInput.js +2 -0
- package/dist/lib/resolveInput.js.map +1 -0
- package/dist/lib/rtf.js +2 -0
- package/dist/lib/rtf.js.map +1 -0
- package/dist/lib/sevenZip.js +2 -0
- package/dist/lib/sevenZip.js.map +1 -0
- package/dist/lib/sha1.js +2 -0
- package/dist/lib/sha1.js.map +1 -0
- package/dist/lib/sha256.js +2 -0
- package/dist/lib/sha256.js.map +1 -0
- package/dist/lib/speechAudio.js +2 -0
- package/dist/lib/speechAudio.js.map +1 -0
- package/dist/lib/sqlite.js +2 -0
- package/dist/lib/sqlite.js.map +1 -0
- package/dist/lib/subtitle.js +2 -0
- package/dist/lib/subtitle.js.map +1 -0
- package/dist/lib/tarball.js +2 -0
- package/dist/lib/tarball.js.map +1 -0
- package/dist/lib/viewerFormats.js +2 -0
- package/dist/lib/viewerFormats.js.map +1 -0
- package/dist/lib/viewerKind.js +2 -0
- package/dist/lib/viewerKind.js.map +1 -0
- package/dist/lib/x509.js +2 -0
- package/dist/lib/x509.js.map +1 -0
- package/dist/lib/xlsx.js +2 -0
- package/dist/lib/xlsx.js.map +1 -0
- package/dist/lib/xlsxCharts.js +2 -0
- package/dist/lib/xlsxCharts.js.map +1 -0
- package/dist/lib/xmind.js +2 -0
- package/dist/lib/xmind.js.map +1 -0
- package/dist/lib/zip.js +2 -0
- package/dist/lib/zip.js.map +1 -0
- package/dist/style.css +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"viewerKind.js","names":[],"sources":["../../src/lib/viewerKind.ts"],"sourcesContent":["/**\n * Maps a file (name / mime / size / URL) to a {@link ViewerFile} — the single source of truth for \"which\n * in-browser viewer renders this\", shared by:\n * - {@link FileChip} (the chip in a conversation): routes images/videos to the lightbox itself, then\n * delegates everything else here.\n * - the archive browsers (Zip / 7z / tar / rar): an entry, once extracted to a blob URL, is classified the\n * same way and handed to {@link FileViewerBody} — so a file INSIDE an archive previews with the very same\n * viewers as a top-level file (the previous archive detail view only handled text/image/office/dicom).\n *\n * Detection order mirrors FileChip's historical priority exactly (raster/ebook beat the image lightbox; the\n * textual specializations — har/subtitle/geo/csv/notebook/cert — beat the generic source route). The only\n * additions for the archive context are `image` / `video` (rendered inline in the body instead of the lightbox)\n * and a `binary` fall-through (an entry that matches no viewer and isn't text → a download card).\n */\nimport {\n ebookKind,\n fileExt,\n hljsLang,\n isAppxFile,\n isAudioFile,\n isBinaryObjFile,\n isCertFile,\n isCsvFile,\n isDicomFile,\n isDocxFile,\n isDxfFile,\n isEmlFile,\n isEplFile,\n isFontFile,\n isGeoFile,\n isHarFile,\n isHprofFile,\n isHtmlFile,\n isImageFile,\n isJavaClassFile,\n isJksFile,\n isJsonlFile,\n isLogFile,\n isMhtFile,\n isMindMapFile,\n isModelFile,\n isLrcFile,\n isMsiFile,\n isNotebookFile,\n isPdfFile,\n isQmcAudioFile,\n isPptxFile,\n isRarFile,\n isRtfFile,\n isSevenZipFile,\n isSqliteFile,\n isSubtitleFile,\n isSvgFile,\n isTarballFile,\n isTextFile,\n isVideoFile,\n isXlsxFile,\n isXmindFile,\n isZipFile,\n rasterImageKind,\n} from '@/lib/fileType'\nimport { isDiagramFile } from '@/lib/diagram'\nimport { looksLikeMindMap } from '@/lib/mindmap'\nimport type { ViewerFile } from '@/composables/useFileViewer'\n\nexport interface ClassifyOpts {\n /** Display name (becomes {@link ViewerFile.name}). */\n name: string\n /** Fetch / download URL — the real endpoint, or a `blob:` URL for an extracted archive entry. */\n url: string\n /** Provider mime when known; null for archive entries (extension-driven detection). */\n mime?: string | null\n /** Byte size when known (drives the large-file / log routing). */\n size?: number\n /** Name used for TYPE detection when it differs from the display name (e.g. a chip captioned \"16×16 PNG\"). */\n detectName?: string\n /** Fall-back hint when nothing matches and the extension is ambiguous (archive NUL-sniff says \"looks textual\"). */\n textHint?: boolean\n}\n\n/**\n * Classify a file into the {@link ViewerFile} the side viewer should render. Pure — no lightbox / fetching\n * side effects (FileChip handles the lightbox before calling this). Never returns image/video for the main\n * chip flow (FileChip routes those itself); archive callers DO get image/video/binary.\n */\nexport function classifyViewerFile(o: ClassifyOpts): ViewerFile {\n const name = o.name\n const dn = o.detectName ?? o.name\n const mime = o.mime ?? null\n const size = o.size\n const base = { name, url: o.url, size }\n\n // Decoding images (TIFF/PSD/HEIC/RAW) and e-books carry image/* mimes, so they must beat the image route.\n const raster = rasterImageKind(dn, mime)\n if (raster) return { ...base, lang: '', raster }\n const eb = ebookKind(dn, mime)\n if (eb) return { ...base, lang: '', ebook: eb }\n // Diagrams (.drawio/.excalidraw) are XML/JSON under the hood — render as SVG, with the raw text on the source\n // toggle (lang feeds that highlight: xml for drawio, json for excalidraw).\n const diagram = isDiagramFile(dn, mime)\n if (diagram) return { ...base, lang: diagram === 'excalidraw' ? 'json' : 'xml', diagram }\n // image / video render inline in the body (archive context); the main FileChip flow never reaches here for\n // those (it opens the lightbox first), so this only fires for archive entries.\n if (isImageFile(dn, mime)) return { ...base, lang: '', image: true }\n if (isVideoFile(dn, mime)) return { ...base, lang: '', video: true }\n // APPX/MSIX are zip containers, so they MUST be claimed before the generic zip route; MSI is an OLE\n // compound file (not a zip), grouped here with the other Windows installer/package formats.\n if (isAppxFile(dn, mime)) return { ...base, lang: '', appx: true }\n if (isMsiFile(dn, mime)) return { ...base, lang: '', msi: true }\n // XMind is a zip container → claim it before the generic zip route (renders as a mind map, not an archive).\n if (isXmindFile(dn, mime)) return { ...base, lang: '', xmind: true }\n if (isZipFile(dn, mime)) return { ...base, lang: '', zip: true }\n if (isSevenZipFile(dn, mime)) return { ...base, lang: '', sevenZip: true }\n if (isTarballFile(dn, mime)) return { ...base, lang: '', archive: 'tar' }\n if (isRarFile(dn, mime)) return { ...base, lang: '', archive: 'rar' }\n if (isDicomFile(dn, mime)) return { ...base, lang: '', dicom: true }\n if (isBinaryObjFile(dn, mime)) return { ...base, lang: '', binobj: true }\n if (isEplFile(dn, mime)) return { ...base, lang: '', epl: true }\n if (isJavaClassFile(dn, mime)) return { ...base, lang: '', javaClass: true }\n if (isHprofFile(dn, mime)) return { ...base, lang: '', hprof: true }\n if (isDocxFile(dn, mime)) return { ...base, lang: '', docx: true }\n if (isXlsxFile(dn, mime)) return { ...base, lang: '', xlsx: true }\n if (isPptxFile(dn, mime)) return { ...base, lang: '', pptx: true }\n if (isRtfFile(dn, mime)) return { ...base, lang: '', rtf: true }\n if (isPdfFile(dn, mime)) return { ...base, lang: '', pdf: true }\n if (isMindMapFile(dn, mime)) return { ...base, lang: hljsLang(dn, mime), mindmap: true }\n // DXF is textual (ASCII CAD) → claim it before the generic text route so it renders as a drawing.\n if (isDxfFile(dn, mime)) return { ...base, lang: '', dxf: true }\n if (isMhtFile(dn, mime)) return { ...base, lang: '', mht: true }\n if (isEmlFile(dn, mime)) return { ...base, lang: '', eml: true }\n if (isHarFile(dn, mime)) return { ...base, lang: '', har: true }\n if (isSqliteFile(dn, mime)) return { ...base, lang: '', sqlite: true }\n if (isSubtitleFile(dn, mime)) return { ...base, lang: '', subtitle: true }\n if (isGeoFile(dn, mime)) return { ...base, lang: hljsLang(dn, mime), geo: true }\n // QQ Music encrypted audio (.mflac/.mgg/…) → decrypt + play; LRC lyrics (.lrc) → synced lyric list. LRC must\n // beat the generic text route (its viewer self-fetches so it can sniff the usual GBK encoding).\n if (isQmcAudioFile(dn, mime)) return { ...base, lang: '', qmc: true }\n if (isLrcFile(dn, mime)) return { ...base, lang: '', lrc: true }\n if (isAudioFile(dn, mime)) return { ...base, lang: '', audio: true }\n // Logs / large text before the generic text + csv routes (a huge CSV streams through the log viewer).\n if (isLogFile(dn, mime, size)) return { ...base, lang: '', log: true }\n if (isCsvFile(dn, mime)) return { ...base, lang: '', csv: true }\n if (isJsonlFile(dn, mime)) return { ...base, lang: 'json', jsonl: true }\n if (isModelFile(dn, mime)) return { ...base, lang: '', model: true }\n if (isFontFile(dn, mime)) return { ...base, lang: '', font: true }\n if (isNotebookFile(dn, mime)) return { ...base, lang: '', ipynb: true }\n // Keystores (JKS/JCEKS + PKCS#12) before the cert route — the viewer sniffs the bytes to pick the parser.\n if (isJksFile(dn, mime)) return { ...base, lang: '', jks: true }\n if (isCertFile(dn, mime)) return { ...base, lang: '', cert: true }\n // SVG / HTML get a render/source toggle; plain text/code falls to the source view.\n if (isSvgFile(dn, mime)) return { ...base, lang: hljsLang(dn, mime), svg: true }\n if (isHtmlFile(dn, mime)) return { ...base, lang: hljsLang(dn, mime), html: true }\n if (isTextFile(dn, mime) || o.textHint) return { ...base, lang: hljsLang(dn, mime) }\n // Nothing matched and it isn't text → an unpreviewable binary (archive entries only; FileChip never opens these).\n return { ...base, lang: '', binary: true }\n}\n\n// Common image/video/audio MIME by extension — used when an archive browser wraps extracted entry bytes in a\n// Blob URL, so an <img>/<video>/<audio> reliably decodes it (an untyped blob makes the browser guess). Returns\n// '' for non-media (text/office/etc. sub-viewers fetch the bytes themselves, where the blob type is irrelevant).\nconst MEDIA_MIME: Record<string, string> = {\n png: 'image/png',\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n jfif: 'image/jpeg',\n gif: 'image/gif',\n webp: 'image/webp',\n bmp: 'image/bmp',\n ico: 'image/x-icon',\n avif: 'image/avif',\n apng: 'image/apng',\n svg: 'image/svg+xml',\n mp4: 'video/mp4',\n m4v: 'video/mp4',\n webm: 'video/webm',\n ogv: 'video/ogg',\n mov: 'video/quicktime',\n mkv: 'video/x-matroska',\n mp3: 'audio/mpeg',\n wav: 'audio/wav',\n flac: 'audio/flac',\n ogg: 'audio/ogg',\n oga: 'audio/ogg',\n m4a: 'audio/mp4',\n aac: 'audio/aac',\n opus: 'audio/opus',\n}\n\n/** Best-effort media MIME for an extracted archive entry's Blob (see {@link MEDIA_MIME}); '' when not media. */\nexport function mediaMimeForName(name: string): string {\n return MEDIA_MIME[fileExt(name)] ?? ''\n}\n\n/**\n * Does this file render WITHOUT the panel-loaded text — i.e. a self-fetching sub-viewer (it pulls its own\n * bytes), or an image/video/binary (rendered straight from the URL)? When true the panel skips the text\n * fetch and hides the copy/refresh actions; when false the content is built from fetched text (markdown /\n * html / svg / subtitle / geo / csv / mind-map / source) or inline `content`.\n */\nexport function skipsTextLoad(file: ViewerFile | null): boolean {\n if (!file) return false\n return !!(\n file.zip ||\n file.sevenZip ||\n file.xmind ||\n file.dxf ||\n file.archive ||\n file.binobj ||\n file.epl ||\n file.javaClass ||\n file.hprof ||\n file.msi ||\n file.appx ||\n file.dicom ||\n file.docx ||\n file.xlsx ||\n file.pptx ||\n file.rtf ||\n file.pdf ||\n file.eml ||\n file.mht ||\n file.har ||\n file.sqlite ||\n file.cert ||\n file.jks ||\n file.font ||\n file.ipynb ||\n file.audio ||\n file.qmc ||\n file.lrc ||\n file.log ||\n file.model ||\n file.raster ||\n file.ebook ||\n file.image ||\n file.video ||\n file.binary ||\n file.hex\n )\n}\n\n/**\n * Can this file be shown both rendered and as source (so the header offers an eye/code toggle)? True for\n * markdown / html / svg / subtitle / geo / csv, and for a mind map only once the loaded text actually looks\n * like one (a non-mind-map `.mm` reads as plain source, no toggle).\n */\nexport function canToggleView(file: ViewerFile | null, text: string): boolean {\n if (!file) return false\n if (\n file.lang === 'markdown' ||\n file.html === true ||\n file.svg === true ||\n file.subtitle === true ||\n file.geo === true ||\n file.csv === true ||\n file.jsonl === true ||\n file.diagram != null\n ) {\n return true\n }\n if (file.mindmap === true) return looksLikeMindMap(text, fileExt(file.name))\n return false\n}\n"],"mappings":"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"}
|
package/dist/lib/x509.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"x509.js","names":[],"sources":["../../src/lib/x509.ts"],"sourcesContent":["/**\n * Zero-dependency X.509 / PKCS#10 reading on top of the ASN.1 decoder (lib/asn1.ts), for the certificate\n * viewer (CertViewer.vue). It extracts the human-meaningful fields — subject / issuer, validity, serial,\n * signature + public-key algorithm, SANs, key usages, CA flag — from a DER certificate or CSR, and splits a\n * PEM file into its blocks. This is a READ-ONLY visualizer: it does NOT verify signatures or trust chains, so\n * a malformed or untrusted file is shown, not rejected. Anything it can't map to a friendly field still shows\n * in the raw ASN.1 structure tree the viewer renders alongside.\n */\n\nimport {\n asn1String,\n asn1Time,\n bytesToHex,\n decodeAsn1,\n oidToString,\n TAG,\n type Asn1Node,\n} from './asn1'\n\n// OID → short name. Covers the attribute types, algorithms, curves and extensions seen on real certificates;\n// unknown OIDs fall back to their dotted form (see oidName).\nconst OID_NAMES: Record<string, string> = {\n // Distinguished-name attribute types\n '2.5.4.3': 'CN',\n '2.5.4.4': 'SN',\n '2.5.4.5': 'serialNumber',\n '2.5.4.6': 'C',\n '2.5.4.7': 'L',\n '2.5.4.8': 'ST',\n '2.5.4.9': 'street',\n '2.5.4.10': 'O',\n '2.5.4.11': 'OU',\n '2.5.4.12': 'title',\n '2.5.4.42': 'GN',\n '2.5.4.43': 'initials',\n '1.2.840.113549.1.9.1': 'E',\n '0.9.2342.19200300.100.1.25': 'DC',\n '0.9.2342.19200300.100.1.1': 'UID',\n // Signature / public-key algorithms\n '1.2.840.113549.1.1.1': 'RSA',\n '1.2.840.113549.1.1.5': 'sha1WithRSA',\n '1.2.840.113549.1.1.11': 'sha256WithRSA',\n '1.2.840.113549.1.1.12': 'sha384WithRSA',\n '1.2.840.113549.1.1.13': 'sha512WithRSA',\n '1.2.840.113549.1.1.10': 'RSASSA-PSS',\n '1.2.840.10045.2.1': 'EC',\n '1.2.840.10045.4.3.2': 'ecdsa-with-SHA256',\n '1.2.840.10045.4.3.3': 'ecdsa-with-SHA384',\n '1.2.840.10045.4.3.4': 'ecdsa-with-SHA512',\n '1.3.101.112': 'Ed25519',\n '1.3.101.113': 'Ed448',\n // EC named curves\n '1.2.840.10045.3.1.7': 'P-256 (secp256r1)',\n '1.3.132.0.34': 'P-384 (secp384r1)',\n '1.3.132.0.35': 'P-521 (secp521r1)',\n '1.3.132.0.10': 'secp256k1',\n // Certificate extensions\n '2.5.29.14': 'subjectKeyIdentifier',\n '2.5.29.15': 'keyUsage',\n '2.5.29.17': 'subjectAltName',\n '2.5.29.19': 'basicConstraints',\n '2.5.29.31': 'cRLDistributionPoints',\n '2.5.29.32': 'certificatePolicies',\n '2.5.29.35': 'authorityKeyIdentifier',\n '2.5.29.37': 'extKeyUsage',\n '1.3.6.1.5.5.7.1.1': 'authorityInfoAccess',\n // Extended key usages\n '1.3.6.1.5.5.7.3.1': 'serverAuth',\n '1.3.6.1.5.5.7.3.2': 'clientAuth',\n '1.3.6.1.5.5.7.3.3': 'codeSigning',\n '1.3.6.1.5.5.7.3.4': 'emailProtection',\n '1.3.6.1.5.5.7.3.8': 'timeStamping',\n}\n\nexport function oidName(oid: string): string {\n return OID_NAMES[oid] ?? oid\n}\n\nexport interface RDN {\n oid: string\n name: string\n value: string\n}\n\nexport type CertKind = 'certificate' | 'request' | 'key' | 'crl' | 'pkcs12' | 'pkcs7' | 'unknown'\n\nexport interface CertSummary {\n ok: boolean\n kind: CertKind\n label: string\n subject: RDN[]\n issuer: RDN[]\n subjectCN?: string\n issuerCN?: string\n serialHex?: string\n version?: number\n sigAlg?: string\n pubKeyAlg?: string\n pubKeyBits?: number\n pubKeyCurve?: string\n notBefore?: string\n notAfter?: string\n notBeforeMs?: number\n notAfterMs?: number\n sans: string[]\n keyUsages: string[]\n extKeyUsages: string[]\n isCa?: boolean\n pathLen?: number\n der: Uint8Array\n fingerprintSha256?: string // hex, filled in asynchronously by the viewer\n note?: string\n}\n\nexport interface PemBlock {\n label: string\n der: Uint8Array\n}\n\nfunction b64ToBytes(b64: string): Uint8Array {\n const bin = atob(b64)\n const out = new Uint8Array(bin.length)\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i)\n return out\n}\n\n/** Split a PEM document into its `-----BEGIN X-----`…`-----END X-----` blocks (base64 → DER bytes). */\nexport function parsePemBlocks(text: string): PemBlock[] {\n const blocks: PemBlock[] = []\n const re = /-----BEGIN ([^-]+)-----([\\s\\S]*?)-----END \\1-----/g\n let m: RegExpExecArray | null\n while ((m = re.exec(text)) !== null) {\n const b64 = m[2].replace(/[^A-Za-z0-9+/=]/g, '')\n if (!b64) continue\n try {\n blocks.push({ label: m[1].trim(), der: b64ToBytes(b64) })\n } catch {\n /* malformed base64 — skip this block */\n }\n }\n return blocks\n}\n\n/** Does this text look like a PEM document (has at least one BEGIN marker)? */\nexport function looksLikePem(text: string): boolean {\n return /-----BEGIN [^-]+-----/.test(text)\n}\n\nfunction emptySummary(label: string, kind: CertKind, der: Uint8Array): CertSummary {\n return {\n ok: false,\n kind,\n label,\n subject: [],\n issuer: [],\n sans: [],\n keyUsages: [],\n extKeyUsages: [],\n der,\n }\n}\n\nfunction intValue(node: Asn1Node): number {\n let v = 0\n for (const b of node.content) v = v * 256 + b\n return v\n}\n\nfunction stripLeadingZero(bytes: Uint8Array): Uint8Array {\n return bytes.length > 1 && bytes[0] === 0 ? bytes.subarray(1) : bytes\n}\n\nfunction cnOf(rdns: RDN[]): string | undefined {\n return rdns.find((r) => r.name === 'CN')?.value\n}\n\nfunction algName(algId: Asn1Node | undefined): string {\n const oidNode = algId?.children[0]\n return oidNode ? oidName(oidToString(oidNode.content)) : ''\n}\n\n// Name ::= SEQUENCE OF RelativeDistinguishedName (SET OF AttributeTypeAndValue { type OID, value ANY }).\nfunction parseName(name: Asn1Node | undefined): RDN[] {\n const out: RDN[] = []\n if (!name) return out\n for (const rdn of name.children) {\n for (const atv of rdn.children) {\n const oidNode = atv.children[0]\n const valNode = atv.children[1]\n if (!oidNode || !valNode) continue\n const oid = oidToString(oidNode.content)\n out.push({ oid, name: oidName(oid), value: asn1String(valNode) })\n }\n }\n return out\n}\n\n// SubjectPublicKeyInfo ::= SEQUENCE { algorithm AlgorithmIdentifier, subjectPublicKey BIT STRING }.\nfunction applyPubKey(summary: CertSummary, spki: Asn1Node | undefined): void {\n const algId = spki?.children[0]\n const bitStr = spki?.children[1]\n const algOidNode = algId?.children[0]\n if (!algOidNode) return\n const algOid = oidToString(algOidNode.content)\n summary.pubKeyAlg = oidName(algOid)\n if (algOid === '1.2.840.113549.1.1.1' && bitStr) {\n // RSA: BIT STRING = [unusedBits byte][DER RSAPublicKey SEQUENCE { modulus INTEGER, exponent INTEGER }].\n const seq = decodeAsn1(bitStr.content.subarray(1))[0]\n const modulus = seq?.children[0]\n if (modulus) summary.pubKeyBits = stripLeadingZero(modulus.content).length * 8\n } else if (algOid === '1.2.840.10045.2.1') {\n // EC: AlgorithmIdentifier.parameters is the named-curve OID.\n const params = algId?.children[1]\n if (params) {\n summary.pubKeyCurve = oidName(oidToString(params.content))\n const m = summary.pubKeyCurve.match(/(\\d{3})/)\n if (m) summary.pubKeyBits = parseInt(m[1], 10)\n }\n } else if (algOid === '1.3.101.112') {\n summary.pubKeyBits = 256 // Ed25519\n }\n}\n\nconst KEY_USAGE = [\n 'digitalSignature',\n 'nonRepudiation',\n 'keyEncipherment',\n 'dataEncipherment',\n 'keyAgreement',\n 'keyCertSign',\n 'cRLSign',\n 'encipherOnly',\n 'decipherOnly',\n]\n\nfunction parseKeyUsage(bitStr: Asn1Node): string[] {\n const content = bitStr.content\n if (content.length < 2) return []\n const bits = content.subarray(1)\n const total = bits.length * 8 - content[0]\n const out: string[] = []\n for (let i = 0; i < total && i < KEY_USAGE.length; i++) {\n if (bits[Math.floor(i / 8)] & (0x80 >> (i % 8))) out.push(KEY_USAGE[i])\n }\n return out\n}\n\nfunction ipFromBytes(b: Uint8Array): string {\n if (b.length === 4) return Array.from(b).join('.')\n if (b.length === 16) {\n const parts: string[] = []\n for (let i = 0; i < 16; i += 2) parts.push(((b[i] << 8) | b[i + 1]).toString(16))\n return parts.join(':')\n }\n return bytesToHex(b)\n}\n\n// SubjectAltName ::= SEQUENCE OF GeneralName (context-tagged: [1]email [2]DNS [6]URI [7]IP, the common ones).\nfunction parseSan(seq: Asn1Node): string[] {\n const out: string[] = []\n for (const gn of seq.children) {\n if (gn.tagClass !== 2) continue\n const txt = new TextDecoder('latin1').decode(gn.content)\n if (gn.tagNumber === 2) out.push(`DNS:${txt}`)\n else if (gn.tagNumber === 1) out.push(`email:${txt}`)\n else if (gn.tagNumber === 6) out.push(`URI:${txt}`)\n else if (gn.tagNumber === 7) out.push(`IP:${ipFromBytes(gn.content)}`)\n }\n return out\n}\n\nfunction applyExtensions(summary: CertSummary, extSeq: Asn1Node): void {\n for (const ext of extSeq.children) {\n const oidNode = ext.children[0]\n if (!oidNode) continue\n const oid = oidToString(oidNode.content)\n const valNode = ext.children[ext.children.length - 1] // extnValue OCTET STRING (after optional critical)\n const inner = valNode ? decodeAsn1(valNode.content)[0] : undefined\n if (!inner) continue\n if (oid === '2.5.29.17') {\n summary.sans = parseSan(inner)\n } else if (oid === '2.5.29.19') {\n const ca = inner.children.find((c) => c.tagNumber === TAG.BOOLEAN)\n summary.isCa = ca ? ca.content[0] !== 0 : false\n const pl = inner.children.find((c) => c.tagNumber === TAG.INTEGER)\n if (pl) summary.pathLen = intValue(pl)\n } else if (oid === '2.5.29.37') {\n for (const eku of inner.children) summary.extKeyUsages.push(oidName(oidToString(eku.content)))\n } else if (oid === '2.5.29.15') {\n summary.keyUsages = parseKeyUsage(inner)\n }\n }\n}\n\n/** Parse a DER X.509 Certificate into its friendly fields (best effort; `ok=false` + `note` on failure). */\nexport function parseCertificate(der: Uint8Array, label = 'CERTIFICATE'): CertSummary {\n const summary = emptySummary(label, 'certificate', der)\n try {\n const top = decodeAsn1(der)[0]\n const tbs = top?.children[0]\n if (!top || top.tagNumber !== TAG.SEQUENCE || !tbs) {\n summary.note = '不是有效的 X.509 证书结构'\n return summary\n }\n const c = tbs.children\n let idx = 0\n if (c[idx] && c[idx].tagClass === 2 && c[idx].tagNumber === 0) {\n const vint = c[idx].children[0]\n summary.version = vint ? intValue(vint) + 1 : 1\n idx++\n } else {\n summary.version = 1\n }\n if (c[idx]) summary.serialHex = bytesToHex(stripLeadingZero(c[idx++].content), ':')\n if (c[idx]) summary.sigAlg = algName(c[idx++])\n if (c[idx]) summary.issuer = parseName(c[idx++])\n if (c[idx]) {\n const nb = c[idx].children[0] && asn1Time(c[idx].children[0])\n const na = c[idx].children[1] && asn1Time(c[idx].children[1])\n if (nb) {\n summary.notBefore = nb.text\n summary.notBeforeMs = nb.ms\n }\n if (na) {\n summary.notAfter = na.text\n summary.notAfterMs = na.ms\n }\n idx++\n }\n if (c[idx]) summary.subject = parseName(c[idx++])\n if (c[idx]) applyPubKey(summary, c[idx++])\n const extWrap = c.find((n) => n.tagClass === 2 && n.tagNumber === 3)\n if (extWrap && extWrap.children[0]) applyExtensions(summary, extWrap.children[0])\n summary.subjectCN = cnOf(summary.subject)\n summary.issuerCN = cnOf(summary.issuer)\n summary.ok = true\n } catch (e) {\n summary.note = e instanceof Error ? e.message : String(e)\n }\n return summary\n}\n\n/** Parse a DER PKCS#10 CertificationRequest (CSR): subject + public key (best effort). */\nexport function parseCsr(der: Uint8Array, label = 'CERTIFICATE REQUEST'): CertSummary {\n const summary = emptySummary(label, 'request', der)\n try {\n const cri = decodeAsn1(der)[0]?.children[0]\n if (!cri) {\n summary.note = '不是有效的 PKCS#10 请求结构'\n return summary\n }\n // CertificationRequestInfo ::= SEQUENCE { version, subject Name, subjectPKInfo, attributes [0] }\n summary.subject = parseName(cri.children[1])\n applyPubKey(summary, cri.children[2])\n summary.subjectCN = cnOf(summary.subject)\n summary.ok = true\n } catch (e) {\n summary.note = e instanceof Error ? e.message : String(e)\n }\n return summary\n}\n\n/** Pick a parser from a PEM block label (CERTIFICATE / CERTIFICATE REQUEST / *KEY* / *CRL* / fallback). */\nexport function summarizeBlock(label: string, der: Uint8Array): CertSummary {\n const up = label.toUpperCase()\n if (up.includes('CERTIFICATE REQUEST')) return parseCsr(der, label)\n if (up.includes('CERTIFICATE')) return parseCertificate(der, label)\n if (up.includes('CRL')) {\n const s = emptySummary(label, 'crl', der)\n s.ok = true\n s.note = 'X.509 证书吊销列表(CRL)—— 展开下方结构树查看条目。'\n return s\n }\n if (up.includes('KEY')) {\n const s = emptySummary(label, 'key', der)\n s.ok = true\n // PKCS#8 PrivateKeyInfo / SubjectPublicKeyInfo both start with an AlgorithmIdentifier we can name.\n try {\n const top = decodeAsn1(der)[0]\n const algId = top?.children.find((n) => n.tagNumber === TAG.SEQUENCE)\n summarizeKeyAlg(s, algId)\n } catch {\n /* leave the algorithm unset */\n }\n s.note = up.includes('PUBLIC')\n ? '公钥文件 —— 仅展示算法与结构,不含私密材料。'\n : '私钥文件 —— 仅展示算法与结构,密钥材料请勿外泄。'\n return s\n }\n // Unknown label — try a certificate, else just mark the structure as inspectable.\n const cert = parseCertificate(der, label)\n if (cert.ok) return cert\n const s = emptySummary(label, 'unknown', der)\n s.ok = true\n return s\n}\n\nfunction summarizeKeyAlg(s: CertSummary, algId: Asn1Node | undefined): void {\n const oidNode = algId?.children[0]\n if (oidNode && oidNode.tagNumber === TAG.OID) s.pubKeyAlg = oidName(oidToString(oidNode.content))\n}\n\n/**\n * Best-effort algorithm / size read from a DER PKCS#8 PrivateKeyInfo (never throws) — shared by the keystore\n * viewers (lib/jks.ts, lib/pkcs12.ts) to label a decrypted private key. Returns the friendly algorithm name\n * plus, for RSA, the modulus bit-length, and for EC, the named curve (+ its bit size when the name encodes it).\n */\nexport function describePrivateKey(pkcs8: Uint8Array): {\n algorithm: string\n bits?: number\n curve?: string\n} {\n try {\n // PrivateKeyInfo ::= SEQUENCE { version INTEGER, privateKeyAlgorithm AlgorithmIdentifier, privateKey OCTET STRING }\n const top = decodeAsn1(pkcs8)[0]\n const algId = top?.children[1]\n const oidNode = algId?.children[0]\n const oid = oidNode ? oidToString(oidNode.content) : ''\n const res: { algorithm: string; bits?: number; curve?: string } = {\n algorithm: oid ? oidName(oid) : '未知',\n }\n if (oid === '1.2.840.113549.1.1.1') {\n // RSA: privateKey OCTET STRING wraps an RSAPrivateKey SEQUENCE { version, modulus INTEGER, ... }.\n const pk = top?.children[2]\n const rsa = pk ? decodeAsn1(pk.content)[0] : undefined\n const modulus = rsa?.children[1]\n if (modulus) res.bits = stripLeadingZero(modulus.content).length * 8\n } else if (oid === '1.2.840.10045.2.1') {\n // EC: the named curve is the algorithm parameters OID.\n const params = algId?.children[1]\n if (params && params.tagNumber === TAG.OID) {\n res.curve = oidName(oidToString(params.content))\n const m = res.curve.match(/(\\d{3})/)\n if (m) res.bits = parseInt(m[1], 10)\n }\n }\n return res\n } catch {\n return { algorithm: '未知' }\n }\n}\n\n/** SHA-256 fingerprint of DER bytes, as colon-separated hex (e.g. `AB:CD:…`). */\nexport async function sha256Hex(der: Uint8Array): Promise<string> {\n // Copy into a fresh ArrayBuffer so a subarray view's offset doesn't confuse digest.\n const copy = der.slice()\n const digest = await crypto.subtle.digest('SHA-256', copy)\n return bytesToHex(new Uint8Array(digest), ':').toUpperCase()\n}\n"],"mappings":"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"}
|
package/dist/lib/xlsx.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xlsx.js","names":[],"sources":["../../src/lib/xlsx.ts"],"sourcesContent":["/**\n * In-browser `.xlsx` (and `.xlsm`) parsing for the side viewer's spreadsheet preview.\n *\n * exceljs does the OOXML heavy lifting (unzip + parse the SpreadsheetML); here we flatten one of its\n * Workbooks into a small, serializable model the {@link XlsxViewer} renders as plain HTML `<table>`s —\n * one table per worksheet, with merged cells, column widths, and the common cell styling (bold/italic/\n * color/fill/alignment/borders) carried over so the preview reads like the original sheet.\n *\n * exceljs is heavy (~1MB), so this module is only ever imported by the async-loaded XlsxViewer — it never\n * lands in the main bundle. We import the namespace (not named exports) because exceljs's browser entry is\n * a UMD bundle: `new ExcelJS.Workbook()` resolves identically under Vite's CJS interop and the .d.ts.\n */\nimport * as ExcelJS from 'exceljs'\nimport { format as nfFormat, isDateFormat as nfIsDateFormat } from 'numfmt'\n\n/** One rendered cell. A cell covered by a merge is dropped from the row entirely (see {@link XlsxRow}). */\nexport interface XlsxCell {\n text: string\n rowSpan: number\n colSpan: number\n /** 1-based column index of this cell's left edge — lets the renderer place cells after merge gaps. */\n col: number\n bold?: boolean\n italic?: boolean\n underline?: boolean\n strike?: boolean\n /** Font size in px (converted from the workbook's points), or undefined to use the table default. */\n fontSize?: number\n /** The cell's formula expression WITHOUT a leading `=` (e.g. `数据基表!B10`, `#REF!`), for the source view. */\n formula?: string\n /** CSS color for the text / the cell background, derived from the workbook's ARGB colors. */\n color?: string\n bg?: string\n align?: 'left' | 'center' | 'right'\n valign?: 'top' | 'middle' | 'bottom'\n wrap?: boolean\n /** Which borders the cell draws (true = a visible edge in the source). */\n bt?: boolean\n br?: boolean\n bb?: boolean\n bl?: boolean\n}\n\nexport interface XlsxRow {\n /** 1-based row number in the worksheet (so the viewer can label the gutter and key frozen/hidden state). */\n num: number\n /** Row height in px (converted from Excel points), or undefined to size to content. */\n height?: number\n /** Hidden in the source (\"hide rows\") — the viewer collapses it behind an expander, like Excel/WPS. */\n hidden?: boolean\n /** Only the cells that actually render — merge-covered slots are omitted; use {@link XlsxCell.col}. */\n cells: XlsxCell[]\n}\n\nexport interface XlsxSheet {\n name: string\n /** Per-column width in px (index 0 = column A); columns past this use a default width. */\n colWidths: number[]\n /** Per-column hidden flag (index 0 = column A) — collapsed behind an expander, like Excel/WPS. */\n colHidden: boolean[]\n rows: XlsxRow[]\n cols: number\n /** Frozen pane sizes (Excel \"freeze panes\"): the first N rows / M columns stay pinned while scrolling. */\n frozenRows: number\n frozenCols: number\n}\n\nexport interface XlsxModel {\n sheets: XlsxSheet[]\n}\n\n// Guardrails: a corrupt or absurdly large sheet shouldn't lock up the tab. We cap the rendered range and\n// let the viewer note the truncation. (Real attachments are tiny; these only bite pathological files.)\nconst MAX_ROWS = 2000\nconst MAX_COLS = 200\n\n// Excel column-letter (A, B, …, AA) → 1-based index.\nfunction colToNum(s: string): number {\n let n = 0\n for (let i = 0; i < s.length; i++) n = n * 26 + (s.charCodeAt(i) - 64)\n return n\n}\n\n// 1-based index → Excel column-letter (1→A, 26→Z, 27→AA). Used by the viewer's A/B/C… header row.\nexport function numToCol(n: number): string {\n let s = ''\n while (n > 0) {\n const m = (n - 1) % 26\n s = String.fromCharCode(65 + m) + s\n n = Math.floor((n - 1) / 26)\n }\n return s\n}\n\ninterface Span {\n rowSpan: number\n colSpan: number\n}\n\n// Parse exceljs's merge ranges (\"A1:B2\") into: span info keyed at each master cell, and a set of the\n// addresses (`r,c`) covered by a merge (which we then skip rendering).\nfunction parseMerges(merges: string[]): { spans: Map<string, Span>; covered: Set<string> } {\n const spans = new Map<string, Span>()\n const covered = new Set<string>()\n for (const range of merges) {\n const m = /^([A-Z]+)(\\d+):([A-Z]+)(\\d+)$/.exec(range)\n if (!m) continue\n const c1 = colToNum(m[1])\n const r1 = parseInt(m[2], 10)\n const c2 = colToNum(m[3])\n const r2 = parseInt(m[4], 10)\n const top = Math.min(r1, r2)\n const bottom = Math.max(r1, r2)\n const left = Math.min(c1, c2)\n const right = Math.max(c1, c2)\n spans.set(`${top},${left}`, { rowSpan: bottom - top + 1, colSpan: right - left + 1 })\n for (let r = top; r <= bottom; r++) {\n for (let c = left; c <= right; c++) {\n if (r === top && c === left) continue\n covered.add(`${r},${c}`)\n }\n }\n }\n return { spans, covered }\n}\n\n// Excel's legacy 56-colour indexed palette (BIFF8). Cells/fonts that don't carry an explicit ARGB reference\n// a palette slot instead (e.g. `{ indexed: 47 }`) — very common in older workbooks. 64/65 are the system\n// foreground/background (\"automatic\"), handled specially below.\n// prettier-ignore\nconst INDEXED_COLORS = [\n '000000', 'FFFFFF', 'FF0000', '00FF00', '0000FF', 'FFFF00', 'FF00FF', '00FFFF',\n '000000', 'FFFFFF', 'FF0000', '00FF00', '0000FF', 'FFFF00', 'FF00FF', '00FFFF',\n '800000', '008000', '000080', '808000', '800080', '008080', 'C0C0C0', '808080',\n '9999FF', '993366', 'FFFFCC', 'CCFFFF', '660066', 'FF8080', '0066CC', 'CCCCFF',\n '000080', 'FF00FF', 'FFFF00', '00FFFF', '800080', '800000', '008080', '0000FF',\n '00CCFF', 'CCFFFF', 'CCFFCC', 'FFFF99', '99CCFF', 'FF99CC', 'CC99FF', 'FFCC99',\n '3366FF', '33CCCC', '99CC00', 'FFCC00', 'FF9900', 'FF6600', '666699', '969696',\n '003366', '339966', '003300', '333300', '993300', '993366', '333399', '333333',\n]\n\n// Default Office theme colours, in the order the styles `theme` attribute indexes them (0=bg1/white,\n// 1=text1/black, 2=bg2, 3=text2, 4..9=accent1..6, 10=hlink, 11=folHlink). We don't parse the workbook's\n// actual theme XML (exceljs doesn't surface it), so this default scheme is a best-effort — but it gets the\n// common cases right (notably theme 1 = black text, theme 0 = white) and `tint` is applied on top.\n// prettier-ignore\nconst THEME_COLORS = [\n 'FFFFFF', '000000', 'E7E6E6', '44546A', '4472C4', 'ED7D31',\n 'A5A5A5', 'FFC000', '5B9BD5', '70AD47', '0563C1', '954F72',\n]\n\nfunction rgbToHsl(r: number, g: number, b: number): [number, number, number] {\n r /= 255\n g /= 255\n b /= 255\n const max = Math.max(r, g, b)\n const min = Math.min(r, g, b)\n let h = 0\n let s = 0\n const l = (max + min) / 2\n if (max !== min) {\n const d = max - min\n s = l > 0.5 ? d / (2 - max - min) : d / (max + min)\n if (max === r) h = (g - b) / d + (g < b ? 6 : 0)\n else if (max === g) h = (b - r) / d + 2\n else h = (r - g) / d + 4\n h /= 6\n }\n return [h, s, l]\n}\n\nfunction hue2rgb(p: number, q: number, t: number): number {\n if (t < 0) t += 1\n if (t > 1) t -= 1\n if (t < 1 / 6) return p + (q - p) * 6 * t\n if (t < 1 / 2) return q\n if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6\n return p\n}\n\nfunction hslToRgb(h: number, s: number, l: number): [number, number, number] {\n if (s === 0) {\n const v = Math.round(l * 255)\n return [v, v, v]\n }\n const q = l < 0.5 ? l * (1 + s) : l + s - l * s\n const p = 2 * l - q\n return [\n Math.round(hue2rgb(p, q, h + 1 / 3) * 255),\n Math.round(hue2rgb(p, q, h) * 255),\n Math.round(hue2rgb(p, q, h - 1 / 3) * 255),\n ]\n}\n\n// Excel applies `tint` to a theme/indexed colour by shifting its HSL luminance: negative darkens, positive\n// lightens. Mirrors the algorithm in the OOXML spec so shaded theme fills come out the right tone.\nfunction applyTint(hex: string, tint: number): string {\n const r = parseInt(hex.slice(0, 2), 16)\n const g = parseInt(hex.slice(2, 4), 16)\n const b = parseInt(hex.slice(4, 6), 16)\n const [h, s, l0] = rgbToHsl(r, g, b)\n let l = tint < 0 ? l0 * (1 + tint) : l0 * (1 - tint) + tint\n l = Math.max(0, Math.min(1, l))\n const [nr, ng, nb] = hslToRgb(h, s, l)\n const hx = (n: number): string => n.toString(16).padStart(2, '0')\n return hx(nr) + hx(ng) + hx(nb)\n}\n\n/**\n * Resolve any exceljs colour object to a CSS `#RRGGBB`, covering all three references Excel uses: explicit\n * `argb`, the legacy `indexed` palette, and `theme` index (+ optional `tint`). Returns undefined for \"no\n * colour\" — a fully-transparent argb, the system-automatic indices, or an unresolvable shape.\n */\nfunction colorToCss(color: unknown): string | undefined {\n if (!color || typeof color !== 'object') return undefined\n const c = color as { argb?: string; theme?: number; indexed?: number; tint?: number }\n let hex: string | undefined\n if (typeof c.argb === 'string') {\n if (c.argb.length === 8) {\n if (c.argb.slice(0, 2).toLowerCase() === '00') return undefined // fully transparent → no colour\n hex = c.argb.slice(2)\n } else if (c.argb.length === 6) {\n hex = c.argb\n }\n } else if (typeof c.theme === 'number') {\n hex = THEME_COLORS[c.theme]\n } else if (typeof c.indexed === 'number') {\n if (c.indexed === 64) return undefined // system foreground / automatic → let the default apply\n hex = c.indexed === 65 ? 'FFFFFF' : INDEXED_COLORS[c.indexed]\n }\n if (!hex || !/^[0-9a-fA-F]{6}$/.test(hex)) return undefined\n if (typeof c.tint === 'number' && c.tint !== 0) hex = applyTint(hex, c.tint)\n return `#${hex}`\n}\n\n// Excel font sizes are in points; CSS wants px. The conventional screen conversion is pt × 96/72 (= ×4/3),\n// which keeps relative sizing faithful (a 24pt title reads clearly larger than 10pt body text).\nfunction fontSizeToPx(size: number | undefined): number | undefined {\n if (!size || size <= 0) return undefined\n return Math.round((size * 4) / 3)\n}\n\n// Excel column width is in \"character\" units; the conventional pixel conversion to the COLUMN's total width is\n// width*7 + 5 (the +5 is Excel's own internal cell padding). But our HTML cell adds its OWN padding (~4px each\n// side) and a gridline border on top of the `<col>` width, so reusing Excel's total width would leave the\n// content area ~8px short and clip text into \"…\". We add that HTML chrome back (+9 instead of +5) so the\n// usable content width matches Excel's, keeping the cell `padding`/border in sync (see XlsxViewer .xv__cell).\nfunction colWidthToPx(w: number | undefined): number {\n if (!w || w <= 0) return 80\n return Math.round(w * 7 + 9)\n}\n\n// A two-digit zero-pad for the date formatter.\nfunction pad2(n: number): string {\n return n < 10 ? `0${n}` : String(n)\n}\n\n// Render a Date the way a spreadsheet preview should — `YYYY-MM-DD`, plus a `HH:MM[:SS]` tail only when the\n// value carries a time-of-day. (exceljs's own `cell.text` gives the raw JS `Date.toString()`, e.g.\n// \"Sat May 04 2019 08:00:00 GMT+0800 (中国标准时间)\", which is useless in a cell.)\nfunction formatDate(d: Date): string {\n const date = `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}`\n const h = d.getHours()\n const mi = d.getMinutes()\n const s = d.getSeconds()\n if (!h && !mi && !s) return date\n return `${date} ${pad2(h)}:${pad2(mi)}${s ? `:${pad2(s)}` : ''}`\n}\n\nfunction richTextToString(runs: unknown): string {\n if (!Array.isArray(runs)) return ''\n return runs\n .map((r) => (r && typeof r === 'object' ? ((r as { text?: string }).text ?? '') : ''))\n .join('')\n}\n\n// CJK numeral digits/units for Excel's `[DBNum1]` (中文小写) and `[DBNum2]` (中文大写/financial) — numfmt does\n// not implement these (Excel's 特殊 → 中文数字), so we convert the integer ourselves with proper 万/亿 grouping.\nconst DBNUM_DIGITS = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九']\nconst DBNUM_DIGITS_UP = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖']\nconst DBNUM_UNITS = ['', '十', '百', '千']\nconst DBNUM_UNITS_UP = ['', '拾', '佰', '仟']\nconst DBNUM_BIG = ['', '万', '亿', '兆']\n\n// Convert one 0..9999 group to CJK numerals, collapsing internal zeros to a single 零 (no trailing zero unit).\nfunction dbNumGroup(n: number, digits: string[], units: string[]): string {\n let s = ''\n let pendingZero = false\n for (let i = 3; i >= 0; i--) {\n const x = Math.floor(n / 10 ** i) % 10\n if (x === 0) {\n if (s !== '') pendingZero = true\n } else {\n if (pendingZero) {\n s += digits[0]\n pendingZero = false\n }\n s += digits[x] + units[i]\n }\n }\n return s\n}\n\n// Excel `[DBNum1]`/`[DBNum2]` of a non-negative integer: e.g. 1234 → 一千二百三十四 / 壹仟贰佰叁拾肆, 10001 → 一万零一,\n// 100000001 → 一亿零一. (Keeps Excel's leading 一十 for 10-19, single 零 between groups, like Excel's own output.)\nfunction toChineseNumeral(n: number, upper: boolean): string {\n const digits = upper ? DBNUM_DIGITS_UP : DBNUM_DIGITS\n const units = upper ? DBNUM_UNITS_UP : DBNUM_UNITS\n if (n === 0) return digits[0]\n const groups: number[] = []\n let x = n\n while (x > 0) {\n groups.push(x % 10000)\n x = Math.floor(x / 10000)\n }\n let s = ''\n for (let i = groups.length - 1; i >= 0; i--) {\n const g = groups[i]\n if (g === 0) continue // an all-zero group: the next non-zero group's leading 零 covers the gap\n let gs = dbNumGroup(g, digits, units)\n if (i < groups.length - 1 && g < 1000) gs = digits[0] + gs // 1,0001 → 一万零一 (zero after a higher group)\n s += gs + DBNUM_BIG[i]\n }\n return s\n}\n\n// Map ASCII digits to full-width — Excel's `[DBNum3]` (全角数字); applied over numfmt's normal output so any\n// separators / decimals survive.\nfunction toFullWidthDigits(s: string): string {\n return s.replace(/[0-9]/g, (d) => '0123456789'[Number(d)])\n}\n\n// Honor Excel's `[DBNumN]` CJK-numeral prefixes that numfmt can't render. Returns null to fall back to numfmt\n// (negatives / non-integers for DBNum1/2, where Excel's behavior is fiddly — a plain number is safer than a guess).\nfunction formatDbNum(value: number, n: number, fmt: string): string | null {\n if (n === 3) {\n try {\n const out = nfFormat(fmt, value) // numfmt strips the [DBNum3] tag and formats the rest\n if (typeof out === 'string') return toFullWidthDigits(out)\n } catch {\n /* fall through */\n }\n return null\n }\n if (Number.isInteger(value) && value >= 0) return toChineseNumeral(value, n === 2)\n return null\n}\n\n// numfmt understands the English \"General\" keyword but not Excel's LOCALIZED one (Simplified Chinese \"G/通用格式\",\n// Japanese \"G/標準\", etc.), throwing \"Invalid pattern\" — normalize those to \"General\" so a locale-tagged General\n// format (common in CJK files after a [DBNum]/[$-…] prefix) parses. (Plain \"[$-NNN]\" locale tags numfmt handles.)\nfunction cleanFmt(fmt: string): string {\n return fmt.replace(/G\\/(通用格式|標準|标准|표준)/g, 'General')\n}\n\n// Apply the cell's Excel number format (`cell.numFmt`) to a numeric value the way Excel DISPLAYS it — exceljs's\n// `cell.text` returns the raw stored number, so an integer-formatted cell (\"0\") shows the stored float\n// \"2.9999999999\" instead of \"3\", a percent format shows \"0.5\" instead of \"50%\", thousands separators go missing,\n// etc. An undefined format → \"General\", which (like Excel) also strips IEEE float noise (0.1+0.2 → \"0.3\"); this is\n// why a cell *referencing* a formatted one rendered wrong too — its cached result is the noisy float. numfmt covers\n// currency / accounting / fraction / scientific / percent / text(@) / 邮编 / custom codes; `[DBNumN]` CJK numerals\n// (which numfmt doesn't implement) are handled here. Unsupported codes / unexpected output fall back to a plain\n// string, never throwing.\nfunction formatNumber(value: number, numFmt: string | undefined): string {\n const fmt = cleanFmt(numFmt || 'General')\n const dbnum = /\\[DBNum([123])\\]/.exec(fmt)\n if (dbnum) {\n const out = formatDbNum(value, Number(dbnum[1]), fmt)\n if (out != null) return out\n }\n try {\n const out = nfFormat(fmt, value)\n return typeof out === 'string' ? out : String(value)\n } catch {\n return String(value)\n }\n}\n\n// Format a date the way Excel's number format dictates — exceljs hands us a JS `Date`, and numfmt formats it\n// directly (reading its local components) for any date/time format code: ISO, 中文 yyyy\"年\"m\"月\"d\"日\", h:mm AM/PM,\n// custom date-times, etc. Only route through numfmt when the format actually IS a date format; otherwise (no/General\n// format) keep the viewer's plain YYYY-MM-DD default from {@link formatDate}.\nfunction formatDateValue(d: Date, numFmt: string | undefined): string {\n if (numFmt && numFmt !== 'General' && nfIsDateFormat(numFmt)) {\n try {\n const out = nfFormat(numFmt, d)\n if (typeof out === 'string' && out !== '') return out\n } catch {\n /* fall through to the plain default */\n }\n }\n return formatDate(d)\n}\n\n/**\n * Resolve a cell's display text from its value SHAPE rather than exceljs's `cell.text` — which returns the\n * raw object for some value kinds (so a formula whose cached result is an error renders as \"[object Object]\")\n * and even throws on certain merged cells. We cover every {@link ExcelJS.ValueType}: numbers/strings/booleans\n * verbatim, dates formatted, rich text joined, hyperlinks as their label, formula results unwrapped (errors →\n * their code like \"#REF!\", a nested date/rich-text result resolved), and error cells as their code. Anything\n * unexpected falls back to a guarded `cell.text`, never to a stringified object.\n */\nfunction cellText(cell: ExcelJS.Cell): string {\n const v = cell.value as unknown\n if (v == null) return ''\n if (typeof v === 'number') return formatNumber(v, cell.numFmt)\n if (typeof v === 'string' || typeof v === 'boolean') return String(v)\n if (v instanceof Date) return formatDateValue(v, cell.numFmt)\n if (typeof v === 'object') {\n const o = v as Record<string, unknown>\n if (typeof o.error === 'string') return o.error // error cell: { error: '#DIV/0!' }\n if (Array.isArray(o.richText)) return richTextToString(o.richText) // rich text\n if ('hyperlink' in o) {\n // hyperlink: { text, hyperlink } — text may itself be rich text\n const t = o.text\n if (Array.isArray((t as { richText?: unknown })?.richText)) {\n return richTextToString((t as { richText?: unknown }).richText)\n }\n return t == null ? String(o.hyperlink) : String(t)\n }\n if ('result' in o || 'formula' in o || 'sharedFormula' in o) {\n // formula: { formula, result } — the result is what Excel shows in the cell\n const res = o.result as unknown\n if (res == null) return ''\n if (typeof res === 'number') return formatNumber(res, cell.numFmt)\n if (typeof res === 'string' || typeof res === 'boolean') return String(res)\n if (res instanceof Date) return formatDateValue(res, cell.numFmt)\n if (res && typeof res === 'object') {\n const ro = res as Record<string, unknown>\n if (typeof ro.error === 'string') return ro.error // formula result is an error → its code\n if (Array.isArray(ro.richText)) return richTextToString(ro.richText)\n }\n return ''\n }\n }\n // Unknown shape: try exceljs's own text, but never surface a stringified object (and never throw).\n try {\n const t = cell.text\n return t != null && t !== '[object Object]' ? String(t) : ''\n } catch {\n return ''\n }\n}\n\n// Numbers and dates align right by default in Excel (text aligns left). Mirror that when the cell has no\n// explicit horizontal alignment — including formula cells whose cached result is a number or date.\nfunction defaultRight(value: unknown): boolean {\n if (typeof value === 'number') return true\n if (value instanceof Date) return true\n if (value && typeof value === 'object' && 'result' in value) {\n const res = (value as { result?: unknown }).result\n return typeof res === 'number' || res instanceof Date\n }\n return false\n}\n\nfunction buildSheet(ws: ExcelJS.Worksheet): XlsxSheet {\n const merges = Array.isArray(ws.model?.merges) ? (ws.model.merges as string[]) : []\n const { spans, covered } = parseMerges(merges)\n\n const cols = Math.min(ws.columnCount || 0, MAX_COLS)\n const rowCount = Math.min(ws.rowCount || 0, MAX_ROWS)\n\n const colWidths: number[] = []\n const colHidden: boolean[] = []\n for (let c = 1; c <= cols; c++) {\n const col = ws.getColumn(c)\n colWidths.push(colWidthToPx(col?.width))\n colHidden.push(!!col?.hidden)\n }\n\n const rows: XlsxRow[] = []\n for (let r = 1; r <= rowCount; r++) {\n const row = ws.getRow(r)\n const cells: XlsxCell[] = []\n for (let c = 1; c <= cols; c++) {\n if (covered.has(`${r},${c}`)) continue\n const cell = row.getCell(c)\n const span = spans.get(`${r},${c}`)\n const text = cellText(cell)\n // Formula source (without leading `=`): regular cells carry `{ formula }`, shared-formula dependents\n // carry `{ sharedFormula }` (exceljs's per-cell expression). Read from the value shape to avoid the\n // `.formula` getter's side effects.\n let formula: string | undefined\n const cv = cell.value as unknown\n if (cv && typeof cv === 'object') {\n const o = cv as Record<string, unknown>\n if (typeof o.formula === 'string' && o.formula) formula = o.formula\n else if (typeof o.sharedFormula === 'string' && o.sharedFormula) formula = o.sharedFormula\n }\n // Skip truly empty, unstyled, unmerged cells but still emit a placeholder so columns line up — we do\n // that by always pushing a cell (with its `col`), letting the renderer position via colSpan gaps.\n const font = cell.font || {}\n const align = cell.alignment || {}\n const border = cell.border || {}\n const horizontal =\n align.horizontal === 'center' || align.horizontal === 'centerContinuous'\n ? 'center'\n : align.horizontal === 'right'\n ? 'right'\n : align.horizontal === 'left'\n ? 'left'\n : defaultRight(cell.value)\n ? 'right'\n : undefined\n const vertical =\n align.vertical === 'top'\n ? 'top'\n : align.vertical === 'bottom'\n ? 'bottom'\n : align.vertical === 'middle'\n ? 'middle'\n : undefined\n cells.push({\n text,\n col: c,\n rowSpan: span?.rowSpan ?? 1,\n colSpan: span?.colSpan ?? 1,\n bold: font.bold || undefined,\n italic: font.italic || undefined,\n underline: !!font.underline || undefined,\n strike: font.strike || undefined,\n fontSize: fontSizeToPx(font.size),\n formula,\n color: colorToCss(font.color),\n bg:\n cell.fill && cell.fill.type === 'pattern'\n ? colorToCss((cell.fill as { fgColor?: unknown }).fgColor)\n : undefined,\n align: horizontal,\n valign: vertical,\n wrap: align.wrapText || undefined,\n bt: !!border.top?.style || undefined,\n br: !!border.right?.style || undefined,\n bb: !!border.bottom?.style || undefined,\n bl: !!border.left?.style || undefined,\n })\n }\n rows.push({\n num: r,\n height: row.height ? Math.round((row.height * 4) / 3) : undefined,\n hidden: !!row.hidden,\n cells,\n })\n }\n\n // Freeze panes: exceljs surfaces the active view as { state:'frozen', xSplit, ySplit } where xSplit/ySplit\n // are the counts of columns/rows held fixed. Clamp to the rendered range so a stale split can't pin offscreen.\n const view = Array.isArray(ws.views) ? ws.views[0] : undefined\n const frozen = view && (view as { state?: string }).state === 'frozen' ? view : undefined\n const frozenCols = frozen\n ? Math.min(Math.max(0, (frozen as { xSplit?: number }).xSplit ?? 0), cols)\n : 0\n const frozenRows = frozen\n ? Math.min(Math.max(0, (frozen as { ySplit?: number }).ySplit ?? 0), rowCount)\n : 0\n\n return { name: ws.name || `Sheet`, colWidths, colHidden, rows, cols, frozenRows, frozenCols }\n}\n\n/** Parse `.xlsx` bytes into the render model. Throws on a non-spreadsheet / corrupt file. */\nexport async function parseXlsx(buf: ArrayBuffer): Promise<XlsxModel> {\n const wb = new ExcelJS.Workbook()\n await wb.xlsx.load(buf)\n const sheets = wb.worksheets\n // Hidden sheets (state 'hidden'/'veryHidden') aren't shown in Excel's tab bar; skip them too.\n .filter((ws) => ws && ws.state !== 'hidden' && ws.state !== 'veryHidden')\n .map(buildSheet)\n if (sheets.length === 0) throw new Error('工作簿中没有可显示的工作表')\n return { sheets }\n}\n"],"mappings":"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"}
|
package/dist/lib/xlsxCharts.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xlsxCharts.js","names":[],"sources":["../../src/lib/xlsxCharts.ts"],"sourcesContent":["/**\n * Extract embedded **charts** from an `.xlsx` for the spreadsheet preview.\n *\n * exceljs parses cells and styles but NOT charts (and a workbook can hold native line/bar/pie charts with no\n * raster image anywhere — `wb.model.media` is empty). So we read them ourselves: an `.xlsx` is a ZIP, and we\n * already have a dependency-free reader ({@link readZip}/{@link extractZipEntry}). We walk\n * worksheet → drawing → chart, parse the chart's cached series/categories straight out of the OOXML\n * (`<c:numCache>`/`<c:strCache>` — the values Excel last computed, so no recomputation needed), and hand the\n * viewer a small model it renders as SVG ({@link XlsxChart}). Browser-only (DOMParser + DecompressionStream);\n * charts simply don't appear under SSR.\n */\nimport { extractZipEntry, readZip, zipSupported, type ZipEntry } from '@/lib/zip'\n\nexport type ChartType = 'line' | 'bar' | 'barH' | 'pie' | 'area' | 'scatter' | 'unknown'\n\nexport interface ChartSeries {\n name: string\n /** Series line/fill colour as CSS `#RRGGBB`, when the chart specifies one. */\n color?: string\n /** Cached y-values, one per category (null = a gap/blank point). */\n values: (number | null)[]\n}\n\n/** Where a chart sits on the sheet — top-left cell + pixel size, converted from the drawing anchor's EMUs. */\nexport interface ChartAnchor {\n /** 0-based column / row of the anchor's top-left cell. */\n fromCol: number\n fromRow: number\n /** Pixel offset into that cell, and the chart's pixel width/height (from the frame's EMU extent). */\n offX: number\n offY: number\n wPx: number\n hPx: number\n}\n\nexport interface XlsxChart {\n type: ChartType\n title: string\n categories: string[]\n series: ChartSeries[]\n /** Explicit value-axis bounds, when the chart pins them (else the renderer auto-scales). */\n yMin?: number\n yMax?: number\n anchor: ChartAnchor\n}\n\nconst EMU_PER_PX = 9525 // 914400 EMU per inch / 96 px per inch\nconst emuToPx = (emu: number): number => Math.round(emu / EMU_PER_PX)\n\n// Descendants matching a localName, regardless of namespace prefix (xdr:/c:/a: vary by producer).\nfunction tags(el: Element | Document, localName: string): Element[] {\n return Array.from(el.getElementsByTagNameNS('*', localName))\n}\nfunction tag(el: Element | Document, localName: string): Element | undefined {\n return el.getElementsByTagNameNS('*', localName)[0]\n}\nfunction attrVal(el: Element | undefined): string | undefined {\n return el?.getAttribute('val') ?? undefined\n}\n\n// Resolve an OOXML relationship target (often \"../charts/chart3.xml\") against the part that referenced it.\nfunction resolvePath(baseDir: string, rel: string): string {\n const parts = (baseDir ? baseDir.split('/') : []).concat(rel.split('/'))\n const out: string[] = []\n for (const p of parts) {\n if (p === '' || p === '.') continue\n if (p === '..') out.pop()\n else out.push(p)\n }\n return out.join('/')\n}\nconst dirOf = (path: string): string => path.split('/').slice(0, -1).join('/')\n\n/** A loader bound to one workbook's ZIP: lazily inflate + XML-parse parts, cached by path. */\nfunction makeLoader(\n buf: ArrayBuffer,\n entries: ZipEntry[],\n): (path: string) => Promise<Document | null> {\n const byPath = new Map(entries.map((e) => [e.path, e]))\n const cache = new Map<string, Promise<Document | null>>()\n const decoder = new TextDecoder('utf-8')\n return (path: string) => {\n let p = cache.get(path)\n if (!p) {\n p = (async () => {\n const e = byPath.get(path)\n if (!e) return null\n const bytes = await extractZipEntry(buf, e)\n const doc = new DOMParser().parseFromString(decoder.decode(bytes), 'application/xml')\n return doc.querySelector('parsererror') ? null : doc\n })()\n cache.set(path, p)\n }\n return p\n }\n}\n\n// Parse a \"rels\" part into { relId → resolved absolute path }.\nfunction relsMap(doc: Document | null, baseDir: string): Map<string, string> {\n const m = new Map<string, string>()\n if (!doc) return m\n for (const r of tags(doc, 'Relationship')) {\n const id = r.getAttribute('Id')\n const target = r.getAttribute('Target')\n if (id && target && !/^[a-z]+:\\/\\//i.test(target)) m.set(id, resolvePath(baseDir, target))\n }\n return m\n}\n\n// Pull the ordered category labels (or numbers) out of a <c:cat>'s cached points.\nfunction readCacheStrings(parent: Element | undefined): string[] {\n if (!parent) return []\n const cache = tag(parent, 'strCache') || tag(parent, 'numCache')\n if (!cache) return []\n const count = parseInt(attrVal(tag(cache, 'ptCount')) || '0', 10) || 0\n const out: string[] = Array.from({ length: count }, () => '')\n for (const pt of tags(cache, 'pt')) {\n const idx = parseInt(pt.getAttribute('idx') || '-1', 10)\n const v = tag(pt, 'v')?.textContent ?? ''\n if (idx >= 0) {\n if (idx >= out.length) out.length = idx + 1\n out[idx] = v\n }\n }\n return out\n}\n\n// Pull cached numeric values out of a <c:val>'s numCache (blank/non-numeric → null).\nfunction readCacheNumbers(parent: Element | undefined): (number | null)[] {\n if (!parent) return []\n const cache = tag(parent, 'numCache') || tag(parent, 'strCache')\n if (!cache) return []\n const count = parseInt(attrVal(tag(cache, 'ptCount')) || '0', 10) || 0\n const out: (number | null)[] = Array.from({ length: count }, () => null)\n for (const pt of tags(cache, 'pt')) {\n const idx = parseInt(pt.getAttribute('idx') || '-1', 10)\n const raw = tag(pt, 'v')?.textContent\n const n = raw == null || raw === '' ? null : Number(raw)\n if (idx >= 0) {\n if (idx >= out.length) out.length = idx + 1\n out[idx] = n != null && Number.isFinite(n) ? n : null\n }\n }\n return out\n}\n\nfunction chartTypeOf(plot: Element): ChartType {\n if (tag(plot, 'lineChart')) return 'line'\n if (tag(plot, 'barChart')) {\n const dir = attrVal(tag(tag(plot, 'barChart')!, 'barDir'))\n return dir === 'bar' ? 'barH' : 'bar'\n }\n if (tag(plot, 'pieChart') || tag(plot, 'doughnutChart')) return 'pie'\n if (tag(plot, 'areaChart')) return 'area'\n if (tag(plot, 'scatterChart')) return 'scatter'\n return 'unknown'\n}\n\n// First explicit series colour: prefer the line stroke, else any solid fill in the series' shape props.\nfunction seriesColor(ser: Element): string | undefined {\n const spPr = tag(ser, 'spPr')\n if (!spPr) return undefined\n const ln = tag(spPr, 'ln')\n const clr = (ln && tag(ln, 'srgbClr')) || tag(spPr, 'srgbClr')\n const val = clr?.getAttribute('val')\n return val && /^[0-9a-fA-F]{6}$/.test(val) ? `#${val}` : undefined\n}\n\nfunction parseChart(doc: Document): Omit<XlsxChart, 'anchor'> | null {\n const plot = tag(doc, 'plotArea')\n if (!plot) return null\n const type = chartTypeOf(plot)\n // Title: concatenate the rich-text runs under <c:title>.\n const titleEl = tag(doc, 'title')\n const title = titleEl\n ? tags(titleEl, 't')\n .map((t) => t.textContent ?? '')\n .join('')\n .trim()\n : ''\n\n let categories: string[] = []\n const series: ChartSeries[] = []\n for (const ser of tags(plot, 'ser')) {\n const cats = readCacheStrings(tag(ser, 'cat'))\n if (cats.length > categories.length) categories = cats\n const txV = tag(tag(ser, 'tx') || ser, 'v')?.textContent\n series.push({\n name: (txV || `系列 ${series.length + 1}`).trim(),\n color: seriesColor(ser),\n values: readCacheNumbers(tag(ser, 'val')),\n })\n }\n\n // Value-axis explicit bounds, if any.\n let yMin: number | undefined\n let yMax: number | undefined\n for (const ax of tags(plot, 'valAx')) {\n const sc = tag(ax, 'scaling')\n if (!sc) continue\n const mx = attrVal(tag(sc, 'max'))\n const mn = attrVal(tag(sc, 'min'))\n if (mx != null && Number.isFinite(Number(mx))) yMax = Number(mx)\n if (mn != null && Number.isFinite(Number(mn))) yMin = Number(mn)\n }\n return { type, title, categories, series, yMin, yMax }\n}\n\n// Read a drawing's anchors, resolving each graphicFrame's chart part via the drawing's own rels.\nasync function parseDrawing(\n drawingPath: string,\n load: (path: string) => Promise<Document | null>,\n): Promise<XlsxChart[]> {\n const doc = await load(drawingPath)\n if (!doc) return []\n const rels = relsMap(\n await load(`${dirOf(drawingPath)}/_rels/${drawingPath.split('/').pop()}.rels`),\n dirOf(drawingPath),\n )\n const out: XlsxChart[] = []\n for (const anchor of [...tags(doc, 'twoCellAnchor'), ...tags(doc, 'oneCellAnchor')]) {\n const frame = tag(anchor, 'graphicFrame')\n if (!frame) continue\n const chartRef = tag(frame, 'chart') // <c:chart r:id=\"rIdN\"/>\n const rId =\n chartRef?.getAttributeNS(\n 'http://schemas.openxmlformats.org/officeDocument/2006/relationships',\n 'id',\n ) || chartRef?.getAttribute('r:id')\n const chartPath = rId ? rels.get(rId) : undefined\n if (!chartPath) continue\n const chartDoc = await load(chartPath)\n if (!chartDoc) continue\n const parsed = parseChart(chartDoc)\n if (!parsed || !parsed.series.length) continue\n\n const from = tag(anchor, 'from')\n const fromCol = parseInt(tag(from!, 'col')?.textContent || '0', 10)\n const fromRow = parseInt(tag(from!, 'row')?.textContent || '0', 10)\n const offX = emuToPx(parseInt(tag(from!, 'colOff')?.textContent || '0', 10))\n const offY = emuToPx(parseInt(tag(from!, 'rowOff')?.textContent || '0', 10))\n const ext = tag(frame, 'ext') // <a:ext cx cy> in EMU\n const wPx = ext ? emuToPx(parseInt(ext.getAttribute('cx') || '0', 10)) : 384\n const hPx = ext ? emuToPx(parseInt(ext.getAttribute('cy') || '0', 10)) : 230\n out.push({ ...parsed, anchor: { fromCol, fromRow, offX, offY, wPx, hPx } })\n }\n return out\n}\n\n/**\n * Parse every chart in the workbook, grouped by worksheet **name** (matching {@link XlsxSheet.name}). Returns\n * an empty map under SSR or if the bytes can't be unzipped — charts are a best-effort enhancement, never fatal.\n */\nexport async function parseXlsxCharts(buf: ArrayBuffer): Promise<Map<string, XlsxChart[]>> {\n const result = new Map<string, XlsxChart[]>()\n if (!zipSupported() || typeof DOMParser === 'undefined') return result\n let entries: ZipEntry[]\n try {\n entries = readZip(buf)\n } catch {\n return result\n }\n const load = makeLoader(buf, entries)\n\n const wb = await load('xl/workbook.xml')\n if (!wb) return result\n const wbRels = relsMap(await load('xl/_rels/workbook.xml.rels'), 'xl')\n\n for (const sheetEl of tags(wb, 'sheet')) {\n const name = sheetEl.getAttribute('name') || ''\n const rId =\n sheetEl.getAttributeNS(\n 'http://schemas.openxmlformats.org/officeDocument/2006/relationships',\n 'id',\n ) || sheetEl.getAttribute('r:id')\n const sheetPath = rId ? wbRels.get(rId) : undefined\n if (!sheetPath) continue\n const sheetRels = relsMap(\n await load(`${dirOf(sheetPath)}/_rels/${sheetPath.split('/').pop()}.rels`),\n dirOf(sheetPath),\n )\n // Find the worksheet's drawing relationship, then its charts.\n const sheetDoc = await load(sheetPath)\n const drawingEl = sheetDoc ? tag(sheetDoc, 'drawing') : undefined\n const drawRid =\n drawingEl?.getAttributeNS(\n 'http://schemas.openxmlformats.org/officeDocument/2006/relationships',\n 'id',\n ) || drawingEl?.getAttribute('r:id')\n const drawingPath = drawRid ? sheetRels.get(drawRid) : undefined\n if (!drawingPath) continue\n const charts = await parseDrawing(drawingPath, load)\n if (charts.length) result.set(name, charts)\n }\n return result\n}\n"],"mappings":"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"}
|
package/dist/lib/xmind.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xmind.js","names":[],"sources":["../../src/lib/xmind.ts"],"sourcesContent":["/**\n * In-browser XMind workbook reader — no dependency beyond the project's own ZIP reader.\n *\n * An `.xmind` file is a ZIP container (like docx / appx). Its content lives in one of two members:\n * - `content.json` — the modern format (XMind 2020+/Zen): a JSON array of sheets, each with a `rootTopic`\n * whose `children.attached` (and `children.detached`) nest sub-topics. A topic carries `title`, optional\n * `href` (hyperlink), `notes.plain.content` (or `notes.realHTML.content`), and `labels`.\n * - `content.xml` — the legacy format: `<xmap-content><sheet><topic><title/><children><topics\n * type=\"attached\"><topic>…</topic></topics></children></topic></sheet></xmap-content>`.\n *\n * We unzip just the content member with the project's ZIP reader (lib/zip.ts — browser-native DEFLATE) and\n * normalize it into the shared {@link MindMap} tree, so the existing MindMapView renders it exactly like an\n * OPML / FreeMind mind map (a single right-growing tree; multi-sheet workbooks get a synthetic root). The\n * structure was checked against a real XMind file (both content.json and content.xml present).\n *\n * Used by XmindViewer (the side viewer's XMind mode): the bytes come from the same download endpoint as any\n * other file, are unzipped + parsed entirely client-side, and never leave the browser.\n */\nimport { readZip, extractZipEntry, type ZipEntry } from '@/lib/zip'\nimport type { MindMap, MindNode } from '@/lib/mindmap'\n\n/** Thrown when the bytes aren't a readable XMind workbook (not a zip, or no content member inside). */\nexport class NotXmindError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'NotXmindError'\n }\n}\n\nexport async function parseXmind(buf: ArrayBuffer, fallbackTitle = ''): Promise<MindMap> {\n let entries: ZipEntry[]\n try {\n entries = readZip(buf)\n } catch {\n throw new NotXmindError('不是有效的 XMind 文件(无法读取 ZIP 容器)')\n }\n const find = (path: string): ZipEntry | undefined =>\n entries.find((e) => e.path.toLowerCase() === path.toLowerCase())\n\n const jsonEntry = find('content.json')\n if (jsonEntry) {\n const text = new TextDecoder('utf-8').decode(await extractZipEntry(buf, jsonEntry))\n return fromContentJson(text, fallbackTitle)\n }\n const xmlEntry = find('content.xml')\n if (xmlEntry) {\n const text = new TextDecoder('utf-8').decode(await extractZipEntry(buf, xmlEntry))\n return fromContentXml(text, fallbackTitle)\n }\n throw new NotXmindError('未找到 content.json / content.xml(不是 XMind 文件)')\n}\n\n// ── shared helpers ────────────────────────────────────────────────────────────────────────────\nfunction makeIdGen(): () => number {\n let n = 0\n return () => n++\n}\nfunction countNodes(n: MindNode): number {\n let c = 1\n for (const k of n.children) c += countNodes(k)\n return c\n}\nfunction flatten(s: string | null | undefined): string {\n return (s || '').replace(/\\s+/g, ' ').trim()\n}\nfunction stripTags(s: string): string {\n return s.replace(/<[^>]*>/g, ' ')\n}\n\n/** Fold a topic's note + labels into the single note string MindNode exposes (shown as a hover tooltip). */\nfunction noteOf(plain: string, labels: string[]): string | undefined {\n const parts: string[] = []\n if (plain) parts.push(plain)\n if (labels.length) parts.push(`标签: ${labels.join(', ')}`)\n return parts.length ? parts.join('\\n') : undefined\n}\n\n// ── content.json (modern) ───────────────────────────────────────────────────────────────────────\ninterface JsonTopic {\n title?: string\n href?: string\n notes?: { plain?: { content?: string }; realHTML?: { content?: string } }\n labels?: unknown\n children?: { attached?: JsonTopic[]; detached?: JsonTopic[] }\n}\ninterface JsonSheet {\n title?: string\n rootTopic?: JsonTopic\n}\n\nfunction jsonTopicToNode(t: JsonTopic, id: () => number): MindNode {\n const children: MindNode[] = []\n const att = t.children?.attached\n if (Array.isArray(att)) for (const c of att) children.push(jsonTopicToNode(c, id))\n const det = t.children?.detached\n if (Array.isArray(det)) for (const c of det) children.push(jsonTopicToNode(c, id))\n\n const plain =\n flatten(t.notes?.plain?.content) || flatten(stripTags(t.notes?.realHTML?.content || ''))\n const labels = Array.isArray(t.labels)\n ? t.labels.filter((l): l is string => typeof l === 'string')\n : []\n return {\n id: id(),\n text: flatten(t.title),\n note: noteOf(plain, labels),\n link: typeof t.href === 'string' && t.href ? t.href : undefined,\n children,\n }\n}\n\nfunction fromContentJson(text: string, fallbackTitle: string): MindMap {\n let data: unknown\n try {\n data = JSON.parse(text)\n } catch {\n throw new NotXmindError('content.json 解析失败(JSON 无效)')\n }\n // Modern content.json is an array of sheets; be tolerant of a single-sheet object.\n const sheets: JsonSheet[] = Array.isArray(data) ? (data as JsonSheet[]) : [data as JsonSheet]\n const valid = sheets.filter((s) => s && s.rootTopic)\n if (!valid.length) throw new NotXmindError('XMind 内容为空(无 rootTopic)')\n\n const id = makeIdGen()\n let root: MindNode\n if (valid.length === 1) {\n root = jsonTopicToNode(valid[0].rootTopic as JsonTopic, id)\n if (!root.text) root.text = flatten(valid[0].title) || fallbackTitle || 'XMind'\n } else {\n // Multi-sheet workbook → a synthetic root holding each sheet's tree.\n root = {\n id: id(),\n text: fallbackTitle || 'XMind 工作簿',\n children: valid.map((s) => {\n const n = jsonTopicToNode(s.rootTopic as JsonTopic, id)\n if (!n.text) n.text = flatten(s.title) || '画布'\n return n\n }),\n }\n }\n return { format: 'xmind', title: root.text, root, count: countNodes(root) }\n}\n\n// ── content.xml (legacy) ──────────────────────────────────────────────────────────────────────\nfunction childByTag(el: Element, tag: string): Element | null {\n for (const c of Array.from(el.children)) if (c.tagName.toLowerCase() === tag) return c\n return null\n}\n\nfunction xmlTopicToNode(topic: Element, id: () => number): MindNode {\n const title = childByTag(topic, 'title')\n const notesEl = childByTag(topic, 'notes')\n const plainEl = notesEl ? childByTag(notesEl, 'plain') : null\n const children: MindNode[] = []\n // <children><topics type=\"attached\"><topic>…</topic></topics></children>\n const childrenEl = childByTag(topic, 'children')\n if (childrenEl) {\n for (const topics of Array.from(childrenEl.children)) {\n if (topics.tagName.toLowerCase() !== 'topics') continue\n for (const c of Array.from(topics.children)) {\n if (c.tagName.toLowerCase() === 'topic') children.push(xmlTopicToNode(c, id))\n }\n }\n }\n return {\n id: id(),\n text: flatten(title?.textContent),\n note: noteOf(flatten(plainEl?.textContent), []),\n link: topic.getAttribute('xlink:href') || topic.getAttribute('href') || undefined,\n children,\n }\n}\n\nfunction fromContentXml(text: string, fallbackTitle: string): MindMap {\n if (typeof DOMParser === 'undefined') throw new NotXmindError('当前环境不支持解析 XMind XML')\n const doc = new DOMParser().parseFromString(text, 'application/xml')\n if (doc.getElementsByTagName('parsererror').length) {\n throw new NotXmindError('content.xml 解析失败(XML 无效)')\n }\n const sheets = Array.from(doc.getElementsByTagName('sheet'))\n const roots: { title: string; topic: Element }[] = []\n for (const sheet of sheets) {\n const topic = childByTag(sheet, 'topic')\n if (topic) roots.push({ title: flatten(childByTag(sheet, 'title')?.textContent), topic })\n }\n if (!roots.length) throw new NotXmindError('XMind 内容为空(无 topic)')\n\n const id = makeIdGen()\n let root: MindNode\n if (roots.length === 1) {\n root = xmlTopicToNode(roots[0].topic, id)\n if (!root.text) root.text = roots[0].title || fallbackTitle || 'XMind'\n } else {\n root = {\n id: id(),\n text: fallbackTitle || 'XMind 工作簿',\n children: roots.map((r) => {\n const n = xmlTopicToNode(r.topic, id)\n if (!n.text) n.text = r.title || '画布'\n return n\n }),\n }\n }\n return { format: 'xmind', title: root.text, root, count: countNodes(root) }\n}\n"],"mappings":"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"}
|
package/dist/lib/zip.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zip.js","names":[],"sources":["../../src/lib/zip.ts"],"sourcesContent":["/**\n * Minimal in-browser ZIP reader — no dependency, no WASM.\n *\n * Parses the archive's central directory to list entries, then extracts them lazily using the\n * browser-native {@link DecompressionStream} for DEFLATE. Covers what conversation artifacts actually\n * ship: STORE (uncompressed) and DEFLATE, ASCII/UTF-8 names, and the ZIP64 size/offset extensions.\n * Encrypted entries and unusual compression methods are surfaced as errors rather than mis-read.\n *\n * Used by ZipBrowser (the side viewer's archive mode): the bytes come from the same download endpoint\n * as any other file, are decompressed here entirely client-side, and never leave the browser.\n */\n\nimport { aesCtrXor } from '@/lib/aes'\nimport { decodeFileNameBytes } from '@/lib/encoding'\n\nconst SIG_EOCD = 0x06054b50 // end of central directory record\nconst SIG_EOCD64 = 0x06064b50 // ZIP64 end of central directory record\nconst SIG_EOCD64_LOC = 0x07064b50 // ZIP64 EOCD locator\nconst SIG_CDH = 0x02014b50 // central directory file header\nconst SIG_LFH = 0x04034b50 // local file header\n\n/** How an entry is encrypted: none, legacy PKWARE ZipCrypto, or WinZip AES. */\nexport type ZipEncryption = 'none' | 'zipcrypto' | 'aes'\n\nexport interface ZipEntry {\n /** Full path within the archive, forward-slashed, e.g. \"src/app.ts\". */\n path: string\n /** Basename (last path segment). */\n name: string\n /** Uncompressed size in bytes. */\n size: number\n /** Compressed size in bytes (for encrypted entries this includes the crypto header/salt/trailer). */\n compressedSize: number\n /** EFFECTIVE compression method: 0 = stored, 8 = deflate (for AES this is the real method from the extra field, not 99). */\n method: number\n /** Directory entry (its path ends with \"/\"). */\n isDir: boolean\n /** Entry is encrypted (true when {@link encryption} !== 'none'). Kept for callers that only need the flag. */\n encrypted: boolean\n /** Encryption scheme — decrypt needs the password (see {@link extractZipEntry}). */\n encryption: ZipEncryption\n /** CRC-32 from the directory (the ZipCrypto password-check byte is its high byte). */\n crc32: number\n /** WinZip AES parameters (only when {@link encryption} === 'aes'). */\n aes?: { strength: 1 | 2 | 3 }\n /** Byte offset of the local file header (where the data lives). */\n localOffset: number\n}\n\n/** Thrown when an entry is encrypted and no password was supplied — the UI prompts for one. */\nexport class ZipEncryptedError extends Error {\n constructor(message = '该条目已加密,请输入密码') {\n super(message)\n this.name = 'ZipEncryptedError'\n }\n}\n/** Thrown when the supplied password fails the archive's verification (wrong password). */\nexport class ZipPasswordError extends Error {\n constructor(message = '密码错误') {\n super(message)\n this.name = 'ZipPasswordError'\n }\n}\n\n/** Is in-browser inflate available? (DecompressionStream — all current browsers; absent under SSR.) */\nexport function zipSupported(): boolean {\n return typeof DecompressionStream !== 'undefined'\n}\n\n/** Read a little-endian unsigned 64-bit value as a JS number (exact up to 2^53, plenty for our sizes). */\nfunction readU64(view: DataView, off: number): number {\n return view.getUint32(off + 4, true) * 0x100000000 + view.getUint32(off, true)\n}\n\n/** Scan backward from the tail for the EOCD signature (it sits before a variable-length comment). */\nfunction findEocd(view: DataView): number {\n const len = view.byteLength\n const min = Math.max(0, len - 22 - 0xffff)\n for (let i = len - 22; i >= min; i--) {\n if (view.getUint32(i, true) === SIG_EOCD) return i\n }\n throw new Error('不是有效的 ZIP 文件(找不到结尾记录)')\n}\n\n/** Patch any 0xFFFFFFFF sentinel size/offset from the ZIP64 extra field (tag 0x0001, fields in order). */\nfunction applyZip64Extra(\n view: DataView,\n start: number,\n len: number,\n cur: { size: number; compressedSize: number; localOffset: number },\n): void {\n const end = start + len\n let o = start\n while (o + 4 <= end) {\n const id = view.getUint16(o, true)\n const sz = view.getUint16(o + 2, true)\n if (id === 0x0001) {\n let f = o + 4\n if (cur.size === 0xffffffff) {\n cur.size = readU64(view, f)\n f += 8\n }\n if (cur.compressedSize === 0xffffffff) {\n cur.compressedSize = readU64(view, f)\n f += 8\n }\n if (cur.localOffset === 0xffffffff) {\n cur.localOffset = readU64(view, f)\n }\n return\n }\n o += 4 + sz\n }\n}\n\n/** Read the WinZip AES extra field (id 0x9901) from an entry's extra data: strength + real method. */\nfunction parseAesExtra(\n view: DataView,\n start: number,\n len: number,\n): { strength: 1 | 2 | 3; method: number } | null {\n const end = start + len\n let o = start\n while (o + 4 <= end) {\n const id = view.getUint16(o, true)\n const sz = view.getUint16(o + 2, true)\n if (id === 0x9901 && o + 11 <= end) {\n // data: [0-1] version, [2-3] vendor \"AE\", [4] strength (1/2/3), [5-6] actual compression method\n const strength = view.getUint8(o + 8)\n const method = view.getUint16(o + 9, true)\n if (strength >= 1 && strength <= 3) return { strength: strength as 1 | 2 | 3, method }\n }\n o += 4 + sz\n }\n return null\n}\n\n/** Parse the central directory into a flat entry list. Throws if the bytes aren't a readable ZIP. */\nexport function readZip(buf: ArrayBuffer): ZipEntry[] {\n const view = new DataView(buf)\n if (view.byteLength < 22) throw new Error('文件太小,不是有效的 ZIP')\n const eocd = findEocd(view)\n\n let cdCount = view.getUint16(eocd + 10, true)\n let cdOffset = view.getUint32(eocd + 16, true)\n\n // ZIP64: sentinels in the classic EOCD redirect to a 64-bit record via the locator just before it.\n if (cdCount === 0xffff || cdOffset === 0xffffffff) {\n const loc = eocd - 20\n if (loc >= 0 && view.getUint32(loc, true) === SIG_EOCD64_LOC) {\n const rec = readU64(view, loc + 8)\n if (rec >= 0 && rec + 56 <= view.byteLength && view.getUint32(rec, true) === SIG_EOCD64) {\n cdCount = readU64(view, rec + 32)\n cdOffset = readU64(view, rec + 48)\n }\n }\n }\n\n const bytes = new Uint8Array(buf)\n const entries: ZipEntry[] = []\n let p = cdOffset\n for (let i = 0; i < cdCount; i++) {\n if (p + 46 > view.byteLength || view.getUint32(p, true) !== SIG_CDH) break\n const flags = view.getUint16(p + 8, true)\n const method = view.getUint16(p + 10, true)\n const fnLen = view.getUint16(p + 28, true)\n const extraLen = view.getUint16(p + 30, true)\n const commentLen = view.getUint16(p + 32, true)\n const cur = {\n size: view.getUint32(p + 24, true),\n compressedSize: view.getUint32(p + 20, true),\n localOffset: view.getUint32(p + 42, true),\n }\n // bit 11 flags UTF-8 names, but legacy zips (esp. from Chinese Windows) store GBK without it — so we\n // sniff the encoding (UTF-8 → GBK → \\uXXXX escape) rather than force UTF-8. Backslashes normalize to '/'\n // (the spec mandates '/', but Windows' Compress-Archive emits '\\', which would flatten a nested tree).\n const path = decodeFileNameBytes(bytes.subarray(p + 46, p + 46 + fnLen), true)\n if (\n cur.size === 0xffffffff ||\n cur.compressedSize === 0xffffffff ||\n cur.localOffset === 0xffffffff\n ) {\n applyZip64Extra(view, p + 46 + fnLen, extraLen, cur)\n }\n // Encryption: AES (method 99 + the 0x9901 extra, which carries the REAL method) vs legacy ZipCrypto\n // (general-purpose flag bit 0). For AES the effective method becomes the real one from the extra.\n const crc32 = view.getUint32(p + 16, true)\n let encryption: ZipEncryption = 'none'\n let effMethod = method\n let aes: { strength: 1 | 2 | 3 } | undefined\n if (method === 99) {\n const info = parseAesExtra(view, p + 46 + fnLen, extraLen)\n if (info) {\n encryption = 'aes'\n effMethod = info.method\n aes = { strength: info.strength }\n }\n } else if ((flags & 0x1) !== 0) {\n encryption = 'zipcrypto'\n }\n entries.push({\n path,\n name: path.replace(/\\/+$/, '').split('/').pop() ?? path,\n size: cur.size,\n compressedSize: cur.compressedSize,\n method: effMethod,\n isDir: path.endsWith('/'),\n encrypted: encryption !== 'none',\n encryption,\n crc32,\n aes,\n localOffset: cur.localOffset,\n })\n p += 46 + fnLen + extraLen + commentLen\n }\n return entries\n}\n\n/** Inflate a raw DEFLATE stream via the browser. */\nasync function inflateRaw(data: Uint8Array): Promise<Uint8Array> {\n const stream = new Blob([data]).stream().pipeThrough(new DecompressionStream('deflate-raw'))\n return new Uint8Array(await new Response(stream).arrayBuffer())\n}\n\n// ── encryption ────────────────────────────────────────────────────────────────────────────────\n// Two schemes appear in the wild: legacy PKWARE \"ZipCrypto\" (a weak keystream cipher, still what most\n// \"password-protected zip\" tools default to) and WinZip AES (PBKDF2-HMAC-SHA1 → AES-CTR, used by 7-Zip/WinZip).\n// Both are decrypted here entirely client-side; the password never leaves the browser.\n\nconst CRC_TABLE = /* @__PURE__ */ (() => {\n const t = new Uint32Array(256)\n for (let n = 0; n < 256; n++) {\n let c = n\n for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1\n t[n] = c >>> 0\n }\n return t\n})()\nconst crc32Byte = (crc: number, b: number): number =>\n ((crc >>> 8) ^ CRC_TABLE[(crc ^ b) & 0xff]) >>> 0\n\n// PKWARE ZipCrypto: three 32-bit keys evolved one byte at a time.\nclass ZipCryptoState {\n private k0 = 0x12345678\n private k1 = 0x23456789\n private k2 = 0x34567890\n constructor(pw: Uint8Array) {\n for (const b of pw) this.update(b)\n }\n private update(b: number): void {\n this.k0 = crc32Byte(this.k0, b)\n this.k1 = (this.k1 + (this.k0 & 0xff)) >>> 0\n this.k1 = (Math.imul(this.k1, 134775813) + 1) >>> 0\n this.k2 = crc32Byte(this.k2, (this.k1 >>> 24) & 0xff)\n }\n decryptByte(cipher: number): number {\n const t = (this.k2 | 2) & 0xffff\n const plain = (cipher ^ ((Math.imul(t, t ^ 1) >>> 8) & 0xff)) & 0xff\n this.update(plain)\n return plain\n }\n}\n\n// Decrypt a ZipCrypto blob: the first 12 bytes are a header whose last byte must equal the CRC's high byte\n// (the password check); returns the remaining plaintext (still deflate-compressed / stored).\nfunction zipCryptoDecrypt(data: Uint8Array, pw: Uint8Array, checkByte: number): Uint8Array {\n if (data.length < 12) throw new ZipPasswordError()\n const st = new ZipCryptoState(pw)\n const out = new Uint8Array(data.length)\n for (let i = 0; i < data.length; i++) out[i] = st.decryptByte(data[i])\n if (out[11] !== (checkByte & 0xff)) throw new ZipPasswordError()\n return out.subarray(12)\n}\n\n// Decrypt a WinZip AES blob: [salt][pwVerify(2)][ciphertext][authCode(10)]. PBKDF2-HMAC-SHA1(1000) derives\n// encKey ‖ authKey ‖ pwVerify(2); we check pwVerify (wrong password) and CTR-decrypt (auth code not verified).\nasync function winzipAesDecrypt(\n data: Uint8Array,\n pw: Uint8Array,\n strength: 1 | 2 | 3,\n): Promise<Uint8Array> {\n const saltLen = [8, 12, 16][strength - 1]\n const keyLen = [16, 24, 32][strength - 1]\n if (data.length < saltLen + 2 + 10) throw new ZipPasswordError()\n if (typeof crypto === 'undefined' || !crypto.subtle) {\n throw new Error('当前环境不支持 AES 解密(缺少 WebCrypto)')\n }\n const salt = data.subarray(0, saltLen)\n const pwVerify = data.subarray(saltLen, saltLen + 2)\n const cipher = data.subarray(saltLen + 2, data.length - 10)\n const baseKey = await crypto.subtle.importKey('raw', pw, 'PBKDF2', false, ['deriveBits'])\n const derived = new Uint8Array(\n await crypto.subtle.deriveBits(\n { name: 'PBKDF2', hash: 'SHA-1', salt, iterations: 1000 },\n baseKey,\n (keyLen * 2 + 2) * 8,\n ),\n )\n if (derived[keyLen * 2] !== pwVerify[0] || derived[keyLen * 2 + 1] !== pwVerify[1]) {\n throw new ZipPasswordError()\n }\n return aesCtrXor(derived.subarray(0, keyLen), cipher)\n}\n\n/** Decompress one entry's bytes. Reads the local header (its name/extra lengths can differ from the central\n * directory's) to find the data, decrypts it first when encrypted (ZipCrypto / WinZip AES — needs `password`),\n * then STORE-copies or DEFLATE-inflates. Throws {@link ZipEncryptedError} if a password is required but missing,\n * and {@link ZipPasswordError} if the supplied password is wrong. */\nexport async function extractZipEntry(\n buf: ArrayBuffer,\n e: ZipEntry,\n password?: string,\n): Promise<Uint8Array> {\n if (e.isDir || e.size === 0) return new Uint8Array(0)\n\n const view = new DataView(buf)\n if (e.localOffset + 30 > view.byteLength || view.getUint32(e.localOffset, true) !== SIG_LFH) {\n throw new Error('本地文件头损坏')\n }\n const fnLen = view.getUint16(e.localOffset + 26, true)\n const extraLen = view.getUint16(e.localOffset + 28, true)\n const start = e.localOffset + 30 + fnLen + extraLen\n if (start + e.compressedSize > buf.byteLength) throw new Error('条目数据超出文件范围')\n\n // The raw local data; for an encrypted entry this is the crypto blob (header/salt + ciphertext + trailer).\n let data: Uint8Array = new Uint8Array(buf, start, e.compressedSize)\n\n if (e.encryption !== 'none') {\n if (!password) throw new ZipEncryptedError()\n const pw = new TextEncoder().encode(password)\n if (e.encryption === 'zipcrypto') {\n // ZipCrypto password-check byte: normally the CRC's high byte, but when a data descriptor is used\n // (flag bit 3, CRC unknown at write time) it's the high byte of the DOS mod-time instead.\n const lflags = view.getUint16(e.localOffset + 6, true)\n const ltime = view.getUint16(e.localOffset + 10, true)\n const checkByte = lflags & 0x8 ? (ltime >> 8) & 0xff : (e.crc32 >>> 24) & 0xff\n data = zipCryptoDecrypt(data, pw, checkByte)\n } else {\n data = await winzipAesDecrypt(data, pw, e.aes?.strength ?? 3)\n }\n }\n\n if (e.method === 0) return data.slice() // stored — copy out so callers own the bytes\n if (e.method === 8) {\n if (!zipSupported()) throw new Error('当前浏览器不支持在前端解压(DecompressionStream 缺失)')\n return inflateRaw(data) // deflate\n }\n throw new Error(`不支持的压缩方式(method ${e.method})`)\n}\n"],"mappings":"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"}
|