@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":"pptxNavigation.js","names":[],"sources":["../../src/lib/pptxNavigation.ts"],"sourcesContent":["import { buildTextIndex, type PresentationData, type TextIndexEntry } from '@aiden0z/pptx-renderer'\n\nexport interface PptxNavigationItem {\n index: number\n title: string\n}\n\nfunction cleanTitle(value: string): string {\n const text = value.replace(/\\s+/g, ' ').trim()\n return text.length > 72 ? `${text.slice(0, 69)}…` : text\n}\n\nfunction isUsefulTitle(entry: TextIndexEntry): boolean {\n const text = cleanTitle(entry.text)\n return !!text && !/^(?:slide\\s*)?\\d+$/i.test(text)\n}\n\n/** Derive one concise label per slide from the renderer's parsed text model. */\nexport function buildPptxNavigation(presentation: PresentationData): PptxNavigationItem[] {\n const bySlide = new Map<number, TextIndexEntry[]>()\n for (const entry of buildTextIndex(presentation)) {\n if (!isUsefulTitle(entry)) continue\n const group = bySlide.get(entry.slideIndex) ?? []\n group.push(entry)\n bySlide.set(entry.slideIndex, group)\n }\n\n return presentation.slides.map((_slide, index) => {\n const candidates = (bySlide.get(index) ?? []).sort(\n (a, b) => a.bounds.y - b.bounds.y || a.bounds.x - b.bounds.x,\n )\n const upperTitle = candidates.find((entry) => entry.bounds.y < presentation.height * 0.4)\n const title = cleanTitle((upperTitle ?? candidates[0])?.text ?? '')\n return { index, title: title || `幻灯片 ${index + 1}` }\n })\n}\n"],"mappings":";;AAOA,SAAS,EAAW,GAAuB;CACzC,IAAM,IAAO,EAAM,QAAQ,QAAQ,GAAG,CAAC,CAAC,KAAK;CAC7C,OAAO,EAAK,SAAS,KAAK,GAAG,EAAK,MAAM,GAAG,EAAE,EAAE,KAAK;AACtD;AAEA,SAAS,EAAc,GAAgC;CACrD,IAAM,IAAO,EAAW,EAAM,IAAI;CAClC,OAAO,CAAC,CAAC,KAAQ,CAAC,sBAAsB,KAAK,CAAI;AACnD;AAGA,SAAgB,EAAoB,GAAsD;CACxF,IAAM,oBAAU,IAAI,IAA8B;CAClD,KAAK,IAAM,KAAS,EAAe,CAAY,GAAG;EAChD,IAAI,CAAC,EAAc,CAAK,GAAG;EAC3B,IAAM,IAAQ,EAAQ,IAAI,EAAM,UAAU,KAAK,CAAC;EAEhD,AADA,EAAM,KAAK,CAAK,GAChB,EAAQ,IAAI,EAAM,YAAY,CAAK;CACrC;CAEA,OAAO,EAAa,OAAO,KAAK,GAAQ,MAAU;EAChD,IAAM,KAAc,EAAQ,IAAI,CAAK,KAAK,CAAC,EAAA,CAAG,MAC3C,GAAG,MAAM,EAAE,OAAO,IAAI,EAAE,OAAO,KAAK,EAAE,OAAO,IAAI,EAAE,OAAO,CAC7D;EAGA,OAAO;GAAE;GAAO,OADF,GADK,EAAW,MAAM,MAAU,EAAM,OAAO,IAAI,EAAa,SAAS,EAC3D,KAAc,EAAW,GAAA,EAAK,QAAQ,EACzC,KAAS,OAAO,IAAQ;EAAI;CACrD,CAAC;AACH"}
|
package/dist/lib/qmc.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qmc.js","names":[],"sources":["../../src/lib/qmc.ts"],"sourcesContent":["/**\n * QQ 音乐加密音频(QMCv2)的纯前端解密器 —— 零依赖,照搬 unlock-music / parakeet-crypto 的算法。\n *\n * QQ 音乐客户端下载的「会员歌曲」是被加密的,扩展名形如 `.mflac`(FLAC)、`.mgg`(OGG/Vorbis)、\n * `.qmcflac`/`.qmc0` 等。文件主体是用一段「文件密钥」做的逐字节流加密,密钥本身(EKey)以 base64\n * 形式追加在文件末尾。解出原始密钥后即可还原成标准的 FLAC/OGG/MP3 字节,丢给 <audio> 直接播放。\n *\n * 整条链路(均在浏览器内完成,本函数纯计算、不联网):\n * 1. {@link parseFooter} 解析文件尾,取出 base64 的 EKey 与尾部长度(PC v1 / Android QTag 两种容器)。\n * 2. {@link decryptEKey} 还原文件密钥:base64 解码 → 可选的 EncV2 两轮 TEA → EncV1(8 字节明文头 +\n * 其余走 TC-TEA/CBC 解密,TEA 密钥由「simple key」与密钥头交织而成)。\n * 3. 按密钥长度选择流密码:>300 字节用 {@link rc4Decrypt}(分段 RC4 + 跳表),否则用 {@link mapDecrypt}\n * (把密钥摊成 128 字节查找表逐字节异或)。\n * 4. 去掉尾部,得到明文音频,再用文件头 magic 判定真实格式(fLaC / OggS / ID3·MP3 / ftyp·M4A / WAV)。\n *\n * 这套算法已用本仓库的真实样例逐字节验证:`.mflac` → 合法 FLAC、`.mgg` → 合法 OGG/Vorbis(ffprobe 可解)。\n * 解密是 XOR 流密码,与「加密」同构;这里只做解密。详见 https://git.unlock-music.dev/um/web 。\n */\n\n// ───────────────────────── TC-TEA(CBC,QQ 私有填充)─────────────────────────\n// 标准 TEA 分组(8 字节、大端、16 轮),外层是腾讯私有的 CBC 变体 + 「PadLen|Padding|Salt|Body|Zero」填充。\nconst TEA_ROUNDS = 16\nconst TEA_DELTA = 0x9e3779b9\nconst u32 = (x: number): number => x >>> 0\nconst add32 = (a: number, b: number): number => u32(a + b)\nconst sub32 = (a: number, b: number): number => u32(a - b)\n\nfunction readU32BE(b: Uint8Array, o: number): number {\n return u32((b[o] << 24) | (b[o + 1] << 16) | (b[o + 2] << 8) | b[o + 3])\n}\nfunction writeU32BE(b: Uint8Array, o: number, v: number): void {\n b[o] = (v >>> 24) & 0xff\n b[o + 1] = (v >>> 16) & 0xff\n b[o + 2] = (v >>> 8) & 0xff\n b[o + 3] = v & 0xff\n}\n// ((v<<4)+k1) ^ (sum+v) ^ ((v>>5)+k2) —— TEA 单轮的一半,加减都按 u32 回绕。\nfunction teaRound(v: number, sum: number, k1: number, k2: number): number {\n return u32(add32(u32(v << 4), k1) ^ add32(sum, v) ^ add32(v >>> 5, k2))\n}\nfunction teaEcbDecrypt(block: Uint8Array, off: number, k: [number, number, number, number]): void {\n let y = readU32BE(block, off)\n let z = readU32BE(block, off + 4)\n let sum = u32(TEA_DELTA * TEA_ROUNDS)\n for (let i = 0; i < TEA_ROUNDS; i++) {\n z = sub32(z, teaRound(y, sum, k[2], k[3]))\n y = sub32(y, teaRound(z, sum, k[0], k[1]))\n sum = sub32(sum, TEA_DELTA)\n }\n writeU32BE(block, off, y)\n writeU32BE(block, off + 4, z)\n}\n\nconst TEA_SALT_LEN = 2\nconst TEA_ZERO_LEN = 7\nconst TEA_FIXED_PADDING = 1 + TEA_SALT_LEN + TEA_ZERO_LEN\n\n/**\n * TC-TEA/CBC 解密。`key` 必须是 16 字节(4×u32 大端)。失败(长度不合法 / 尾部校验位非零)返回 null。\n * 明文布局:`PadLen(1) | Padding(0-7) | Salt(2) | Body | Zero(7)`,PadLen 取自首字节低 3 位。\n */\nfunction teaCbcDecrypt(encrypted: Uint8Array, key: Uint8Array): Uint8Array | null {\n if (key.length < 16) return null\n const k: [number, number, number, number] = [\n readU32BE(key, 0),\n readU32BE(key, 4),\n readU32BE(key, 8),\n readU32BE(key, 12),\n ]\n const len = encrypted.length\n if (len < TEA_FIXED_PADDING || len % 8 !== 0) return null\n const buf = encrypted.slice()\n teaEcbDecrypt(buf, 0, k)\n for (let i = 8; i < len; i += 8) {\n for (let j = 0; j < 8; j++) buf[i + j] ^= buf[i - 8 + j] // XOR 前一明文块\n teaEcbDecrypt(buf, i, k)\n }\n for (let i = 8; i < len; i++) buf[i] ^= encrypted[i - 8] // 收尾:XOR 前一密文块\n const padSize = buf[0] & 0x7\n const start = 1 + padSize + TEA_SALT_LEN\n const end = len - TEA_ZERO_LEN\n if (start > end) return null\n for (let i = end; i < len; i++) if (buf[i] !== 0) return null // 末 7 字节必须全 0\n return buf.slice(start, end)\n}\n\n// ───────────────────────── EKey 解密 ─────────────────────────\n// EncV2 前缀:base64(\"QQMusic EncV2,Key:\")。命中则先用两把固定 TEA 密钥各解一轮,再走 EncV1。\nconst EKEY_V2_PREFIX = 'UVFNdXNpYyBFbmNWMixLZXk6'\nconst ENC_V2_KEY_1 = strBytes('386ZJY!@#*$%^&)(')\nconst ENC_V2_KEY_2 = strBytes('**#!(#$%&^a1cZ,T')\nconst EKEY_SEED = 106 // simple key 的起始种子(QMC 固定值)\nconst MAX_EKEY_LEN = 0x500\n\nfunction strBytes(s: string): Uint8Array {\n const out = new Uint8Array(s.length)\n for (let i = 0; i < s.length; i++) out[i] = s.charCodeAt(i) & 0xff\n return out\n}\n// Tolerant base64 → bytes: drop anything outside the base64 alphabet (whitespace, the NUL padding the inner\n// EncV2 key carries, …) and re-pad, so a slightly-dirty string decodes instead of throwing in atob().\nfunction base64ToBytes(s: string): Uint8Array {\n const clean = s.replace(/[^A-Za-z0-9+/]/g, '')\n const padded = clean + '='.repeat((4 - (clean.length % 4)) % 4)\n const bin = atob(padded)\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// simple key:8 字节,第 i 个 = ⌊|tan(106 + i*0.1)| * 100⌋(用 double,与 C++ 原版一致)。\nfunction makeSimpleKey(): Uint8Array {\n const out = new Uint8Array(8)\n for (let i = 0; i < 8; i++)\n out[i] = Math.trunc(Math.abs(Math.tan(EKEY_SEED + i * 0.1)) * 100) & 0xff\n return out\n}\nconst SIMPLE_KEY = makeSimpleKey()\n\n// EncV1:前 8 字节是明文密钥头,TEA 密钥由 simple key 与该头逐字节交织(simple[0],head[0],simple[1],…),\n// 其余字节走 TC-TEA/CBC 解密,结果 = 头 + 明文。\nfunction ekeyDecryptV1(raw: Uint8Array): Uint8Array | null {\n if (raw.length < 8) return null\n const header = raw.slice(0, 8)\n const cipher = raw.slice(8)\n const teaKey = new Uint8Array(16)\n for (let i = 0; i < 8; i++) {\n teaKey[i * 2] = SIMPLE_KEY[i]\n teaKey[i * 2 + 1] = header[i]\n }\n const plain = teaCbcDecrypt(cipher, teaKey)\n if (!plain) return null\n const out = new Uint8Array(8 + plain.length)\n out.set(header, 0)\n out.set(plain, 8)\n return out\n}\n\n/** 把文件尾的 base64 EKey 字符串还原成文件密钥;失败返回 null。 */\nexport function decryptEKey(ekey: string): Uint8Array | null {\n const trimmed = ekey.replace(/\\s+/g, '')\n if (trimmed.startsWith(EKEY_V2_PREFIX)) {\n // EncV2:base64 前缀对应 18 字节明文前缀(24 个 base64 字符正好 3 的倍数,分段解码等价),故直接切串。\n let k: Uint8Array | null = base64ToBytes(trimmed.slice(EKEY_V2_PREFIX.length))\n k = teaCbcDecrypt(k, ENC_V2_KEY_1)\n if (!k) return null\n k = teaCbcDecrypt(k, ENC_V2_KEY_2)\n if (!k) return null\n // 结果是 EncV1 ekey 的 base64 文本(可能带尾部 0,base64ToBytes 会自动忽略非字母表字符)。\n let s = ''\n for (const b of k) s += String.fromCharCode(b)\n return ekeyDecryptV1(base64ToBytes(s))\n }\n return ekeyDecryptV1(base64ToBytes(trimmed))\n}\n\n// ───────────────────────── 文件尾解析 ─────────────────────────\nexport interface QmcFooter {\n /** base64 的 EKey 字符串。 */\n ekey: string\n /** 需要从文件末尾去掉的字节数(密钥块 + 长度/标记)。 */\n tailLen: number\n}\n\nconst td = new TextDecoder('latin1')\n\n/**\n * 解析 QMC 文件尾,取出 EKey。支持两种容器:\n * - **PC v1**:末 4 字节小端 = EKey 长度,其前即 EKey(去尾部 0)。\n * - **Android QTag**:末 4 字节为 ASCII \"QTag\",再往前 4 字节大端为 payload 长度,payload 为\n * `ekey,resourceId,version` 逗号分隔。\n * 解析不出(或像无嵌入密钥的 STag/musicex)返回 null。\n */\nexport function parseFooter(buf: Uint8Array): QmcFooter | null {\n if (buf.length < 8) return null\n // QTag\n if (td.decode(buf.subarray(buf.length - 4)) === 'QTag') {\n const payloadLen = readU32BE(buf, buf.length - 8)\n const tailLen = payloadLen + 8\n if (tailLen > buf.length) return null\n const payload = td.decode(buf.subarray(buf.length - tailLen, buf.length - 8))\n const ekey = payload.split(',')[0]\n return ekey ? { ekey, tailLen } : null\n }\n // PC v1\n const keyLen = u32(\n buf[buf.length - 4] |\n (buf[buf.length - 3] << 8) |\n (buf[buf.length - 2] << 16) |\n (buf[buf.length - 1] << 24),\n )\n if (keyLen === 0 || keyLen > MAX_EKEY_LEN || keyLen + 4 > buf.length) return null\n let end = buf.length - 4\n let start = end - keyLen\n while (end > start && buf[end - 1] === 0) end-- // 去尾部 0\n const ekey = td.decode(buf.subarray(start, end))\n // 粗校验:EKey 应为 base64 字符集\n if (!/^[A-Za-z0-9+/=]+$/.test(ekey)) return null\n return { ekey, tailLen: keyLen + 4 }\n}\n\n// ───────────────────────── Map 流密码(短密钥)─────────────────────────\n// 把文件密钥摊成 128 字节查找表,再逐字节 `byte ^= table[ f(offset) ]`。\nfunction qmc2KeyToTable(fileKey: Uint8Array): Uint8Array {\n const table = new Uint8Array(128)\n const n = fileKey.length\n if (n === 0) return table\n const longKey = new Uint8Array(n)\n let shift = 4\n for (let i = 0; i < n; i++) {\n const v = fileKey[i]\n longKey[i] = (((v << shift) & 0xff) | (v >>> shift)) & 0xff\n shift = (shift + 1) & 0x7\n }\n for (let i = 0; i < 128; i++) table[i] = longKey[(i * i + 0x1162e) % n]\n return table\n}\nfunction mapDecrypt(audio: Uint8Array, fileKey: Uint8Array): void {\n const table = qmc2KeyToTable(fileKey)\n for (let i = 0; i < audio.length; i++) {\n let idx = i\n if (idx > 0x7fff) idx %= 0x7fff\n audio[i] ^= table[idx & 0x7f]\n }\n}\n\n// ───────────────────────── RC4 流密码(长密钥)─────────────────────────\nconst RC4_FIRST_SEG = 0x80\nconst RC4_OTHER_SEG = 0x1400\nconst RC4_KEY_STREAM_LEN = 0x1ff + RC4_OTHER_SEG\n\nfunction rc4KeyStream(key: Uint8Array, outLen: number): Uint8Array {\n const n = key.length\n const s = new Uint8Array(n)\n for (let i = 0; i < n; i++) s[i] = i & 0xff\n let j = 0\n for (let i = 0; i < n; i++) {\n j = (j + s[i] + key[i % n]) % n\n const t = s[i]\n s[i] = s[j]\n s[j] = t\n }\n const out = new Uint8Array(outLen)\n let i = 0\n j = 0\n for (let k = 0; k < outLen; k++) {\n i = (i + 1) % n\n j = (j + s[i]) % n\n const t = s[i]\n s[i] = s[j]\n s[j] = t\n out[k] = s[(s[i] + s[j]) % n]\n }\n return out\n}\nfunction calcKeyHash(key: Uint8Array): number {\n let hash = 1\n for (let i = 0; i < key.length; i++) {\n const v = key[i]\n if (v === 0) continue\n const next = Math.imul(hash, v) >>> 0\n if (next <= hash >>> 0) break\n hash = next\n }\n return hash >>> 0\n}\nfunction rc4Decrypt(audio: Uint8Array, key: Uint8Array): void {\n const keyLen = key.length\n const keyHash = calcKeyHash(key)\n const ks = rc4KeyStream(key, RC4_KEY_STREAM_LEN)\n // 分段密钥:⌊keyHash / (seed*(id+1)) * 100⌋(double)。\n const segKey = (id: number, seed: number): number =>\n seed === 0 ? 0 : Math.floor((keyHash / (seed * (id + 1))) * 100)\n\n let offset = 0\n let pos = 0\n const total = audio.length\n // 段 1(前 0x80 字节):每字节用按位置算出的密钥下标异或。\n if (offset < RC4_FIRST_SEG) {\n const len = Math.min(total - pos, RC4_FIRST_SEG - offset)\n let i = offset\n for (let p = 0; p < len; p++) {\n audio[pos + p] ^= key[segKey(i, key[i % keyLen]) % keyLen]\n i++\n }\n offset += len\n pos += len\n }\n // 对齐到 0x1400 段边界的零头。\n if (offset % RC4_OTHER_SEG !== 0) {\n const len = Math.min(total - pos, RC4_OTHER_SEG - (offset % RC4_OTHER_SEG))\n encodeOther(audio, pos, len, offset)\n offset += len\n pos += len\n }\n // 其余按 0x1400 整段处理。\n while (pos < total) {\n const len = Math.min(total - pos, RC4_OTHER_SEG)\n encodeOther(audio, pos, len, offset)\n offset += len\n pos += len\n }\n\n function encodeOther(buf: Uint8Array, start: number, len: number, off: number): void {\n const segIdx = Math.floor(off / RC4_OTHER_SEG)\n const segOff = off % RC4_OTHER_SEG\n const sk = segKey(segIdx, key[segIdx % keyLen])\n const ksBase = (sk % 512) + segOff // sk & 0x1FF\n for (let p = 0; p < len; p++) buf[start + p] ^= ks[ksBase + p]\n }\n}\n\n// ───────────────────────── 顶层 ─────────────────────────\nexport interface QmcDecrypted {\n data: Uint8Array\n /** 探测出的真实格式:flac / ogg / mp3 / m4a / wav / bin。 */\n format: string\n /** 对应的 MIME,用于 <audio> 的 Blob。 */\n mime: string\n /** 建议的下载扩展名(不含点)。 */\n ext: string\n}\n\nfunction detectFormat(b: Uint8Array): { format: string; mime: string; ext: string } {\n const ascii = (o: number, s: string): boolean => {\n for (let i = 0; i < s.length; i++) if (b[o + i] !== s.charCodeAt(i)) return false\n return true\n }\n if (ascii(0, 'fLaC')) return { format: 'flac', mime: 'audio/flac', ext: 'flac' }\n if (ascii(0, 'OggS')) return { format: 'ogg', mime: 'audio/ogg', ext: 'ogg' }\n if (ascii(0, 'ID3') || (b[0] === 0xff && (b[1] & 0xe0) === 0xe0))\n return { format: 'mp3', mime: 'audio/mpeg', ext: 'mp3' }\n if (b.length > 11 && ascii(4, 'ftyp')) return { format: 'm4a', mime: 'audio/mp4', ext: 'm4a' }\n if (ascii(0, 'RIFF') && ascii(8, 'WAVE')) return { format: 'wav', mime: 'audio/wav', ext: 'wav' }\n return { format: 'bin', mime: 'application/octet-stream', ext: 'bin' }\n}\n\nexport class QmcError extends Error {}\n\n/**\n * 解密一个 QMC 加密音频。`input` 是整个文件字节。成功返回明文音频 + 探测出的格式信息;\n * 解析/解密失败抛 {@link QmcError}(调用方据此给出友好提示)。\n */\nexport function decryptQmc(input: ArrayBuffer | Uint8Array): QmcDecrypted {\n const buf = input instanceof Uint8Array ? input : new Uint8Array(input)\n const footer = parseFooter(buf)\n if (!footer)\n throw new QmcError(\n '未能解析文件尾部的密钥(可能不是 QQ 音乐加密文件,或为需要外部密钥的 STag 格式)',\n )\n const fileKey = decryptEKey(footer.ekey)\n if (!fileKey || fileKey.length === 0) throw new QmcError('EKey 解密失败')\n const audio = buf.slice(0, buf.length - footer.tailLen)\n if (fileKey.length > 300) rc4Decrypt(audio, fileKey)\n else mapDecrypt(audio, fileKey)\n const fmt = detectFormat(audio)\n if (fmt.format === 'bin') throw new QmcError('解密结果不是可识别的音频格式(密钥或格式不支持)')\n return { data: audio, ...fmt }\n}\n"],"mappings":"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"}
|
package/dist/lib/rar.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rar.js","names":[],"sources":["../../src/lib/rar.ts"],"sourcesContent":["/**\n * In-browser RAR reader, wrapping the WASM unrar port (node-unrar-js).\n *\n * RAR is the one archive format in this viewer we don't hand-roll: RAR3/RAR5 decompression is a large,\n * proprietary LZ+filter pipeline, so — unlike zip/7z/tar — we lean on the upstream `unrar` C sources\n * compiled to WebAssembly. The ESM build (`node-unrar-js/esm`) is browser-targeted; we feed it the wasm\n * binary fetched as a same-origin asset (Vite emits `unrar.wasm` via the `?url` import) so nothing loads\n * from a CDN. The module + wasm are pulled in lazily (this file is dynamic-imported by ArchiveBrowser),\n * keeping the ~200KB wasm out of the main bundle until a `.rar` is actually opened.\n *\n * Exposed through the shared {@link OpenedArchive} interface like the other archives. The upstream bytes are\n * decoded entirely client-side; the WASM sandbox never reaches the network or filesystem.\n */\n\nimport { createExtractorFromData, type FileHeader } from 'node-unrar-js'\n// Vite emits the browser-build wasm as a hashed, same-origin asset and gives us its URL.\nimport unrarWasmUrl from 'node-unrar-js/esm/js/unrar.wasm?url'\nimport type { OpenedArchive } from '@/lib/archive'\n\nlet wasmBinary: ArrayBuffer | null = null\n\n/** Fetch (and cache) the unrar wasm binary. */\nasync function getWasm(): Promise<ArrayBuffer> {\n if (!wasmBinary) {\n const res = await fetch(unrarWasmUrl)\n if (!res.ok) throw new Error(`无法加载 unrar.wasm(HTTP ${res.status})`)\n wasmBinary = await res.arrayBuffer()\n }\n return wasmBinary\n}\n\n/** Open a RAR archive: list its entries, extracting each file's bytes on demand via the WASM extractor. */\nexport async function openRar(buf: ArrayBuffer): Promise<OpenedArchive> {\n const extractor = await createExtractorFromData({ wasmBinary: await getWasm(), data: buf })\n // getFileList() / extract() return one-shot generators — materialize the headers up front.\n const headers: FileHeader[] = [...extractor.getFileList().fileHeaders]\n const entries = headers.map((h, i) => {\n const path = h.name.replace(/\\\\/g, '/').replace(/\\/+$/, '')\n return {\n path,\n name: path.split('/').pop() ?? path,\n size: h.flags.directory ? 0 : h.unpSize,\n isDir: h.flags.directory,\n ref: i,\n }\n })\n return {\n entries,\n extract(e) {\n const h = headers[e.ref]\n if (!h || h.flags.directory) return new Uint8Array(0)\n if (h.flags.encrypted) throw new Error('该条目已加密,无法读取')\n // Re-run extraction for just this file (the extractor decompresses predecessors itself for solid archives).\n const files = [...extractor.extract({ files: [h.name] }).files]\n const file = files.find((f) => f.fileHeader.name === h.name) ?? files[0]\n if (!file?.extraction) throw new Error('解压失败(该条目无数据)')\n return file.extraction\n },\n }\n}\n"],"mappings":";;;AAmBA,IAAI,IAAiC;AAGrC,eAAe,IAAgC;CAC7C,IAAI,CAAC,GAAY;EACf,IAAM,IAAM,MAAM,MAAM,CAAY;EACpC,IAAI,CAAC,EAAI,IAAI,MAAU,MAAM,wBAAwB,EAAI,OAAO,EAAE;EAClE,IAAa,MAAM,EAAI,YAAY;CACrC;CACA,OAAO;AACT;AAGA,eAAsB,EAAQ,GAA0C;CACtE,IAAM,IAAY,MAAM,EAAwB;EAAE,YAAY,MAAM,EAAQ;EAAG,MAAM;CAAI,CAAC,GAEpF,IAAwB,CAAC,GAAG,EAAU,YAAY,CAAC,CAAC,WAAW;CAWrE,OAAO;EACL,SAXc,EAAQ,KAAK,GAAG,MAAM;GACpC,IAAM,IAAO,EAAE,KAAK,QAAQ,OAAO,GAAG,CAAC,CAAC,QAAQ,QAAQ,EAAE;GAC1D,OAAO;IACL;IACA,MAAM,EAAK,MAAM,GAAG,CAAC,CAAC,IAAI,KAAK;IAC/B,MAAM,EAAE,MAAM,YAAY,IAAI,EAAE;IAChC,OAAO,EAAE,MAAM;IACf,KAAK;GACP;EACF,CAEE;EACA,QAAQ,GAAG;GACT,IAAM,IAAI,EAAQ,EAAE;GACpB,IAAI,CAAC,KAAK,EAAE,MAAM,WAAW,OAAO,IAAI,WAAY;GACpD,IAAI,EAAE,MAAM,WAAW,MAAU,MAAM,aAAa;GAEpD,IAAM,IAAQ,CAAC,GAAG,EAAU,QAAQ,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,GACxD,IAAO,EAAM,MAAM,MAAM,EAAE,WAAW,SAAS,EAAE,IAAI,KAAK,EAAM;GACtE,IAAI,CAAC,GAAM,YAAY,MAAU,MAAM,cAAc;GACrD,OAAO,EAAK;EACd;CACF;AACF"}
|
package/dist/lib/rawPreview.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rawPreview.js","names":[],"sources":["../../src/lib/rawPreview.ts"],"sourcesContent":["/**\n * Best-effort camera-RAW preview extraction (zero-dependency).\n *\n * Decoding a raw sensor file (CR2/NEF/ARW/DNG/…) into an image requires a full demosaicing pipeline that's\n * out of scope for the browser. But virtually every RAW embeds a ready-to-view JPEG preview (often full-res).\n * This walks the file's JPEG streams properly — skipping each marker segment by its length, including the\n * EXIF thumbnail nested in APP1, so it finds true SOI…EOI boundaries rather than cutting at the first stray\n * `FF D9` — and returns the LARGEST embedded JPEG (the main preview). Imperfect by nature; a file with no\n * embedded JPEG yields null.\n */\n\n// Given an SOI at `start`, walk markers to the real EOI; return the index just past EOI, or -1 if unterminated.\nfunction jpegEnd(b: Uint8Array, start: number): number {\n let i = start + 2 // past the SOI (FF D8)\n const n = b.length\n while (i < n - 1) {\n if (b[i] !== 0xff) {\n i++\n continue\n }\n const marker = b[i + 1]\n if (marker === 0xff) {\n i++ // fill byte\n continue\n }\n if (marker === 0xd9) return i + 2 // EOI\n // Standalone markers with no length payload: TEM (0x01) and RSTn (0xD0–0xD7).\n if (marker === 0x01 || (marker >= 0xd0 && marker <= 0xd7)) {\n i += 2\n continue\n }\n if (i + 3 >= n) return -1\n const len = (b[i + 2] << 8) | b[i + 3]\n if (len < 2) return -1\n if (marker === 0xda) {\n // Start of Scan: skip the header, then scan entropy-coded data for the next real marker (not FF00/RSTn).\n i += 2 + len\n while (i < n - 1) {\n if (b[i] === 0xff) {\n const next = b[i + 1]\n if (next !== 0x00 && !(next >= 0xd0 && next <= 0xd7)) break\n }\n i++\n }\n continue\n }\n i += 2 + len // ordinary segment (APPn, DQT, SOFn, …) — skip by length\n }\n return -1\n}\n\n/** Return the bytes of the largest embedded JPEG in `buf` (the RAW's main preview), or null if none. */\nexport function extractLargestJpeg(buf: ArrayBuffer): Uint8Array | null {\n const b = new Uint8Array(buf)\n const n = b.length\n let best: { start: number; end: number } | null = null\n let i = 0\n while (i < n - 1) {\n if (b[i] === 0xff && b[i + 1] === 0xd8) {\n const end = jpegEnd(b, i)\n if (end > i) {\n if (!best || end - i > best.end - best.start) best = { start: i, end }\n i = end // jump past this JPEG so nested streams aren't double-counted\n continue\n }\n }\n i++\n }\n return best ? b.subarray(best.start, best.end) : null\n}\n"],"mappings":";AAYA,SAAS,EAAQ,GAAe,GAAuB;CACrD,IAAI,IAAI,IAAQ,GACV,IAAI,EAAE;CACZ,OAAO,IAAI,IAAI,IAAG;EAChB,IAAI,EAAE,OAAO,KAAM;GACjB;GACA;EACF;EACA,IAAM,IAAS,EAAE,IAAI;EACrB,IAAI,MAAW,KAAM;GACnB;GACA;EACF;EACA,IAAI,MAAW,KAAM,OAAO,IAAI;EAEhC,IAAI,MAAW,KAAS,KAAU,OAAQ,KAAU,KAAO;GACzD,KAAK;GACL;EACF;EACA,IAAI,IAAI,KAAK,GAAG,OAAO;EACvB,IAAM,IAAO,EAAE,IAAI,MAAM,IAAK,EAAE,IAAI;EACpC,IAAI,IAAM,GAAG,OAAO;EACpB,IAAI,MAAW,KAAM;GAGnB,KADA,KAAK,IAAI,GACF,IAAI,IAAI,IAAG;IAChB,IAAI,EAAE,OAAO,KAAM;KACjB,IAAM,IAAO,EAAE,IAAI;KACnB,IAAI,MAAS,KAAQ,EAAE,KAAQ,OAAQ,KAAQ,MAAO;IACxD;IACA;GACF;GACA;EACF;EACA,KAAK,IAAI;CACX;CACA,OAAO;AACT;AAGA,SAAgB,EAAmB,GAAqC;CACtE,IAAM,IAAI,IAAI,WAAW,CAAG,GACtB,IAAI,EAAE,QACR,IAA8C,MAC9C,IAAI;CACR,OAAO,IAAI,IAAI,IAAG;EAChB,IAAI,EAAE,OAAO,OAAQ,EAAE,IAAI,OAAO,KAAM;GACtC,IAAM,IAAM,EAAQ,GAAG,CAAC;GACxB,IAAI,IAAM,GAAG;IAEX,CADI,CAAC,KAAQ,IAAM,IAAI,EAAK,MAAM,EAAK,WAAO,IAAO;KAAE,OAAO;KAAG;IAAI,IACrE,IAAI;IACJ;GACF;EACF;EACA;CACF;CACA,OAAO,IAAO,EAAE,SAAS,EAAK,OAAO,EAAK,GAAG,IAAI;AACnD"}
|
package/dist/lib/rc2.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rc2.js","names":[],"sources":["../../src/lib/rc2.ts"],"sourcesContent":["/**\n * Compact RC2 (RFC 2268) CBC decryption — no dependency, no WASM.\n *\n * Needed for legacy PKCS#12 keystores (see lib/pkcs12.ts): certificate bags are commonly encrypted with\n * `pbeWithSHA1And40BitRC2-CBC` (or the 128-bit variant), and WebCrypto ships no RC2. Decrypt-only. RC2 is\n * obsolete — used here ONLY to read existing keystores. Blocks are 8 bytes; words are 16-bit little-endian.\n */\n\n// PITABLE — the fixed 256-byte permutation from RFC 2268 §2.\n// prettier-ignore\nconst PITABLE = new Uint8Array([\n 0xd9, 0x78, 0xf9, 0xc4, 0x19, 0xdd, 0xb5, 0xed, 0x28, 0xe9, 0xfd, 0x79, 0x4a, 0xa0, 0xd8, 0x9d,\n 0xc6, 0x7e, 0x37, 0x83, 0x2b, 0x76, 0x53, 0x8e, 0x62, 0x4c, 0x64, 0x88, 0x44, 0x8b, 0xfb, 0xa2,\n 0x17, 0x9a, 0x59, 0xf5, 0x87, 0xb3, 0x4f, 0x13, 0x61, 0x45, 0x6d, 0x8d, 0x09, 0x81, 0x7d, 0x32,\n 0xbd, 0x8f, 0x40, 0xeb, 0x86, 0xb7, 0x7b, 0x0b, 0xf0, 0x95, 0x21, 0x22, 0x5c, 0x6b, 0x4e, 0x82,\n 0x54, 0xd6, 0x65, 0x93, 0xce, 0x60, 0xb2, 0x1c, 0x73, 0x56, 0xc0, 0x14, 0xa7, 0x8c, 0xf1, 0xdc,\n 0x12, 0x75, 0xca, 0x1f, 0x3b, 0xbe, 0xe4, 0xd1, 0x42, 0x3d, 0xd4, 0x30, 0xa3, 0x3c, 0xb6, 0x26,\n 0x6f, 0xbf, 0x0e, 0xda, 0x46, 0x69, 0x07, 0x57, 0x27, 0xf2, 0x1d, 0x9b, 0xbc, 0x94, 0x43, 0x03,\n 0xf8, 0x11, 0xc7, 0xf6, 0x90, 0xef, 0x3e, 0xe7, 0x06, 0xc3, 0xd5, 0x2f, 0xc8, 0x66, 0x1e, 0xd7,\n 0x08, 0xe8, 0xea, 0xde, 0x80, 0x52, 0xee, 0xf7, 0x84, 0xaa, 0x72, 0xac, 0x35, 0x4d, 0x6a, 0x2a,\n 0x96, 0x1a, 0xd2, 0x71, 0x5a, 0x15, 0x49, 0x74, 0x4b, 0x9f, 0xd0, 0x5e, 0x04, 0x18, 0xa4, 0xec,\n 0xc2, 0xe0, 0x41, 0x6e, 0x0f, 0x51, 0xcb, 0xcc, 0x24, 0x91, 0xaf, 0x50, 0xa1, 0xf4, 0x70, 0x39,\n 0x99, 0x7c, 0x3a, 0x85, 0x23, 0xb8, 0xb4, 0x7a, 0xfc, 0x02, 0x36, 0x5b, 0x25, 0x55, 0x97, 0x31,\n 0x2d, 0x5d, 0xfa, 0x98, 0xe3, 0x8a, 0x92, 0xae, 0x05, 0xdf, 0x29, 0x10, 0x67, 0x6c, 0xba, 0xc9,\n 0xd3, 0x00, 0xe6, 0xcf, 0xe1, 0x9e, 0xa8, 0x2c, 0x63, 0x16, 0x01, 0x3f, 0x58, 0xe2, 0x89, 0xa9,\n 0x0d, 0x38, 0x34, 0x1b, 0xab, 0x33, 0xff, 0xb0, 0xbb, 0x48, 0x0c, 0x5f, 0xb9, 0xb1, 0xcd, 0x2e,\n 0xc5, 0xf3, 0xdb, 0x47, 0xe5, 0xa5, 0x9c, 0x77, 0x0a, 0xa6, 0x20, 0x68, 0xfe, 0x7f, 0xc1, 0xad,\n])\n\n/** Expand a key into the 64-word (16-bit) RC2 schedule, limited to `effectiveBits` of effective strength. */\nfunction rc2KeySchedule(key: Uint8Array, effectiveBits: number): Uint16Array {\n const L = new Uint8Array(128)\n const T = key.length\n L.set(key.subarray(0, T))\n const T1 = effectiveBits\n const T8 = (T1 + 7) >> 3\n const TM = 0xff >> ((8 * T8 - T1) & 7) // = 255 mod 2^(8 + T1 - 8*T8)\n for (let i = T; i < 128; i++) L[i] = PITABLE[(L[i - 1] + L[i - T]) & 0xff]\n L[128 - T8] = PITABLE[L[128 - T8] & TM]\n for (let i = 127 - T8; i >= 0; i--) L[i] = PITABLE[L[i + 1] ^ L[i + T8]]\n const K = new Uint16Array(64)\n for (let i = 0; i < 64; i++) K[i] = L[2 * i] | (L[2 * i + 1] << 8)\n return K\n}\n\nconst rotr16 = (x: number, n: number): number => ((x >>> n) | (x << (16 - n))) & 0xffff\n\n/** Decrypt one 8-byte block (little-endian words) in place, returning [R0,R1,R2,R3]. */\nfunction rc2DecryptBlock(r: number[], K: Uint16Array): void {\n let [r0, r1, r2, r3] = r\n let j = 63\n const rMix = (): void => {\n r3 = rotr16(r3, 5)\n r3 = (r3 - K[j--] - (r2 & r1) - (~r2 & r0)) & 0xffff\n r2 = rotr16(r2, 3)\n r2 = (r2 - K[j--] - (r1 & r0) - (~r1 & r3)) & 0xffff\n r1 = rotr16(r1, 2)\n r1 = (r1 - K[j--] - (r0 & r3) - (~r0 & r2)) & 0xffff\n r0 = rotr16(r0, 1)\n r0 = (r0 - K[j--] - (r3 & r2) - (~r3 & r1)) & 0xffff\n }\n const rMash = (): void => {\n r3 = (r3 - K[r2 & 63]) & 0xffff\n r2 = (r2 - K[r1 & 63]) & 0xffff\n r1 = (r1 - K[r0 & 63]) & 0xffff\n r0 = (r0 - K[r3 & 63]) & 0xffff\n }\n for (let i = 0; i < 5; i++) rMix()\n rMash()\n for (let i = 0; i < 6; i++) rMix()\n rMash()\n for (let i = 0; i < 5; i++) rMix()\n r[0] = r0\n r[1] = r1\n r[2] = r2\n r[3] = r3\n}\n\n/**\n * RC2-CBC decrypt, no padding removal (the caller strips PKCS#7). `iv` is 8 bytes; `data` must be a whole\n * number of 8-byte blocks. `effectiveBits` defaults to the key length in bits (PKCS#12 40-bit RC2 passes 40).\n */\nexport function rc2CbcDecrypt(\n key: Uint8Array,\n iv: Uint8Array,\n data: Uint8Array,\n effectiveBits = key.length * 8,\n): Uint8Array {\n const K = rc2KeySchedule(key, effectiveBits)\n const n = data.length - (data.length % 8)\n const out = new Uint8Array(n)\n const prev = new Uint8Array(8)\n prev.set(iv.subarray(0, 8))\n const r = [0, 0, 0, 0]\n for (let off = 0; off < n; off += 8) {\n r[0] = data[off] | (data[off + 1] << 8)\n r[1] = data[off + 2] | (data[off + 3] << 8)\n r[2] = data[off + 4] | (data[off + 5] << 8)\n r[3] = data[off + 6] | (data[off + 7] << 8)\n rc2DecryptBlock(r, K)\n out[off] = (r[0] & 0xff) ^ prev[0]\n out[off + 1] = ((r[0] >> 8) & 0xff) ^ prev[1]\n out[off + 2] = (r[1] & 0xff) ^ prev[2]\n out[off + 3] = ((r[1] >> 8) & 0xff) ^ prev[3]\n out[off + 4] = (r[2] & 0xff) ^ prev[4]\n out[off + 5] = ((r[2] >> 8) & 0xff) ^ prev[5]\n out[off + 6] = (r[3] & 0xff) ^ prev[6]\n out[off + 7] = ((r[3] >> 8) & 0xff) ^ prev[7]\n prev.set(data.subarray(off, off + 8))\n }\n return out\n}\n"],"mappings":";AAUA,IAAM,IAAU,IAAI,WAAW;CAC7B;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAC1F;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAC1F;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAC1F;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAC1F;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAC1F;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAC1F;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAC1F;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAC1F;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAC1F;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAC1F;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAC1F;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAC1F;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAC1F;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAC1F;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM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"}
|
package/dist/lib/resolveInput.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolveInput.js","names":[],"sources":["../../src/lib/resolveInput.ts"],"sourcesContent":["import type { ViewerInput } from '../types'\n\n/** A normalized source: always a fetchable URL, plus optional pre-decoded text; `dispose()` frees blob: URLs. */\nexport interface ResolvedSource {\n name: string\n mime: string | null\n size?: number\n /** Always fetchable: the real `src`, or a `blob:` URL for blob/text/load/pre-fetched sources. */\n url: string\n /** Present when the input carried decoded text (text-backed viewers use it directly — no fetch). */\n text?: string\n /** Release any `blob:` URL this created. No-op for a pass-through `src`. */\n dispose(): void\n}\n\nconst noop = (): void => {}\n\nfunction toBlob(bytes: Blob | ArrayBuffer, mime: string | null): Blob {\n if (bytes instanceof Blob) return bytes\n return new Blob([bytes], mime ? { type: mime } : undefined)\n}\n\n/**\n * Normalize a {@link ViewerInput} into a fetchable source (see docs/file-viewer-package-design.md §2).\n *\n * Async because AUTHENTICATED sources (`fetchConfig`/`load`) are pulled ONCE here and turned into a `blob:`\n * URL — so the self-fetching sub-viewers keep calling plain `fetch(url)` with no auth knowledge, and the\n * credentials live in exactly one place. A plain public `src` is passed through unchanged (streaming stays\n * intact for the big-file viewers). `blob`/`text` become object URLs. Precedence: text > blob > load > src.\n */\nexport async function resolveInput(input: ViewerInput): Promise<ResolvedSource> {\n const name = input.name\n const mime = input.mime && input.mime.trim() ? input.mime : null\n const base = { name, mime, size: input.size }\n\n // text: hand it straight to text-backed viewers; also expose a blob: URL so self-fetching viewers work.\n if (input.text != null) {\n const url = URL.createObjectURL(new Blob([input.text], { type: mime ?? 'text/plain' }))\n return { ...base, url, text: input.text, dispose: () => URL.revokeObjectURL(url) }\n }\n\n // blob: object URL straight from the in-memory bytes.\n if (input.blob) {\n const url = URL.createObjectURL(input.blob)\n return { ...base, url, dispose: () => URL.revokeObjectURL(url) }\n }\n\n // load: custom loader → bytes → object URL (auth handled inside the consumer's function).\n if (input.load) {\n const url = URL.createObjectURL(toBlob(await input.load(), mime))\n return { ...base, url, dispose: () => URL.revokeObjectURL(url) }\n }\n\n // src + fetchConfig: authenticated fetch ONCE here → object URL (sub-viewers fetch the blob, no auth needed).\n if (input.src && input.fetchConfig) {\n const res = await fetch(input.src, input.fetchConfig)\n if (!res.ok) throw new Error(`HTTP ${res.status} fetching \"${name}\"`)\n const url = URL.createObjectURL(await res.blob())\n return { ...base, url, dispose: () => URL.revokeObjectURL(url) }\n }\n\n // Plain public src: pass through (streaming preserved for the big-file viewers).\n if (input.src) return { ...base, url: input.src, dispose: noop }\n\n throw new Error(`ViewerInput for \"${name}\" has no data source (src / blob / text / load)`)\n}\n"],"mappings":";AAeA,IAAM,UAAmB,CAAC;AAE1B,SAAS,EAAO,GAA2B,GAA2B;CAEpE,OADI,aAAiB,OAAa,IAC3B,IAAI,KAAK,CAAC,CAAK,GAAG,IAAO,EAAE,MAAM,EAAK,IAAI,KAAA,CAAS;AAC5D;AAUA,eAAsB,EAAa,GAA6C;CAC9E,IAAM,IAAO,EAAM,MACb,IAAO,EAAM,QAAQ,EAAM,KAAK,KAAK,IAAI,EAAM,OAAO,MACtD,IAAO;EAAE;EAAM;EAAM,MAAM,EAAM;CAAK;CAG5C,IAAI,EAAM,QAAQ,MAAM;EACtB,IAAM,IAAM,IAAI,gBAAgB,IAAI,KAAK,CAAC,EAAM,IAAI,GAAG,EAAE,MAAM,KAAQ,aAAa,CAAC,CAAC;EACtF,OAAO;GAAE,GAAG;GAAM;GAAK,MAAM,EAAM;GAAM,eAAe,IAAI,gBAAgB,CAAG;EAAE;CACnF;CAGA,IAAI,EAAM,MAAM;EACd,IAAM,IAAM,IAAI,gBAAgB,EAAM,IAAI;EAC1C,OAAO;GAAE,GAAG;GAAM;GAAK,eAAe,IAAI,gBAAgB,CAAG;EAAE;CACjE;CAGA,IAAI,EAAM,MAAM;EACd,IAAM,IAAM,IAAI,gBAAgB,EAAO,MAAM,EAAM,KAAK,GAAG,CAAI,CAAC;EAChE,OAAO;GAAE,GAAG;GAAM;GAAK,eAAe,IAAI,gBAAgB,CAAG;EAAE;CACjE;CAGA,IAAI,EAAM,OAAO,EAAM,aAAa;EAClC,IAAM,IAAM,MAAM,MAAM,EAAM,KAAK,EAAM,WAAW;EACpD,IAAI,CAAC,EAAI,IAAI,MAAU,MAAM,QAAQ,EAAI,OAAO,aAAa,EAAK,EAAE;EACpE,IAAM,IAAM,IAAI,gBAAgB,MAAM,EAAI,KAAK,CAAC;EAChD,OAAO;GAAE,GAAG;GAAM;GAAK,eAAe,IAAI,gBAAgB,CAAG;EAAE;CACjE;CAGA,IAAI,EAAM,KAAK,OAAO;EAAE,GAAG;EAAM,KAAK,EAAM;EAAK,SAAS;CAAK;CAE/D,MAAU,MAAM,oBAAoB,EAAK,gDAAgD;AAC3F"}
|
package/dist/lib/rtf.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rtf.js","names":[],"sources":["../../src/lib/rtf.ts"],"sourcesContent":["/**\n * Zero-dependency RTF (Rich Text Format) → HTML renderer for the side viewer's {@link RtfViewer}.\n *\n * RTF is a 7-bit-ASCII control-word format — `{\\rtf1 … }` groups, `\\word`/`\\word42` control words, `\\'hh` raw\n * bytes, `\\uN` unicode — so (like the eml / mht / har / asn1 parsers) we hand-roll it rather than pull a heavy\n * canvas-based library. We interpret the document with a group state stack and emit a small, SAFE HTML tree:\n *\n * - paragraphs (`\\par`) → `<p>` with text-align; soft breaks (`\\line`) → `\\n` (the container is white-space:\n * pre-wrap, so `\\tab`/`\\n`/runs of spaces survive without needing `<br>`);\n * - character runs → `<span style>` carrying ONLY whitelisted, self-computed values: bold/italic via\n * font-weight/style, underline/strike via text-decoration, `\\cfN` → `#rrggbb` from the parsed color table,\n * `\\fsN` → pt, `\\fN` → a sanitized font-family stack from the font table, `\\super`/`\\sub` → vertical-align.\n *\n * Security: every byte of text is HTML-escaped and the only attributes we emit are numbers (sizes), `#rrggbb`\n * colors, and a quote-/bracket-stripped font name — there is NO path for upstream markup, URLs, or scripts to\n * reach the output, so the component can inline it with `v-html` (same posture as DrawioViewer/WidgetBlock's\n * self-generated SVG). Unknown control words, binary blobs (`\\bin`), pictures and other destinations we don't\n * render are skipped, never dumped as raw text.\n *\n * Charset handling: `\\'hh` bytes are decoded with a TextDecoder chosen from the current font's `\\fcharset` (or\n * the document's `\\ansicpg`), so CJK / Cyrillic / etc. documents read correctly; `\\uN` carries unicode directly\n * (with `\\ucN` fallback bytes skipped). Multi-byte `\\'hh` pairs are buffered and decoded together — we only\n * flush the byte buffer at formatting/group boundaries, never between two adjacent `\\'` escapes.\n */\n\n/** One styled text run within a paragraph. Booleans/values map 1:1 to a `<span style>` (see {@link runHtml}). */\nexport interface RtfRun {\n text: string\n bold: boolean\n italic: boolean\n underline: boolean\n strike: boolean\n superscript: boolean\n subscript: boolean\n /** '#rrggbb' from the color table (`\\cfN`), or null for the automatic/default color. */\n color: string | null\n /** Point size (`\\fsN` is half-points), or null to inherit the container's size. */\n size: number | null\n /** A sanitized CSS font-family stack (e.g. `'Times New Roman', serif`), or null to inherit. */\n font: string | null\n}\n\n/** A paragraph: its alignment plus the styled runs it holds (empty runs ⇒ a blank line). */\nexport interface RtfPara {\n align: 'left' | 'center' | 'right' | 'justify'\n runs: RtfRun[]\n}\n\nexport interface RtfDoc {\n paras: RtfPara[]\n}\n\n/** Per-group interpreter state — cloned on `{`, restored on `}`. Character + paragraph formatting both live here\n * (RTF saves/restores both with the group), alongside the active destination and the `\\uc` fallback width. */\ninterface Frame {\n bold: boolean\n italic: boolean\n underline: boolean\n strike: boolean\n sup: boolean\n sub: boolean\n /** Font size in HALF-points (`\\fsN`), or null when unspecified. */\n size: number | null\n /** Index into the color table (`\\cfN`); 0 / negative ⇒ automatic. */\n colorIdx: number\n /** Index into the font table (`\\fN`); negative ⇒ use the document default font. */\n fontIdx: number\n align: 'left' | 'center' | 'right' | 'justify'\n /**\n * null = normal body text. Otherwise a destination we treat specially: 'fonttbl'/'colortbl' route bytes to\n * those table parsers; 'skip' swallows everything (ignorable groups, pictures, stylesheet, info, …).\n */\n dest: null | 'fonttbl' | 'colortbl' | 'skip'\n /** `\\ucN`: how many fallback bytes/chars follow a `\\uN` and must be discarded. */\n uc: number\n}\n\ninterface FontEntry {\n charset: number\n /** Generic CSS family from `\\froman`/`\\fswiss`/… ('serif'|'sans-serif'|'monospace'|'cursive'|'fantasy'|''). */\n family: string\n name: string\n}\n\nconst BACKSLASH = 0x5c\nconst LBRACE = 0x7b\nconst RBRACE = 0x7d\nconst SEMI = 0x3b\nconst APOS = 0x27 // '\n\n// Destinations whose content must NOT appear in the body. fonttbl/colortbl are handled separately (parsed, not\n// skipped); these are pure-skip. `\\*` (ignorable-destination marker) adds skip for anything we don't understand.\nconst SKIP_DESTS = new Set([\n 'stylesheet',\n 'info',\n 'pict',\n 'object',\n 'header',\n 'headerl',\n 'headerr',\n 'headerf',\n 'footer',\n 'footerl',\n 'footerr',\n 'footerf',\n 'footnote',\n 'annotation',\n 'atnid',\n 'atnauthor',\n 'generator',\n 'datastore',\n 'themedata',\n 'colorschememapping',\n 'latentstyles',\n 'listtable',\n 'listoverridetable',\n 'revtbl',\n 'rsidtbl',\n 'xmlnstbl',\n 'fldinst',\n 'pgptbl',\n 'sn',\n 'sv',\n 'company',\n 'operator',\n 'nesttableprops',\n 'bkmkstart',\n 'bkmkend',\n])\n\nfunction isAlpha(b: number): boolean {\n return (b >= 0x41 && b <= 0x5a) || (b >= 0x61 && b <= 0x7a)\n}\nfunction isDigit(b: number): boolean {\n return b >= 0x30 && b <= 0x39\n}\nfunction hexVal(b: number): number {\n if (b >= 0x30 && b <= 0x39) return b - 0x30\n if (b >= 0x61 && b <= 0x66) return b - 0x61 + 10\n if (b >= 0x41 && b <= 0x46) return b - 0x41 + 10\n return 0\n}\n\nconst decoderCache = new Map<string, TextDecoder>()\nfunction decode(bytes: number[], label: string): string {\n let dec = decoderCache.get(label)\n if (!dec) {\n try {\n dec = new TextDecoder(label)\n } catch {\n dec = new TextDecoder('windows-1252')\n }\n decoderCache.set(label, dec)\n }\n return dec.decode(new Uint8Array(bytes))\n}\n\n/** Windows code page (`\\ansicpgN`) → a WHATWG encoding label a TextDecoder understands. */\nfunction labelForCpg(cpg: number): string {\n switch (cpg) {\n case 65001:\n return 'utf-8'\n case 936:\n return 'gb18030' // superset of GBK/GB2312\n case 950:\n return 'big5'\n case 932:\n return 'shift_jis'\n case 949:\n return 'euc-kr'\n case 874:\n return 'windows-874'\n case 1250:\n return 'windows-1250'\n case 1251:\n return 'windows-1251'\n case 1253:\n return 'windows-1253'\n case 1254:\n return 'windows-1254'\n case 1255:\n return 'windows-1255'\n case 1256:\n return 'windows-1256'\n case 1257:\n return 'windows-1257'\n case 1258:\n return 'windows-1258'\n case 10000:\n return 'macintosh'\n default:\n return 'windows-1252'\n }\n}\n\nexport function parseRtf(bytes: Uint8Array): RtfDoc {\n const n = bytes.length\n let i = 0\n\n const base: Frame = {\n bold: false,\n italic: false,\n underline: false,\n strike: false,\n sup: false,\n sub: false,\n size: null,\n colorIdx: 0,\n fontIdx: -1,\n align: 'left',\n dest: null,\n uc: 1,\n }\n const stack: Frame[] = [base]\n const top = (): Frame => stack[stack.length - 1]\n\n let docCpg = 1252\n let defFont = 0\n const fonts: Record<number, FontEntry> = {}\n const colors: (string | null)[] = []\n\n // Font-charset (`\\fcharsetN`) → encoding label; charset 0/1 (ansi/default) defer to the document code page.\n function labelForCharset(cs: number): string {\n switch (cs) {\n case 77:\n return 'macintosh'\n case 128:\n return 'shift_jis'\n case 129:\n case 130:\n return 'euc-kr'\n case 134:\n return 'gb18030'\n case 136:\n return 'big5'\n case 161:\n return 'windows-1253'\n case 162:\n return 'windows-1254'\n case 163:\n return 'windows-1258'\n case 177:\n return 'windows-1255'\n case 178:\n return 'windows-1256'\n case 186:\n return 'windows-1257'\n case 204:\n return 'windows-1251'\n case 222:\n return 'windows-874'\n case 238:\n return 'windows-1250'\n default:\n return labelForCpg(docCpg)\n }\n }\n\n // ---- output accumulation ----\n const paras: RtfPara[] = []\n let curRuns: RtfRun[] = []\n const pendingBytes: number[] = []\n let unicodeSkip = 0\n\n // ---- font-table parse temp ----\n let ftIndex = -1\n let ftCharset = 0\n let ftFamily = ''\n let ftName: number[] = []\n // ---- color-table parse temp ----\n let ctR = 0\n let ctG = 0\n let ctB = 0\n let ctHas = false\n\n function colorFor(idx: number): string | null {\n if (idx <= 0) return null\n return colors[idx] ?? null\n }\n function fontStackFor(idx: number): string | null {\n const f = fonts[idx] ?? fonts[defFont]\n if (!f) return null\n const name = f.name\n .replace(/['\"<>;{}\\\\\\r\\n]/g, '')\n .trim()\n .slice(0, 64)\n if (name && f.family) return `'${name}', ${f.family}`\n if (name) return `'${name}'`\n return f.family || null\n }\n\n function currentFmt(): RtfRun {\n const f = top()\n return {\n text: '',\n bold: f.bold,\n italic: f.italic,\n underline: f.underline,\n strike: f.strike,\n superscript: f.sup,\n subscript: f.sub,\n color: colorFor(f.colorIdx),\n size: f.size != null ? f.size / 2 : null,\n font: fontStackFor(f.fontIdx >= 0 ? f.fontIdx : defFont),\n }\n }\n function sameFmt(a: RtfRun, b: RtfRun): boolean {\n return (\n a.bold === b.bold &&\n a.italic === b.italic &&\n a.underline === b.underline &&\n a.strike === b.strike &&\n a.superscript === b.superscript &&\n a.subscript === b.subscript &&\n a.color === b.color &&\n a.size === b.size &&\n a.font === b.font\n )\n }\n /** Append intended (already-decoded) body text, extending the last run when its formatting matches. */\n function pushText(s: string): void {\n if (!s) return\n const fmt = currentFmt()\n const last = curRuns[curRuns.length - 1]\n if (last && sameFmt(last, fmt)) last.text += s\n else {\n fmt.text = s\n curRuns.push(fmt)\n }\n }\n /** Decode the buffered body bytes under the CURRENT font/codepage and emit them as text. */\n function flushBytes(): void {\n if (!pendingBytes.length) return\n const f = top()\n const idx = f.fontIdx >= 0 ? f.fontIdx : defFont\n const cs = fonts[idx]?.charset\n const label = cs != null && cs > 1 ? labelForCharset(cs) : labelForCpg(docCpg)\n const s = decode(pendingBytes, label)\n pendingBytes.length = 0\n pushText(s)\n }\n /** Emit a single intended character (special chars: tab/line/quotes/dashes). Respects the `\\uc` skip window. */\n function emitChar(s: string): void {\n if (top().dest !== null) return\n if (unicodeSkip > 0) {\n unicodeSkip--\n return\n }\n pushText(s)\n }\n function endParagraph(): void {\n flushBytes()\n paras.push({ align: top().align, runs: curRuns })\n curRuns = []\n }\n\n function finalizeColor(): void {\n colors.push(ctHas ? rgbHex(ctR, ctG, ctB) : null)\n ctR = ctG = ctB = 0\n ctHas = false\n }\n function finalizeFont(): void {\n if (ftIndex >= 0) {\n const name = ftName.length\n ? decode(ftName, ftCharset > 1 ? labelForCharset(ftCharset) : labelForCpg(docCpg)).trim()\n : ''\n fonts[ftIndex] = { charset: ftCharset, family: ftFamily, name }\n }\n ftFamily = ''\n ftCharset = 0\n ftName = []\n }\n\n /** A raw literal byte of document content — routed by the active destination. */\n function literalByte(b: number): void {\n const f = top()\n if (unicodeSkip > 0) {\n unicodeSkip--\n return\n }\n if (f.dest === null) {\n pendingBytes.push(b)\n } else if (f.dest === 'colortbl') {\n if (b === SEMI) finalizeColor()\n } else if (f.dest === 'fonttbl') {\n if (b === SEMI) finalizeFont()\n else if (b !== 0x20 || ftName.length) ftName.push(b) // drop leading spaces before the name\n }\n // 'skip' → discard\n }\n\n function handleWord(word: string, param: number | null): void {\n const f = top()\n\n // Font-table entries (inside \\fonttbl): build the table rather than emit text.\n if (f.dest === 'fonttbl') {\n switch (word) {\n case 'f':\n finalizeFont() // a new \\fN starts a fresh entry (flat-form tables have no per-entry braces)\n ftIndex = param ?? 0\n return\n case 'fcharset':\n ftCharset = param ?? 0\n return\n case 'froman':\n ftFamily = 'serif'\n return\n case 'fswiss':\n ftFamily = 'sans-serif'\n return\n case 'fmodern':\n ftFamily = 'monospace'\n return\n case 'fscript':\n ftFamily = 'cursive'\n return\n case 'fdecor':\n ftFamily = 'fantasy'\n return\n case 'fnil':\n case 'ftech':\n case 'fbidi':\n ftFamily = ''\n return\n default:\n return // \\fprq, \\cpg, names handled as bytes, etc.\n }\n }\n // Color-table entries (inside \\colortbl): accumulate r/g/b; ';' (a literal byte) finalizes.\n if (f.dest === 'colortbl') {\n if (word === 'red') {\n ctR = param ?? 0\n ctHas = true\n } else if (word === 'green') {\n ctG = param ?? 0\n ctHas = true\n } else if (word === 'blue') {\n ctB = param ?? 0\n ctHas = true\n }\n return\n }\n\n switch (word) {\n // structural / header\n case 'rtf':\n case 'ansi':\n case 'mac':\n case 'pc':\n case 'pca':\n return\n case 'ansicpg':\n docCpg = param ?? 1252\n return\n case 'deff':\n defFont = param ?? 0\n return\n case 'uc':\n f.uc = param ?? 1\n return\n case 'fonttbl':\n f.dest = 'fonttbl'\n ftIndex = -1\n ftFamily = ''\n ftCharset = 0\n ftName = []\n return\n case 'colortbl':\n f.dest = 'colortbl'\n ctR = ctG = ctB = 0\n ctHas = false\n return\n case 'bin': {\n // Raw binary run: skip exactly param bytes so braces inside them can't corrupt group nesting.\n const len = Math.max(0, param ?? 0)\n i += len\n if (i > n) i = n\n return\n }\n\n // character formatting\n case 'b':\n f.bold = param !== 0\n return\n case 'i':\n f.italic = param !== 0\n return\n case 'ul':\n f.underline = param !== 0\n return\n case 'ulnone':\n f.underline = false\n return\n case 'strike':\n case 'striked':\n f.strike = param !== 0\n return\n case 'super':\n f.sup = param !== 0\n if (f.sup) f.sub = false\n return\n case 'sub':\n f.sub = param !== 0\n if (f.sub) f.sup = false\n return\n case 'nosupersub':\n f.sup = false\n f.sub = false\n return\n case 'fs':\n f.size = param ?? f.size\n return\n case 'cf':\n f.colorIdx = param ?? 0\n return\n case 'f':\n f.fontIdx = param ?? 0\n return\n case 'plain':\n f.bold = f.italic = f.underline = f.strike = f.sup = f.sub = false\n f.size = null\n f.colorIdx = 0\n f.fontIdx = defFont\n return\n\n // paragraph formatting\n case 'pard':\n f.align = 'left'\n return\n case 'ql':\n f.align = 'left'\n return\n case 'qc':\n f.align = 'center'\n return\n case 'qr':\n f.align = 'right'\n return\n case 'qj':\n case 'qd':\n f.align = 'justify'\n return\n\n // breaks\n case 'par':\n case 'sect':\n case 'row':\n case 'page':\n case 'column':\n endParagraph()\n return\n case 'line':\n case 'softline':\n emitChar('\\n')\n return\n case 'tab':\n case 'cell':\n emitChar('\\t')\n return\n\n // named special characters\n case 'lquote':\n emitChar('‘')\n return\n case 'rquote':\n emitChar('’')\n return\n case 'ldblquote':\n emitChar('“')\n return\n case 'rdblquote':\n emitChar('”')\n return\n case 'bullet':\n emitChar('•')\n return\n case 'endash':\n emitChar('–')\n return\n case 'emdash':\n emitChar('—')\n return\n case 'emspace':\n emitChar(' ')\n return\n case 'enspace':\n emitChar(' ')\n return\n case 'qmspace':\n emitChar(' ')\n return\n case 'zwnj':\n emitChar('')\n return\n case 'zwj':\n emitChar('')\n return\n\n // unicode code point + fallback-skip window\n case 'u': {\n let cp = param ?? 0\n if (cp < 0) cp += 0x10000\n if (f.dest === null && cp > 0) {\n try {\n pushText(String.fromCodePoint(cp))\n } catch {\n /* out-of-range code point — drop it */\n }\n }\n unicodeSkip = f.uc\n return\n }\n\n default:\n if (SKIP_DESTS.has(word)) f.dest = 'skip'\n // otherwise an unknown control word — ignore it (its group text still renders unless skipped)\n return\n }\n }\n\n function handleSymbol(c: number): void {\n switch (c) {\n case BACKSLASH:\n emitChar('\\\\')\n return\n case LBRACE:\n emitChar('{')\n return\n case RBRACE:\n emitChar('}')\n return\n case 0x7e: // \\~ non-breaking space\n emitChar(' ')\n return\n case 0x5f: // \\_ non-breaking hyphen\n emitChar('‑')\n return\n case 0x2d: // \\- optional hyphen → render nothing\n return\n case 0x2a: // \\* ignorable-destination marker\n top().dest = 'skip'\n return\n case 0x0a:\n case 0x0d: // escaped newline ⇒ paragraph break\n endParagraph()\n return\n default:\n return // \\:, \\|, etc. — ignore\n }\n }\n\n // ---- main scan ----\n while (i < n) {\n const b = bytes[i]\n if (b === LBRACE) {\n flushBytes()\n stack.push({ ...top() })\n i++\n } else if (b === RBRACE) {\n flushBytes()\n if (stack.length > 1) stack.pop()\n i++\n } else if (b === BACKSLASH) {\n i++\n if (i >= n) break\n const c = bytes[i]\n if (isAlpha(c)) {\n flushBytes() // a control word may change formatting/destination — decode pending bytes first\n let word = ''\n while (i < n && isAlpha(bytes[i])) {\n word += String.fromCharCode(bytes[i])\n i++\n }\n let param: number | null = null\n let neg = false\n if (i < n && bytes[i] === 0x2d) {\n neg = true\n i++\n }\n if (i < n && isDigit(bytes[i])) {\n let num = 0\n while (i < n && isDigit(bytes[i])) {\n num = num * 10 + (bytes[i] - 0x30)\n i++\n }\n param = neg ? -num : num\n }\n if (i < n && bytes[i] === 0x20) i++ // consume the single optional delimiter space\n handleWord(word, param)\n } else if (c === APOS) {\n // \\'hh — a raw byte; buffer it (NO flush) so adjacent escapes form multi-byte sequences.\n i++\n let byte = 0\n if (i < n) byte = hexVal(bytes[i++])\n if (i < n) byte = byte * 16 + hexVal(bytes[i++])\n literalByte(byte)\n } else {\n i++\n flushBytes()\n handleSymbol(c)\n }\n } else if (b === 0x0d || b === 0x0a) {\n i++ // raw CR/LF in the source is not body text\n } else {\n i++\n literalByte(b)\n }\n }\n\n flushBytes()\n if (curRuns.length) paras.push({ align: top().align, runs: curRuns })\n return { paras }\n}\n\nfunction rgbHex(r: number, g: number, b: number): string {\n const h = (v: number): string => Math.max(0, Math.min(255, v)).toString(16).padStart(2, '0')\n return `#${h(r)}${h(g)}${h(b)}`\n}\n\nfunction escapeHtml(s: string): string {\n return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')\n}\n\n/** Render one run to a `<span style>` (or bare escaped text when it carries no formatting). */\nfunction runHtml(r: RtfRun): string {\n const styles: string[] = []\n if (r.bold) styles.push('font-weight:bold')\n if (r.italic) styles.push('font-style:italic')\n if (r.underline && r.strike) styles.push('text-decoration:underline line-through')\n else if (r.underline) styles.push('text-decoration:underline')\n else if (r.strike) styles.push('text-decoration:line-through')\n if (r.color) styles.push(`color:${r.color}`)\n if (r.font) styles.push(`font-family:${r.font}`)\n let fontSize = r.size ? `${r.size}pt` : ''\n if (r.superscript || r.subscript) {\n styles.push(`vertical-align:${r.superscript ? 'super' : 'sub'}`)\n fontSize = r.size ? `${(r.size * 0.83).toFixed(1)}pt` : '0.83em'\n }\n if (fontSize) styles.push(`font-size:${fontSize}`)\n const text = escapeHtml(r.text)\n return styles.length ? `<span style=\"${styles.join(';')}\">${text}</span>` : text\n}\n\n/** Render a parsed document to a safe HTML string (self-generated, fully escaped — see the file header). */\nexport function rtfToHtml(doc: RtfDoc): string {\n let html = ''\n for (const p of doc.paras) {\n const inner = p.runs.map(runHtml).join('')\n const align = p.align !== 'left' ? ` style=\"text-align:${p.align}\"` : ''\n html += `<p${align}>${inner || '<br>'}</p>`\n }\n return html\n}\n\n/** Parse + render in one step: RTF bytes → safe HTML. */\nexport function renderRtf(bytes: Uint8Array): string {\n return rtfToHtml(parseRtf(bytes))\n}\n"],"mappings":"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"}
|
package/dist/lib/sevenZip.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sevenZip.js","names":[],"sources":["../../src/lib/sevenZip.ts"],"sourcesContent":["/**\n * Minimal in-browser 7z reader — no dependency, no WASM.\n *\n * Mirrors {@link lib/zip.ts}: hand-rolled structural parsing over a DataView/Uint8Array, decoding only\n * what conversation artifacts actually ship. The 7z container is considerably more involved than ZIP —\n * its directory (\"header\") is itself usually LZMA-compressed and describes *folders* (chains of coders)\n * rather than per-file streams — so this leans on the bundled LZMA decoder ({@link lib/lzma.ts}) both to\n * read the header and to extract files. Supported coders: Copy (00), LZMA (03 01 01), LZMA2 (21) and the\n * x86 BCJ filter (03 03 01 03); anything else (other branch filters, BCJ2, AES) is surfaced as an error.\n *\n * Used by SevenZipBrowser (the side viewer's 7z mode): the bytes come from the same download endpoint as\n * any other file, are decompressed entirely client-side, and never leave the browser.\n */\n\nimport { aesCbcDecrypt } from '@/lib/aes'\nimport { bcjX86Decode, lzma2Decode, lzmaDecode } from '@/lib/lzma'\nimport { Sha256 } from '@/lib/sha256'\n\nconst SIGNATURE = [0x37, 0x7a, 0xbc, 0xaf, 0x27, 0x1c] // \"7z\\xBC\\xAF\\x27\\x1C\"\nconst SIG_HEADER_SIZE = 32\n\n// Header property ids.\nconst K_END = 0x00\nconst K_HEADER = 0x01\nconst K_MAIN_STREAMS_INFO = 0x04\nconst K_FILES_INFO = 0x05\nconst K_PACK_INFO = 0x06\nconst K_UNPACK_INFO = 0x07\nconst K_SUBSTREAMS_INFO = 0x08\nconst K_SIZE = 0x09\nconst K_CRC = 0x0a\nconst K_FOLDER = 0x0b\nconst K_CODERS_UNPACK_SIZE = 0x0c\nconst K_NUM_UNPACK_STREAM = 0x0d\nconst K_EMPTY_STREAM = 0x0e\nconst K_EMPTY_FILE = 0x0f\nconst K_NAME = 0x11\nconst K_ENCODED_HEADER = 0x17\n\n// AES-256 + SHA-256 coder method id — 7z whole-header (\"encrypt file names\") or per-file content encryption.\nconst AES_CODER = '06f10701'\n\n/** Thrown when the archive (its header or an entry) is AES-encrypted and no password was supplied. */\nexport class SevenZipEncryptedError extends Error {\n constructor() {\n super('该 7z 已加密,需要密码')\n this.name = 'SevenZipEncryptedError'\n }\n}\n/** Thrown when the supplied password fails to decrypt the header/content. */\nexport class SevenZipPasswordError extends Error {\n constructor() {\n super('密码错误,无法解压')\n this.name = 'SevenZipPasswordError'\n }\n}\n\nexport interface SevenZipEntry {\n /** Full path within the archive, forward-slashed. */\n path: string\n /** Basename (last path segment). */\n name: string\n /** Uncompressed size in bytes. */\n size: number\n /** Directory entry. */\n isDir: boolean\n /** True when this file's folder is AES-encrypted (extraction / listing needs a password). */\n encrypted: boolean\n /** Index of the folder holding this file's bytes (-1 for empty files / directories). */\n folderIndex: number\n /** Byte offset of this file within its (solid) folder's decoded output. */\n offsetInFolder: number\n}\n\ninterface Coder {\n /** Method id as a hex string, e.g. \"00\", \"21\", \"030101\", \"03030103\". */\n id: string\n numIn: number\n numOut: number\n props: Uint8Array\n}\n\ninterface BindPair {\n inIndex: number\n outIndex: number\n}\n\ninterface Folder {\n coders: Coder[]\n bindPairs: BindPair[]\n /** Global in-stream indices fed by the archive's packed streams, in pack order. */\n packedIndices: number[]\n /** Unpacked size per global out-stream. */\n unpackSizes: number[]\n numPackStreams: number\n /** Byte ranges (absolute offsets into the archive) of this folder's packed streams. */\n packStreams: { offset: number; size: number }[]\n}\n\nexport interface SevenZipArchive {\n entries: SevenZipEntry[]\n /** Internal: parsed folder metadata, consumed by {@link extractSevenZipEntry}. */\n folders: Folder[]\n}\n\n/** A cursor over the (already-decoded) header byte stream. */\nclass Reader {\n pos = 0\n constructor(public readonly buf: Uint8Array) {}\n\n byte(): number {\n if (this.pos >= this.buf.length) throw new Error('7z 头部意外结束')\n return this.buf[this.pos++]\n }\n\n bytes(n: number): Uint8Array {\n const s = this.buf.subarray(this.pos, this.pos + n)\n this.pos += n\n return s\n }\n\n /** 7z variable-length number (REAL_UINT64): the first byte's high bits count the extra bytes. */\n number(): number {\n const first = this.byte()\n let mask = 0x80\n let value = 0\n for (let i = 0; i < 8; i++) {\n if ((first & mask) === 0) {\n value += (first & (mask - 1)) * 2 ** (8 * i)\n break\n }\n value += this.byte() * 2 ** (8 * i)\n mask >>= 1\n }\n return value\n }\n\n /** A bit vector of `n` bits, MSB-first within each byte. */\n bitVector(n: number): boolean[] {\n const v: boolean[] = []\n let b = 0\n let mask = 0\n for (let i = 0; i < n; i++) {\n if (mask === 0) {\n b = this.byte()\n mask = 0x80\n }\n v.push((b & mask) !== 0)\n mask >>= 1\n }\n return v\n }\n\n /** A \"defined\" vector: a leading all-defined flag, else an explicit bit vector. */\n boolVectorWithAll(n: number): boolean[] {\n if (this.byte() !== 0) return Array.from({ length: n }, () => true)\n return this.bitVector(n)\n }\n}\n\nfunction readUInt64LE(view: DataView, off: number): number {\n return view.getUint32(off + 4, true) * 0x100000000 + view.getUint32(off, true)\n}\n\n// ── Folder / streams parsing ──────────────────────────────────────────────────────────────────────\n\nfunction readFolder(r: Reader): Folder {\n const numCoders = r.number()\n const coders: Coder[] = []\n let totalIn = 0\n let totalOut = 0\n for (let i = 0; i < numCoders; i++) {\n const flags = r.byte()\n const idSize = flags & 0x0f\n const idBytes = r.bytes(idSize)\n let id = ''\n for (const b of idBytes) id += b.toString(16).padStart(2, '0')\n let numIn = 1\n let numOut = 1\n if (flags & 0x10) {\n numIn = r.number()\n numOut = r.number()\n }\n let props = new Uint8Array(0)\n if (flags & 0x20) props = r.bytes(r.number()).slice()\n coders.push({ id, numIn, numOut, props })\n totalIn += numIn\n totalOut += numOut\n }\n\n const numBindPairs = totalOut - 1\n const bindPairs: BindPair[] = []\n for (let i = 0; i < numBindPairs; i++) {\n bindPairs.push({ inIndex: r.number(), outIndex: r.number() })\n }\n\n const numPackStreams = totalIn - numBindPairs\n const packedIndices: number[] = []\n if (numPackStreams === 1) {\n // the single in-stream not used by any bind pair\n for (let i = 0; i < totalIn; i++) {\n if (!bindPairs.some((bp) => bp.inIndex === i)) {\n packedIndices.push(i)\n break\n }\n }\n } else {\n for (let i = 0; i < numPackStreams; i++) packedIndices.push(r.number())\n }\n\n return {\n coders,\n bindPairs,\n packedIndices,\n unpackSizes: [],\n numPackStreams,\n packStreams: [],\n }\n}\n\n/** The folder's final output is the one out-stream not consumed by any bind pair. */\nfunction folderFinalOutIndex(folder: Folder): number {\n let total = 0\n for (const c of folder.coders) total += c.numOut\n for (let i = 0; i < total; i++) {\n if (!folder.bindPairs.some((bp) => bp.outIndex === i)) return i\n }\n return total - 1\n}\n\nfunction folderUnpackSize(folder: Folder): number {\n return folder.unpackSizes[folderFinalOutIndex(folder)] ?? 0\n}\n\ninterface StreamsInfo {\n packPos: number\n packSizes: number[]\n folders: Folder[]\n /** Per-folder substream count (solid archives pack several files into one folder). */\n numUnpackStreamsInFolders: number[]\n /** Flat list of substream sizes, in folder order. */\n subSizes: number[]\n /** Whether each folder carried its own CRC in UnpackInfo (affects the SubStreamsInfo digest count). */\n folderHasCrc: boolean[]\n}\n\nfunction readStreamsInfo(r: Reader): StreamsInfo {\n const info: StreamsInfo = {\n packPos: 0,\n packSizes: [],\n folders: [],\n numUnpackStreamsInFolders: [],\n subSizes: [],\n folderHasCrc: [],\n }\n let id = r.byte()\n\n if (id === K_PACK_INFO) {\n info.packPos = r.number()\n const numPack = r.number()\n let sub = r.byte()\n while (sub !== K_END) {\n if (sub === K_SIZE) {\n for (let i = 0; i < numPack; i++) info.packSizes.push(r.number())\n } else if (sub === K_CRC) {\n readDigests(r, numPack)\n } else {\n throw new Error(`7z PackInfo 未知字段 0x${sub.toString(16)}`)\n }\n sub = r.byte()\n }\n id = r.byte()\n }\n\n if (id === K_UNPACK_INFO) {\n if (r.byte() !== K_FOLDER) throw new Error('7z UnpackInfo 缺少 Folder')\n const numFolders = r.number()\n const external = r.byte()\n if (external !== 0) throw new Error('7z 不支持外部 Folder 定义')\n for (let i = 0; i < numFolders; i++) info.folders.push(readFolder(r))\n if (r.byte() !== K_CODERS_UNPACK_SIZE) throw new Error('7z 缺少 CodersUnpackSize')\n for (const folder of info.folders) {\n let totalOut = 0\n for (const c of folder.coders) totalOut += c.numOut\n for (let i = 0; i < totalOut; i++) folder.unpackSizes.push(r.number())\n }\n info.folderHasCrc = info.folders.map(() => false)\n let sub = r.byte()\n while (sub !== K_END) {\n if (sub === K_CRC) {\n info.folderHasCrc = readDigests(r, info.folders.length)\n } else {\n throw new Error(`7z UnpackInfo 未知字段 0x${sub.toString(16)}`)\n }\n sub = r.byte()\n }\n id = r.byte()\n }\n\n // Default: one substream per folder. SubStreamsInfo overrides when present.\n info.numUnpackStreamsInFolders = info.folders.map(() => 1)\n if (id === K_SUBSTREAMS_INFO) {\n readSubStreamsInfo(r, info)\n id = r.byte()\n } else {\n info.subSizes = info.folders.map((f) => folderUnpackSize(f))\n }\n\n if (id !== K_END) throw new Error(`7z StreamsInfo 未以 End 结束 (0x${id.toString(16)})`)\n\n // Slice the archive's pack streams across folders in order, computing absolute offsets.\n let packIndex = 0\n let offset = SIG_HEADER_SIZE + info.packPos\n const packOffsets: number[] = []\n for (const sz of info.packSizes) {\n packOffsets.push(offset)\n offset += sz\n }\n for (const folder of info.folders) {\n for (let i = 0; i < folder.numPackStreams; i++) {\n folder.packStreams.push({ offset: packOffsets[packIndex], size: info.packSizes[packIndex] })\n packIndex++\n }\n }\n return info\n}\n\nfunction readSubStreamsInfo(r: Reader, info: StreamsInfo): void {\n let type = r.byte()\n if (type === K_NUM_UNPACK_STREAM) {\n info.numUnpackStreamsInFolders = info.folders.map(() => r.number())\n type = r.byte()\n } else {\n info.numUnpackStreamsInFolders = info.folders.map(() => 1)\n }\n\n const subSizes: number[] = []\n for (let f = 0; f < info.folders.length; f++) {\n const num = info.numUnpackStreamsInFolders[f]\n if (num === 0) continue\n let sum = 0\n if (type === K_SIZE) {\n for (let i = 1; i < num; i++) {\n const s = r.number()\n subSizes.push(s)\n sum += s\n }\n }\n subSizes.push(folderUnpackSize(info.folders[f]) - sum)\n }\n info.subSizes = subSizes\n if (type === K_SIZE) type = r.byte()\n\n // The SubStreamsInfo CRC block lists a digest only for substreams that don't already inherit a\n // folder-level CRC: a folder contributes its substream count, except a 1-substream folder that already\n // has its own CRC (which contributes none).\n let numDigests = 0\n for (let f = 0; f < info.folders.length; f++) {\n const num = info.numUnpackStreamsInFolders[f]\n if (num !== 1 || !info.folderHasCrc[f]) numDigests += num\n }\n if (type === K_CRC) {\n readDigests(r, numDigests)\n type = r.byte()\n }\n while (type !== K_END) type = r.byte() // defensively skip any unknown trailing field\n}\n\n/**\n * Read a digest block — an all-defined flag (or explicit bit vector) followed by a CRC32 per defined\n * entry — and return which entries were defined. The CRC values themselves are discarded (we don't verify).\n */\nfunction readDigests(r: Reader, count: number): boolean[] {\n const defined = r.boolVectorWithAll(count)\n for (const d of defined) if (d) r.bytes(4)\n return defined\n}\n\n// ── Files info ──────────────────────────────────────────────────────────────────────────────────\n\ninterface FilesInfo {\n names: string[]\n emptyStream: boolean[]\n emptyFile: boolean[]\n}\n\nfunction readFilesInfo(r: Reader): FilesInfo {\n const numFiles = r.number()\n const out: FilesInfo = {\n names: Array.from({ length: numFiles }, () => ''),\n emptyStream: Array.from({ length: numFiles }, () => false),\n emptyFile: [],\n }\n let numEmptyStreams = 0\n\n for (;;) {\n const propType = r.number()\n if (propType === K_END) break\n const size = r.number()\n const end = r.pos + size\n switch (propType) {\n case K_EMPTY_STREAM: {\n out.emptyStream = r.bitVector(numFiles)\n numEmptyStreams = out.emptyStream.filter(Boolean).length\n out.emptyFile = Array.from({ length: numEmptyStreams }, () => false)\n break\n }\n case K_EMPTY_FILE: {\n out.emptyFile = r.bitVector(numEmptyStreams)\n break\n }\n case K_NAME: {\n const external = r.byte()\n if (external !== 0) throw new Error('7z 不支持外部文件名')\n out.names = readNames(r, end, numFiles)\n break\n }\n default:\n break // CTime/ATime/MTime/Attributes/Dummy/… — not needed for browsing\n }\n r.pos = end // robust to partial reads\n }\n return out\n}\n\n/** Names are NUL-terminated UTF-16LE, concatenated. */\nfunction readNames(r: Reader, end: number, numFiles: number): string[] {\n const names: string[] = []\n let cur = ''\n while (r.pos + 1 < end && names.length < numFiles) {\n const lo = r.byte()\n const hi = r.byte()\n const code = lo | (hi << 8)\n if (code === 0) {\n names.push(cur)\n cur = ''\n } else {\n cur += String.fromCharCode(code)\n }\n }\n return names\n}\n\n// ── Header assembly ───────────────────────────────────────────────────────────────────────────────\n\nfunction buildEntries(streams: StreamsInfo | null, files: FilesInfo): SevenZipEntry[] {\n const entries: SevenZipEntry[] = []\n const numFiles = files.names.length\n\n // Walk the per-folder substreams in order, assigning each to the next file that has a stream.\n let folderIdx = 0\n let subInFolder = 0\n let offsetInFolder = 0\n let subSizeIdx = 0\n let emptyIdx = 0\n\n const advanceFolder = (): void => {\n // Move to the next folder that actually has substreams.\n while (\n streams &&\n folderIdx < streams.folders.length &&\n subInFolder >= streams.numUnpackStreamsInFolders[folderIdx]\n ) {\n folderIdx++\n subInFolder = 0\n offsetInFolder = 0\n }\n }\n\n for (let i = 0; i < numFiles; i++) {\n const path = files.names[i].replace(/\\\\/g, '/')\n const name = path.replace(/\\/+$/, '').split('/').pop() ?? path\n const hasStream = !files.emptyStream[i]\n if (hasStream) {\n advanceFolder()\n const size = streams ? streams.subSizes[subSizeIdx++] : 0\n entries.push({\n path,\n name,\n size,\n isDir: false,\n encrypted: streams ? folderHasAes(streams.folders[folderIdx]) : false,\n folderIndex: folderIdx,\n offsetInFolder,\n })\n offsetInFolder += size\n subInFolder++\n } else {\n // No stream: an empty file or a directory (distinguished by the emptyFile vector).\n const isEmptyFile = files.emptyFile[emptyIdx++] === true\n entries.push({\n path,\n name,\n size: 0,\n isDir: !isEmptyFile,\n encrypted: false,\n folderIndex: -1,\n offsetInFolder: 0,\n })\n }\n }\n return entries\n}\n\nfunction parseHeader(r: Reader): SevenZipArchive {\n const top = r.byte()\n if (top !== K_HEADER) throw new Error(`7z 头部类型异常 (0x${top.toString(16)})`)\n let id = r.byte()\n let streams: StreamsInfo | null = null\n let files: FilesInfo = { names: [], emptyStream: [], emptyFile: [] }\n\n // ArchiveProperties / AdditionalStreamsInfo are skipped — none of our archives use them.\n if (id === K_MAIN_STREAMS_INFO) {\n streams = readStreamsInfo(r)\n id = r.byte()\n }\n if (id === K_FILES_INFO) {\n files = readFilesInfo(r)\n id = r.byte()\n }\n\n return { entries: buildEntries(streams, files), folders: streams?.folders ?? [] }\n}\n\n// ── AES-256 decryption (encrypted 7z) ─────────────────────────────────────────────────────────────\n\ninterface DecodeCtx {\n password?: string\n}\n\n/** Does any coder in this folder apply 7z AES encryption? */\nfunction folderHasAes(folder: Folder): boolean {\n return folder.coders.some((c) => c.id === AES_CODER)\n}\n\n/** Password bytes as UTF-16LE (7z hashes the password this way). */\nfunction utf16le(s: string): Uint8Array {\n const b = new Uint8Array(s.length * 2)\n for (let i = 0; i < s.length; i++) {\n const c = s.charCodeAt(i)\n b[2 * i] = c & 0xff\n b[2 * i + 1] = (c >> 8) & 0xff\n }\n return b\n}\n\n/**\n * Derive the AES-256 key + IV for a 7z AES coder from its property bytes and the password. Property layout:\n * byte0 = numCyclesPower(bits 0-5) | saltPresent(bit 7) | ivPresent(bit 6); an optional byte1 packs extra\n * salt size (high nibble) + iv size (low nibble); then `saltSize` salt bytes, then `ivSize` iv bytes (zero-\n * padded to 16). The key is SHA-256( (salt ‖ passwordUTF16LE ‖ counterLE64) repeated 2^numCyclesPower times ),\n * the counter incrementing each round. numCyclesPower 0x3F is the degenerate \"no hashing\" form (key = salt ‖\n * password, truncated / zero-padded to 32 bytes).\n */\nfunction deriveSevenZipKey(\n props: Uint8Array,\n password: string,\n): { key: Uint8Array; iv: Uint8Array } {\n const b0 = props[0] ?? 0\n const numCyclesPower = b0 & 0x3f\n let saltSize = (b0 >> 7) & 1\n let ivSize = (b0 >> 6) & 1\n if (props.length >= 2) {\n const b1 = props[1]\n saltSize += b1 >> 4\n ivSize += b1 & 0x0f\n }\n const salt = props.subarray(2, 2 + saltSize)\n const iv = new Uint8Array(16)\n iv.set(props.subarray(2 + saltSize, 2 + saltSize + ivSize))\n\n const pass = utf16le(password)\n const key = new Uint8Array(32)\n if (numCyclesPower === 0x3f) {\n let pos = 0\n for (let i = 0; i < salt.length && pos < 32; i++) key[pos++] = salt[i]\n for (let i = 0; i < pass.length && pos < 32; i++) key[pos++] = pass[i]\n return { key, iv }\n }\n const rounds = 2 ** numCyclesPower\n const sha = new Sha256()\n const counter = new Uint8Array(8)\n for (let r = 0; r < rounds; r++) {\n if (salt.length) sha.update(salt)\n sha.update(pass)\n sha.update(counter)\n for (let i = 0; i < 8; i++) {\n counter[i] = (counter[i] + 1) & 0xff\n if (counter[i] !== 0) break\n }\n }\n key.set(sha.digest())\n return { key, iv }\n}\n\n// ── Coder / folder decoding ─────────────────────────────────────────────────────────────────────\n\nfunction decodeCoder(\n coder: Coder,\n inputs: Uint8Array[],\n outSize: number,\n ctx?: DecodeCtx,\n): Uint8Array {\n switch (coder.id) {\n case '00': // Copy\n return inputs[0].length === outSize ? inputs[0] : inputs[0].subarray(0, outSize)\n case '21': // LZMA2\n return lzma2Decode(inputs[0], coder.props, outSize)\n case '030101': // LZMA\n return lzmaDecode(inputs[0], coder.props, outSize)\n case '03030103': {\n // x86 BCJ — an in-place filter; copy so we don't mutate a shared buffer, then decode.\n const data = inputs[0].slice(0, outSize)\n bcjX86Decode(data)\n return data\n }\n case AES_CODER: {\n // AES-256-CBC: derive key+IV from the password, decrypt, then trim to the coder's real output size\n // (the ciphertext is padded up to a 16-byte boundary).\n if (!ctx?.password) throw new SevenZipEncryptedError()\n const { key, iv } = deriveSevenZipKey(coder.props, ctx.password)\n const dec = aesCbcDecrypt(key, iv, inputs[0])\n return dec.length === outSize ? dec : dec.subarray(0, outSize)\n }\n default:\n throw new Error(`不支持的压缩方式 (coder ${coder.id})`)\n }\n}\n\n/** Decode a folder's final output by resolving the coder graph (bind pairs ↔ packed streams). */\nfunction decodeFolder(buf: Uint8Array, folder: Folder, ctx?: DecodeCtx): Uint8Array {\n // Per-coder global stream base indices.\n const inBase: number[] = []\n const outBase: number[] = []\n let ti = 0\n let to = 0\n for (const c of folder.coders) {\n inBase.push(ti)\n outBase.push(to)\n ti += c.numIn\n to += c.numOut\n }\n\n const coderForOut = (outIndex: number): number => {\n for (let c = 0; c < folder.coders.length; c++) {\n if (outIndex >= outBase[c] && outIndex < outBase[c] + folder.coders[c].numOut) return c\n }\n throw new Error('7z folder 输出流索引越界')\n }\n\n const memo = new Map<number, Uint8Array>()\n const resolveOut = (outIndex: number): Uint8Array => {\n const cached = memo.get(outIndex)\n if (cached) return cached\n const c = coderForOut(outIndex)\n const coder = folder.coders[c]\n const inputs: Uint8Array[] = []\n for (let k = 0; k < coder.numIn; k++) {\n const globalIn = inBase[c] + k\n const bp = folder.bindPairs.find((b) => b.inIndex === globalIn)\n if (bp) {\n inputs.push(resolveOut(bp.outIndex))\n } else {\n const packPos = folder.packedIndices.indexOf(globalIn)\n if (packPos < 0) throw new Error('7z folder 输入流未绑定也非打包流')\n const ps = folder.packStreams[packPos]\n inputs.push(buf.subarray(ps.offset, ps.offset + ps.size))\n }\n }\n const result = decodeCoder(coder, inputs, folder.unpackSizes[outIndex], ctx)\n memo.set(outIndex, result)\n return result\n }\n\n return resolveOut(folderFinalOutIndex(folder))\n}\n\n// ── Public API ────────────────────────────────────────────────────────────────────────────────────\n\n/** Is the buffer a 7z archive (by signature)? */\nexport function isSevenZip(buf: ArrayBuffer): boolean {\n if (buf.byteLength < SIG_HEADER_SIZE) return false\n const b = new Uint8Array(buf, 0, SIGNATURE.length)\n return SIGNATURE.every((s, i) => b[i] === s)\n}\n\n/**\n * Parse a 7z archive's header into a flat entry list (+ folder metadata for extraction). When the header\n * itself is AES-encrypted (\"encrypt file names\"), listing needs the `password`: without one this throws\n * {@link SevenZipEncryptedError} (so the UI can prompt); with a wrong one it throws {@link SevenZipPasswordError}.\n */\nexport function readSevenZip(buf: ArrayBuffer, password?: string): SevenZipArchive {\n if (!isSevenZip(buf)) throw new Error('不是有效的 7z 文件')\n const view = new DataView(buf)\n const bytes = new Uint8Array(buf)\n\n const nextHeaderOffset = readUInt64LE(view, 12)\n const nextHeaderSize = readUInt64LE(view, 20)\n if (nextHeaderSize === 0) return { entries: [], folders: [] } // empty archive\n\n const headerStart = SIG_HEADER_SIZE + nextHeaderOffset\n if (headerStart + nextHeaderSize > buf.byteLength) throw new Error('7z 头部超出文件范围')\n\n let headerBytes: Uint8Array = bytes.subarray(headerStart, headerStart + nextHeaderSize)\n let r = new Reader(headerBytes)\n let first = r.byte()\n let encryptedHeader = false\n\n // The header is often itself a compressed stream (kEncodedHeader): decode that folder to get the real\n // header bytes, then parse those. That folder can additionally be AES-encrypted (encrypted file names).\n if (first === K_ENCODED_HEADER) {\n const streams = readStreamsInfo(r)\n if (streams.folders.length !== 1) throw new Error('7z 编码头部格式异常')\n encryptedHeader = folderHasAes(streams.folders[0])\n try {\n headerBytes = decodeFolder(bytes, streams.folders[0], { password })\n } catch (e) {\n // No password → propagate SevenZipEncryptedError (UI prompts); a wrong one makes the LZMA stage choke\n // on garbage — report it as a password error.\n if (encryptedHeader && password && !(e instanceof SevenZipEncryptedError)) {\n throw new SevenZipPasswordError()\n }\n throw e\n }\n r = new Reader(headerBytes)\n first = r.byte()\n }\n if (first !== K_HEADER) {\n if (encryptedHeader && password) throw new SevenZipPasswordError() // garbage from a wrong password\n throw new Error(`7z 头部类型异常 (0x${first.toString(16)})`)\n }\n // Re-run the header parse from the top (parseHeader expects to read the kHeader id itself).\n r.pos = 0\n try {\n return parseHeader(r)\n } catch (e) {\n if (encryptedHeader && password) throw new SevenZipPasswordError()\n throw e\n }\n}\n\n// Cache the last decoded folder so extracting several files from one solid block doesn't re-decompress it.\nlet lastFolderKey: { buf: ArrayBuffer; index: number } | null = null\nlet lastFolderBytes: Uint8Array | null = null\n\n/**\n * Extract one entry's bytes. Decodes the whole folder (caching it) and slices out the file's span. For an\n * AES-encrypted folder the `password` is required — missing → {@link SevenZipEncryptedError}, wrong →\n * {@link SevenZipPasswordError}.\n */\nexport function extractSevenZipEntry(\n buf: ArrayBuffer,\n archive: SevenZipArchive,\n entry: SevenZipEntry,\n password?: string,\n): Uint8Array {\n if (entry.isDir || entry.folderIndex < 0 || entry.size === 0) return new Uint8Array(0)\n const folder = archive.folders[entry.folderIndex]\n if (!folder) throw new Error('7z 条目缺少对应 folder')\n\n let folderBytes: Uint8Array\n if (\n lastFolderBytes &&\n lastFolderKey &&\n lastFolderKey.buf === buf &&\n lastFolderKey.index === entry.folderIndex\n ) {\n folderBytes = lastFolderBytes\n } else {\n try {\n folderBytes = decodeFolder(new Uint8Array(buf), folder, { password })\n } catch (e) {\n if (folderHasAes(folder) && password && !(e instanceof SevenZipEncryptedError)) {\n throw new SevenZipPasswordError()\n }\n throw e\n }\n lastFolderKey = { buf, index: entry.folderIndex }\n lastFolderBytes = folderBytes\n }\n return folderBytes.subarray(entry.offsetInFolder, entry.offsetInFolder + entry.size).slice()\n}\n"],"mappings":";;;;AAkBA,IAAM,IAAY;CAAC;CAAM;CAAM;CAAM;CAAM;CAAM;AAAI,GAC/C,IAAkB,IAGlB,IAAQ,GACR,IAAW,GACX,IAAsB,GACtB,IAAe,GACf,IAAc,GACd,IAAgB,GAChB,IAAoB,GACpB,IAAS,GACT,IAAQ,IACR,IAAW,IACX,IAAuB,IACvB,IAAsB,IACtB,IAAiB,IACjB,IAAe,IACf,IAAS,IACT,IAAmB,IAGnB,IAAY,YAGL,IAAb,cAA4C,MAAM;CAChD,cAAc;EAEZ,AADA,MAAM,eAAe,GACrB,KAAK,OAAO;CACd;AACF,GAEa,IAAb,cAA2C,MAAM;CAC/C,cAAc;EAEZ,AADA,MAAM,WAAW,GACjB,KAAK,OAAO;CACd;AACF,GAmDM,IAAN,MAAa;CAEiB;CAD5B,MAAM;CACN,YAAY,GAAiC;EAAjB,KAAA,MAAA;CAAkB;CAE9C,OAAe;EACb,IAAI,KAAK,OAAO,KAAK,IAAI,QAAQ,MAAU,MAAM,WAAW;EAC5D,OAAO,KAAK,IAAI,KAAK;CACvB;CAEA,MAAM,GAAuB;EAC3B,IAAM,IAAI,KAAK,IAAI,SAAS,KAAK,KAAK,KAAK,MAAM,CAAC;EAElD,OADA,KAAK,OAAO,GACL;CACT;CAGA,SAAiB;EACf,IAAM,IAAQ,KAAK,KAAK,GACpB,IAAO,KACP,IAAQ;EACZ,KAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KAAK;GAC1B,KAAK,IAAQ,OAAU,GAAG;IACxB,MAAU,IAAS,IAAO,KAAM,MAAM,IAAI;IAC1C;GACF;GAEA,AADA,KAAS,KAAK,KAAK,IAAI,MAAM,IAAI,IACjC,MAAS;EACX;EACA,OAAO;CACT;CAGA,UAAU,GAAsB;EAC9B,IAAM,IAAe,CAAC,GAClB,IAAI,GACJ,IAAO;EACX,KAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KAMrB,AALI,MAAS,MACX,IAAI,KAAK,KAAK,GACd,IAAO,MAET,EAAE,MAAM,IAAI,OAAU,CAAC,GACvB,MAAS;EAEX,OAAO;CACT;CAGA,kBAAkB,GAAsB;EAEtC,OADI,KAAK,KAAK,MAAM,IACb,KAAK,UAAU,CAAC,IADO,MAAM,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAI;CAEpE;AACF;AAEA,SAAS,EAAa,GAAgB,GAAqB;CACzD,OAAO,EAAK,UAAU,IAAM,GAAG,EAAI,IAAI,aAAc,EAAK,UAAU,GAAK,EAAI;AAC/E;AAIA,SAAS,EAAW,GAAmB;CACrC,IAAM,IAAY,EAAE,OAAO,GACrB,IAAkB,CAAC,GACrB,IAAU,GACV,IAAW;CACf,KAAK,IAAI,IAAI,GAAG,IAAI,GAAW,KAAK;EAClC,IAAM,IAAQ,EAAE,KAAK,GACf,IAAS,IAAQ,IACjB,IAAU,EAAE,MAAM,CAAM,GAC1B,IAAK;EACT,KAAK,IAAM,KAAK,GAAS,KAAM,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,GAAG,GAAG;EAC7D,IAAI,IAAQ,GACR,IAAS;EACb,AAAI,IAAQ,OACV,IAAQ,EAAE,OAAO,GACjB,IAAS,EAAE,OAAO;EAEpB,IAAI,IAAQ,IAAI,WAAY;EAI5B,AAHI,IAAQ,OAAM,IAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,MAAM,IACpD,EAAO,KAAK;GAAE;GAAI;GAAO;GAAQ;EAAM,CAAC,GACxC,KAAW,GACX,KAAY;CACd;CAEA,IAAM,IAAe,IAAW,GAC1B,IAAwB,CAAC;CAC/B,KAAK,IAAI,IAAI,GAAG,IAAI,GAAc,KAChC,EAAU,KAAK;EAAE,SAAS,EAAE,OAAO;EAAG,UAAU,EAAE,OAAO;CAAE,CAAC;CAG9D,IAAM,IAAiB,IAAU,GAC3B,IAA0B,CAAC;CACjC,IAAI,MAAmB;OAEhB,IAAI,IAAI,GAAG,IAAI,GAAS,KAC3B,IAAI,CAAC,EAAU,MAAM,MAAO,EAAG,YAAY,CAAC,GAAG;GAC7C,EAAc,KAAK,CAAC;GACpB;EACF;QAGF,KAAK,IAAI,IAAI,GAAG,IAAI,GAAgB,KAAK,EAAc,KAAK,EAAE,OAAO,CAAC;CAGxE,OAAO;EACL;EACA;EACA;EACA,aAAa,CAAC;EACd;EACA,aAAa,CAAC;CAChB;AACF;AAGA,SAAS,EAAoB,GAAwB;CACnD,IAAI,IAAQ;CACZ,KAAK,IAAM,KAAK,EAAO,QAAQ,KAAS,EAAE;CAC1C,KAAK,IAAI,IAAI,GAAG,IAAI,GAAO,KACzB,IAAI,CAAC,EAAO,UAAU,MAAM,MAAO,EAAG,aAAa,CAAC,GAAG,OAAO;CAEhE,OAAO,IAAQ;AACjB;AAEA,SAAS,EAAiB,GAAwB;CAChD,OAAO,EAAO,YAAY,EAAoB,CAAM,MAAM;AAC5D;AAcA,SAAS,EAAgB,GAAwB;CAC/C,IAAM,IAAoB;EACxB,SAAS;EACT,WAAW,CAAC;EACZ,SAAS,CAAC;EACV,2BAA2B,CAAC;EAC5B,UAAU,CAAC;EACX,cAAc,CAAC;CACjB,GACI,IAAK,EAAE,KAAK;CAEhB,IAAI,MAAO,GAAa;EACtB,EAAK,UAAU,EAAE,OAAO;EACxB,IAAM,IAAU,EAAE,OAAO,GACrB,IAAM,EAAE,KAAK;EACjB,OAAO,MAAQ,IAAO;GACpB,IAAI,MAAQ,GACV,KAAK,IAAI,IAAI,GAAG,IAAI,GAAS,KAAK,EAAK,UAAU,KAAK,EAAE,OAAO,CAAC;QAC3D,IAAI,MAAQ,GACjB,EAAY,GAAG,CAAO;QAEtB,MAAU,MAAM,sBAAsB,EAAI,SAAS,EAAE,GAAG;GAE1D,IAAM,EAAE,KAAK;EACf;EACA,IAAK,EAAE,KAAK;CACd;CAEA,IAAI,MAAO,GAAe;EACxB,IAAI,EAAE,KAAK,MAAM,GAAU,MAAU,MAAM,yBAAyB;EACpE,IAAM,IAAa,EAAE,OAAO;EAE5B,IADiB,EAAE,KACf,MAAa,GAAG,MAAU,MAAM,oBAAoB;EACxD,KAAK,IAAI,IAAI,GAAG,IAAI,GAAY,KAAK,EAAK,QAAQ,KAAK,EAAW,CAAC,CAAC;EACpE,IAAI,EAAE,KAAK,MAAM,GAAsB,MAAU,MAAM,wBAAwB;EAC/E,KAAK,IAAM,KAAU,EAAK,SAAS;GACjC,IAAI,IAAW;GACf,KAAK,IAAM,KAAK,EAAO,QAAQ,KAAY,EAAE;GAC7C,KAAK,IAAI,IAAI,GAAG,IAAI,GAAU,KAAK,EAAO,YAAY,KAAK,EAAE,OAAO,CAAC;EACvE;EACA,EAAK,eAAe,EAAK,QAAQ,UAAU,EAAK;EAChD,IAAI,IAAM,EAAE,KAAK;EACjB,OAAO,MAAQ,IAAO;GACpB,IAAI,MAAQ,GACV,EAAK,eAAe,EAAY,GAAG,EAAK,QAAQ,MAAM;QAEtD,MAAU,MAAM,wBAAwB,EAAI,SAAS,EAAE,GAAG;GAE5D,IAAM,EAAE,KAAK;EACf;EACA,IAAK,EAAE,KAAK;CACd;CAWA,IARA,EAAK,4BAA4B,EAAK,QAAQ,UAAU,CAAC,GACrD,MAAO,KACT,EAAmB,GAAG,CAAI,GAC1B,IAAK,EAAE,KAAK,KAEZ,EAAK,WAAW,EAAK,QAAQ,KAAK,MAAM,EAAiB,CAAC,CAAC,GAGzD,MAAO,GAAO,MAAU,MAAM,+BAA+B,EAAG,SAAS,EAAE,EAAE,EAAE;CAGnF,IAAI,IAAY,GACZ,IAAS,IAAkB,EAAK,SAC9B,IAAwB,CAAC;CAC/B,KAAK,IAAM,KAAM,EAAK,WAEpB,AADA,EAAY,KAAK,CAAM,GACvB,KAAU;CAEZ,KAAK,IAAM,KAAU,EAAK,SACxB,KAAK,IAAI,IAAI,GAAG,IAAI,EAAO,gBAAgB,KAEzC,AADA,EAAO,YAAY,KAAK;EAAE,QAAQ,EAAY;EAAY,MAAM,EAAK,UAAU;CAAW,CAAC,GAC3F;CAGJ,OAAO;AACT;AAEA,SAAS,EAAmB,GAAW,GAAyB;CAC9D,IAAI,IAAO,EAAE,KAAK;CAClB,AAAI,MAAS,KACX,EAAK,4BAA4B,EAAK,QAAQ,UAAU,EAAE,OAAO,CAAC,GAClE,IAAO,EAAE,KAAK,KAEd,EAAK,4BAA4B,EAAK,QAAQ,UAAU,CAAC;CAG3D,IAAM,IAAqB,CAAC;CAC5B,KAAK,IAAI,IAAI,GAAG,IAAI,EAAK,QAAQ,QAAQ,KAAK;EAC5C,IAAM,IAAM,EAAK,0BAA0B;EAC3C,IAAI,MAAQ,GAAG;EACf,IAAI,IAAM;EACV,IAAI,MAAS,GACX,KAAK,IAAI,IAAI,GAAG,IAAI,GAAK,KAAK;GAC5B,IAAM,IAAI,EAAE,OAAO;GAEnB,AADA,EAAS,KAAK,CAAC,GACf,KAAO;EACT;EAEF,EAAS,KAAK,EAAiB,EAAK,QAAQ,EAAE,IAAI,CAAG;CACvD;CAEA,AADA,EAAK,WAAW,GACZ,MAAS,MAAQ,IAAO,EAAE,KAAK;CAKnC,IAAI,IAAa;CACjB,KAAK,IAAI,IAAI,GAAG,IAAI,EAAK,QAAQ,QAAQ,KAAK;EAC5C,IAAM,IAAM,EAAK,0BAA0B;EAC3C,CAAI,MAAQ,KAAK,CAAC,EAAK,aAAa,QAAI,KAAc;CACxD;CAKA,KAJI,MAAS,MACX,EAAY,GAAG,CAAU,GACzB,IAAO,EAAE,KAAK,IAET,MAAS,IAAO,IAAO,EAAE,KAAK;AACvC;AAMA,SAAS,EAAY,GAAW,GAA0B;CACxD,IAAM,IAAU,EAAE,kBAAkB,CAAK;CACzC,KAAK,IAAM,KAAK,GAAS,AAAI,KAAG,EAAE,MAAM,CAAC;CACzC,OAAO;AACT;AAUA,SAAS,EAAc,GAAsB;CAC3C,IAAM,IAAW,EAAE,OAAO,GACpB,IAAiB;EACrB,OAAO,MAAM,KAAK,EAAE,QAAQ,EAAS,SAAS,EAAE;EAChD,aAAa,MAAM,KAAK,EAAE,QAAQ,EAAS,SAAS,EAAK;EACzD,WAAW,CAAC;CACd,GACI,IAAkB;CAEtB,SAAS;EACP,IAAM,IAAW,EAAE,OAAO;EAC1B,IAAI,MAAa,GAAO;EACxB,IAAM,IAAO,EAAE,OAAO,GAChB,IAAM,EAAE,MAAM;EACpB,QAAQ,GAAR;GACE,KAAK;IAGH,AAFA,EAAI,cAAc,EAAE,UAAU,CAAQ,GACtC,IAAkB,EAAI,YAAY,OAAO,OAAO,CAAC,CAAC,QAClD,EAAI,YAAY,MAAM,KAAK,EAAE,QAAQ,EAAgB,SAAS,EAAK;IACnE;GAEF,KAAK;IACH,EAAI,YAAY,EAAE,UAAU,CAAe;IAC3C;GAEF,KAAK;IAEH,IADiB,EAAE,KACf,MAAa,GAAG,MAAU,MAAM,aAAa;IACjD,EAAI,QAAQ,EAAU,GAAG,GAAK,CAAQ;IACtC;GAEF,SACE;EACJ;EACA,EAAE,MAAM;CACV;CACA,OAAO;AACT;AAGA,SAAS,EAAU,GAAW,GAAa,GAA4B;CACrE,IAAM,IAAkB,CAAC,GACrB,IAAM;CACV,OAAO,EAAE,MAAM,IAAI,KAAO,EAAM,SAAS,IAAU;EAGjD,IAAM,IAFK,EAAE,KAEA,IADF,EAAE,KACM,KAAM;EACzB,AAAI,MAAS,KACX,EAAM,KAAK,CAAG,GACd,IAAM,MAEN,KAAO,OAAO,aAAa,CAAI;CAEnC;CACA,OAAO;AACT;AAIA,SAAS,EAAa,GAA6B,GAAmC;CACpF,IAAM,IAA2B,CAAC,GAC5B,IAAW,EAAM,MAAM,QAGzB,IAAY,GACZ,IAAc,GACd,IAAiB,GACjB,IAAa,GACb,IAAW,GAET,UAA4B;EAEhC,OACE,KACA,IAAY,EAAQ,QAAQ,UAC5B,KAAe,EAAQ,0BAA0B,KAIjD,AAFA,KACA,IAAc,GACd,IAAiB;CAErB;CAEA,KAAK,IAAI,IAAI,GAAG,IAAI,GAAU,KAAK;EACjC,IAAM,IAAO,EAAM,MAAM,EAAE,CAAC,QAAQ,OAAO,GAAG,GACxC,IAAO,EAAK,QAAQ,QAAQ,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,KAAK;EAE1D,IADmB,EAAM,YAAY,IAe9B;GAEL,IAAM,IAAc,EAAM,UAAU,SAAgB;GACpD,EAAQ,KAAK;IACX;IACA;IACA,MAAM;IACN,OAAO,CAAC;IACR,WAAW;IACX,aAAa;IACb,gBAAgB;GAClB,CAAC;EACH,OA1Be;GACb,EAAc;GACd,IAAM,IAAO,IAAU,EAAQ,SAAS,OAAgB;GAWxD,AAVA,EAAQ,KAAK;IACX;IACA;IACA;IACA,OAAO;IACP,WAAW,IAAU,EAAa,EAAQ,QAAQ,EAAU,IAAI;IAChE,aAAa;IACb;GACF,CAAC,GACD,KAAkB,GAClB;EACF;CAaF;CACA,OAAO;AACT;AAEA,SAAS,EAAY,GAA4B;CAC/C,IAAM,IAAM,EAAE,KAAK;CACnB,IAAI,MAAQ,GAAU,MAAU,MAAM,gBAAgB,EAAI,SAAS,EAAE,EAAE,EAAE;CACzE,IAAI,IAAK,EAAE,KAAK,GACZ,IAA8B,MAC9B,IAAmB;EAAE,OAAO,CAAC;EAAG,aAAa,CAAC;EAAG,WAAW,CAAC;CAAE;CAYnE,OATI,MAAO,MACT,IAAU,EAAgB,CAAC,GAC3B,IAAK,EAAE,KAAK,IAEV,MAAO,MACT,IAAQ,EAAc,CAAC,GACvB,IAAK,EAAE,KAAK,IAGP;EAAE,SAAS,EAAa,GAAS,CAAK;EAAG,SAAS,GAAS,WAAW,CAAC;CAAE;AAClF;AASA,SAAS,EAAa,GAAyB;CAC7C,OAAO,EAAO,OAAO,MAAM,MAAM,EAAE,OAAO,CAAS;AACrD;AAGA,SAAS,EAAQ,GAAuB;CACtC,IAAM,IAAI,IAAI,WAAW,EAAE,SAAS,CAAC;CACrC,KAAK,IAAI,IAAI,GAAG,IAAI,EAAE,QAAQ,KAAK;EACjC,IAAM,IAAI,EAAE,WAAW,CAAC;EAExB,AADA,EAAE,IAAI,KAAK,IAAI,KACf,EAAE,IAAI,IAAI,KAAM,KAAK,IAAK;CAC5B;CACA,OAAO;AACT;AAUA,SAAS,EACP,GACA,GACqC;CACrC,IAAM,IAAK,EAAM,MAAM,GACjB,IAAiB,IAAK,IACxB,IAAY,KAAM,IAAK,GACvB,IAAU,KAAM,IAAK;CACzB,IAAI,EAAM,UAAU,GAAG;EACrB,IAAM,IAAK,EAAM;EAEjB,AADA,KAAY,KAAM,GAClB,KAAU,IAAK;CACjB;CACA,IAAM,IAAO,EAAM,SAAS,GAAG,IAAI,CAAQ,GACrC,IAAK,IAAI,WAAW,EAAE;CAC5B,EAAG,IAAI,EAAM,SAAS,IAAI,GAAU,IAAI,IAAW,CAAM,CAAC;CAE1D,IAAM,IAAO,EAAQ,CAAQ,GACvB,IAAM,IAAI,WAAW,EAAE;CAC7B,IAAI,MAAmB,IAAM;EAC3B,IAAI,IAAM;EACV,KAAK,IAAI,IAAI,GAAG,IAAI,EAAK,UAAU,IAAM,IAAI,KAAK,EAAI,OAAS,EAAK;EACpE,KAAK,IAAI,IAAI,GAAG,IAAI,EAAK,UAAU,IAAM,IAAI,KAAK,EAAI,OAAS,EAAK;EACpE,OAAO;GAAE;GAAK;EAAG;CACnB;CACA,IAAM,IAAS,KAAK,GACd,IAAM,IAAI,EAAO,GACjB,IAAU,IAAI,WAAW,CAAC;CAChC,KAAK,IAAI,IAAI,GAAG,IAAI,GAAQ,KAAK;EAG/B,AAFI,EAAK,UAAQ,EAAI,OAAO,CAAI,GAChC,EAAI,OAAO,CAAI,GACf,EAAI,OAAO,CAAO;EAClB,KAAK,IAAI,IAAI,GAAG,IAAI,MAClB,EAAQ,KAAM,EAAQ,KAAK,IAAK,KAC5B,EAAQ,OAAO,IAFE;CAIzB;CAEA,OADA,EAAI,IAAI,EAAI,OAAO,CAAC,GACb;EAAE;EAAK;CAAG;AACnB;AAIA,SAAS,EACP,GACA,GACA,GACA,GACY;CACZ,QAAQ,EAAM,IAAd;EACE,KAAK,MACH,OAAO,EAAO,EAAE,CAAC,WAAW,IAAU,EAAO,KAAK,EAAO,EAAE,CAAC,SAAS,GAAG,CAAO;EACjF,KAAK,MACH,OAAO,EAAY,EAAO,IAAI,EAAM,OAAO,CAAO;EACpD,KAAK,UACH,OAAO,EAAW,EAAO,IAAI,EAAM,OAAO,CAAO;EACnD,KAAK,YAAY;GAEf,IAAM,IAAO,EAAO,EAAE,CAAC,MAAM,GAAG,CAAO;GAEvC,OADA,EAAa,CAAI,GACV;EACT;EACA,KAAK,GAAW;GAGd,IAAI,CAAC,GAAK,UAAU,MAAM,IAAI,EAAuB;GACrD,IAAM,EAAE,QAAK,UAAO,EAAkB,EAAM,OAAO,EAAI,QAAQ,GACzD,IAAM,EAAc,GAAK,GAAI,EAAO,EAAE;GAC5C,OAAO,EAAI,WAAW,IAAU,IAAM,EAAI,SAAS,GAAG,CAAO;EAC/D;EACA,SACE,MAAU,MAAM,mBAAmB,EAAM,GAAG,EAAE;CAClD;AACF;AAGA,SAAS,EAAa,GAAiB,GAAgB,GAA6B;CAElF,IAAM,IAAmB,CAAC,GACpB,IAAoB,CAAC,GACvB,IAAK,GACL,IAAK;CACT,KAAK,IAAM,KAAK,EAAO,QAIrB,AAHA,EAAO,KAAK,CAAE,GACd,EAAQ,KAAK,CAAE,GACf,KAAM,EAAE,OACR,KAAM,EAAE;CAGV,IAAM,KAAe,MAA6B;EAChD,KAAK,IAAI,IAAI,GAAG,IAAI,EAAO,OAAO,QAAQ,KACxC,IAAI,KAAY,EAAQ,MAAM,IAAW,EAAQ,KAAK,EAAO,OAAO,EAAE,CAAC,QAAQ,OAAO;EAExF,MAAU,MAAM,mBAAmB;CACrC,GAEM,oBAAO,IAAI,IAAwB,GACnC,KAAc,MAAiC;EACnD,IAAM,IAAS,EAAK,IAAI,CAAQ;EAChC,IAAI,GAAQ,OAAO;EACnB,IAAM,IAAI,EAAY,CAAQ,GACxB,IAAQ,EAAO,OAAO,IACtB,IAAuB,CAAC;EAC9B,KAAK,IAAI,IAAI,GAAG,IAAI,EAAM,OAAO,KAAK;GACpC,IAAM,IAAW,EAAO,KAAK,GACvB,IAAK,EAAO,UAAU,MAAM,MAAM,EAAE,YAAY,CAAQ;GAC9D,IAAI,GACF,EAAO,KAAK,EAAW,EAAG,QAAQ,CAAC;QAC9B;IACL,IAAM,IAAU,EAAO,cAAc,QAAQ,CAAQ;IACrD,IAAI,IAAU,GAAG,MAAU,MAAM,uBAAuB;IACxD,IAAM,IAAK,EAAO,YAAY;IAC9B,EAAO,KAAK,EAAI,SAAS,EAAG,QAAQ,EAAG,SAAS,EAAG,IAAI,CAAC;GAC1D;EACF;EACA,IAAM,IAAS,EAAY,GAAO,GAAQ,EAAO,YAAY,IAAW,CAAG;EAE3E,OADA,EAAK,IAAI,GAAU,CAAM,GAClB;CACT;CAEA,OAAO,EAAW,EAAoB,CAAM,CAAC;AAC/C;AAKA,SAAgB,EAAW,GAA2B;CACpD,IAAI,EAAI,aAAa,GAAiB,OAAO;CAC7C,IAAM,IAAI,IAAI,WAAW,GAAK,GAAG,EAAU,MAAM;CACjD,OAAO,EAAU,OAAO,GAAG,MAAM,EAAE,OAAO,CAAC;AAC7C;AAOA,SAAgB,EAAa,GAAkB,GAAoC;CACjF,IAAI,CAAC,EAAW,CAAG,GAAG,MAAU,MAAM,aAAa;CACnD,IAAM,IAAO,IAAI,SAAS,CAAG,GACvB,IAAQ,IAAI,WAAW,CAAG,GAE1B,IAAmB,EAAa,GAAM,EAAE,GACxC,IAAiB,EAAa,GAAM,EAAE;CAC5C,IAAI,MAAmB,GAAG,OAAO;EAAE,SAAS,CAAC;EAAG,SAAS,CAAC;CAAE;CAE5D,IAAM,IAAc,IAAkB;CACtC,IAAI,IAAc,IAAiB,EAAI,YAAY,MAAU,MAAM,aAAa;CAEhF,IAAI,IAA0B,EAAM,SAAS,GAAa,IAAc,CAAc,GAClF,IAAI,IAAI,EAAO,CAAW,GAC1B,IAAQ,EAAE,KAAK,GACf,IAAkB;CAItB,IAAI,MAAU,GAAkB;EAC9B,IAAM,IAAU,EAAgB,CAAC;EACjC,IAAI,EAAQ,QAAQ,WAAW,GAAG,MAAU,MAAM,aAAa;EAC/D,IAAkB,EAAa,EAAQ,QAAQ,EAAE;EACjD,IAAI;GACF,IAAc,EAAa,GAAO,EAAQ,QAAQ,IAAI,EAAE,YAAS,CAAC;EACpE,SAAS,GAAG;GAMV,MAHI,KAAmB,KAAY,EAAE,aAAa,KAC1C,IAAI,EAAsB,IAE5B;EACR;EAEA,AADA,IAAI,IAAI,EAAO,CAAW,GAC1B,IAAQ,EAAE,KAAK;CACjB;CACA,IAAI,MAAU,GAEZ,MADI,KAAmB,IAAgB,IAAI,EAAsB,IACvD,MAAM,gBAAgB,EAAM,SAAS,EAAE,EAAE,EAAE;CAGvD,EAAE,MAAM;CACR,IAAI;EACF,OAAO,EAAY,CAAC;CACtB,SAAS,GAAG;EAEV,MADI,KAAmB,IAAgB,IAAI,EAAsB,IAC3D;CACR;AACF;AAGA,IAAI,IAA4D,MAC5D,IAAqC;AAOzC,SAAgB,EACd,GACA,GACA,GACA,GACY;CACZ,IAAI,EAAM,SAAS,EAAM,cAAc,KAAK,EAAM,SAAS,GAAG,OAAO,IAAI,WAAY;CACrF,IAAM,IAAS,EAAQ,QAAQ,EAAM;CACrC,IAAI,CAAC,GAAQ,MAAU,MAAM,kBAAkB;CAE/C,IAAI;CACJ,IACE,KACA,KACA,EAAc,QAAQ,KACtB,EAAc,UAAU,EAAM,aAE9B,IAAc;MACT;EACL,IAAI;GACF,IAAc,EAAa,IAAI,WAAW,CAAG,GAAG,GAAQ,EAAE,YAAS,CAAC;EACtE,SAAS,GAAG;GAIV,MAHI,EAAa,CAAM,KAAK,KAAY,EAAE,aAAa,KAC/C,IAAI,EAAsB,IAE5B;EACR;EAEA,AADA,IAAgB;GAAE;GAAK,OAAO,EAAM;EAAY,GAChD,IAAkB;CACpB;CACA,OAAO,EAAY,SAAS,EAAM,gBAAgB,EAAM,iBAAiB,EAAM,IAAI,CAAC,CAAC,MAAM;AAC7F"}
|
package/dist/lib/sha1.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sha1.js","names":[],"sources":["../../src/lib/sha1.ts"],"sourcesContent":["/**\n * Minimal synchronous SHA-1 (FIPS 180-4) — no dependency, no WASM.\n *\n * JKS / JCEKS keystores lean on SHA-1 in two synchronous, tight loops (see lib/jks.ts): the store-integrity\n * MAC — SHA1(passwordUtf16BE ‖ \"Mighty Aphrodite\" ‖ storeBytes) — and Sun's KeyProtector, which chains SHA-1\n * over password‖runningDigest once per 20 bytes of key material. WebCrypto's digest is async + one-shot, which\n * would turn the whole parse path async and force a throwaway buffer each round; this incremental hasher stays\n * synchronous and lets the KeyProtector feed the password + running digest directly, mirroring {@link Sha256}.\n *\n * SHA-1 is broken for collision resistance — used here ONLY because the JKS on-disk format mandates it, never\n * as a security primitive for anything new. Not constant-time (fine for reading a keystore locally).\n */\n\nexport class Sha1 {\n private readonly h = new Uint32Array([0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0])\n private readonly block = new Uint8Array(64)\n private readonly w = new Uint32Array(80)\n private blen = 0\n private total = 0 // total bytes fed (for the length suffix)\n\n update(data: Uint8Array): this {\n this.total += data.length\n for (let i = 0; i < data.length; i++) {\n this.block[this.blen++] = data[i]\n if (this.blen === 64) {\n this.compress()\n this.blen = 0\n }\n }\n return this\n }\n\n digest(): Uint8Array {\n const bitLen = this.total * 8\n // Append 0x80, pad with zeros, then a 64-bit big-endian bit length.\n this.block[this.blen++] = 0x80\n if (this.blen > 56) {\n while (this.blen < 64) this.block[this.blen++] = 0\n this.compress()\n this.blen = 0\n }\n while (this.blen < 56) this.block[this.blen++] = 0\n // High 32 bits (bitLen can exceed 2^32; use division to stay exact).\n const hi = Math.floor(bitLen / 0x100000000)\n const lo = bitLen >>> 0\n this.block[56] = (hi >>> 24) & 0xff\n this.block[57] = (hi >>> 16) & 0xff\n this.block[58] = (hi >>> 8) & 0xff\n this.block[59] = hi & 0xff\n this.block[60] = (lo >>> 24) & 0xff\n this.block[61] = (lo >>> 16) & 0xff\n this.block[62] = (lo >>> 8) & 0xff\n this.block[63] = lo & 0xff\n this.compress()\n\n const out = new Uint8Array(20)\n for (let i = 0; i < 5; i++) {\n out[4 * i] = (this.h[i] >>> 24) & 0xff\n out[4 * i + 1] = (this.h[i] >>> 16) & 0xff\n out[4 * i + 2] = (this.h[i] >>> 8) & 0xff\n out[4 * i + 3] = this.h[i] & 0xff\n }\n return out\n }\n\n private compress(): void {\n const w = this.w\n const blk = this.block\n for (let i = 0; i < 16; i++) {\n w[i] = (blk[4 * i] << 24) | (blk[4 * i + 1] << 16) | (blk[4 * i + 2] << 8) | blk[4 * i + 3]\n }\n for (let i = 16; i < 80; i++) {\n const x = w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]\n w[i] = (x << 1) | (x >>> 31)\n }\n let a = this.h[0]\n let b = this.h[1]\n let c = this.h[2]\n let d = this.h[3]\n let e = this.h[4]\n for (let i = 0; i < 80; i++) {\n let f: number\n let k: number\n if (i < 20) {\n f = (b & c) | (~b & d)\n k = 0x5a827999\n } else if (i < 40) {\n f = b ^ c ^ d\n k = 0x6ed9eba1\n } else if (i < 60) {\n f = (b & c) | (b & d) | (c & d)\n k = 0x8f1bbcdc\n } else {\n f = b ^ c ^ d\n k = 0xca62c1d6\n }\n const t = (((a << 5) | (a >>> 27)) + f + e + k + w[i]) | 0\n e = d\n d = c\n c = (b << 30) | (b >>> 2)\n b = a\n a = t\n }\n this.h[0] = (this.h[0] + a) | 0\n this.h[1] = (this.h[1] + b) | 0\n this.h[2] = (this.h[2] + c) | 0\n this.h[3] = (this.h[3] + d) | 0\n this.h[4] = (this.h[4] + e) | 0\n }\n}\n\n/** One-shot convenience: SHA-1 of a single buffer. */\nexport function sha1(data: Uint8Array): Uint8Array {\n return new Sha1().update(data).digest()\n}\n"],"mappings":"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"}
|
package/dist/lib/sha256.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sha256.js","names":[],"sources":["../../src/lib/sha256.ts"],"sourcesContent":["/**\n * Minimal synchronous SHA-256 (FIPS 180-4) — no dependency, no WASM.\n *\n * WebCrypto's digest is async and one-shot (no streaming), which is awkward for the 7z key-derivation loop\n * that hashes salt‖password‖counter hundreds of thousands of times: a one-shot API would force a ~10 MB\n * throwaway buffer AND make the whole 7z decode path async. This incremental hasher lets {@link lib/sevenZip}\n * feed each round's bytes directly and stay synchronous. Not constant-time — fine for archive key derivation,\n * not for anything that must resist timing attacks.\n */\n\nconst K = /* @__PURE__ */ new Uint32Array([\n 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,\n 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,\n 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,\n 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,\n 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,\n 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,\n 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,\n 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,\n])\n\nexport class Sha256 {\n private readonly h = new Uint32Array([\n 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,\n ])\n private readonly block = new Uint8Array(64)\n private readonly w = new Uint32Array(64)\n private blen = 0\n private total = 0 // total bytes fed (for the length suffix)\n\n update(data: Uint8Array): this {\n this.total += data.length\n for (let i = 0; i < data.length; i++) {\n this.block[this.blen++] = data[i]\n if (this.blen === 64) {\n this.compress()\n this.blen = 0\n }\n }\n return this\n }\n\n digest(): Uint8Array {\n const bitLen = this.total * 8\n // Append 0x80, pad with zeros, then a 64-bit big-endian bit length.\n this.block[this.blen++] = 0x80\n if (this.blen > 56) {\n while (this.blen < 64) this.block[this.blen++] = 0\n this.compress()\n this.blen = 0\n }\n while (this.blen < 56) this.block[this.blen++] = 0\n // High 32 bits (bitLen can exceed 2^32; use division to stay exact).\n const hi = Math.floor(bitLen / 0x100000000)\n const lo = bitLen >>> 0\n this.block[56] = (hi >>> 24) & 0xff\n this.block[57] = (hi >>> 16) & 0xff\n this.block[58] = (hi >>> 8) & 0xff\n this.block[59] = hi & 0xff\n this.block[60] = (lo >>> 24) & 0xff\n this.block[61] = (lo >>> 16) & 0xff\n this.block[62] = (lo >>> 8) & 0xff\n this.block[63] = lo & 0xff\n this.compress()\n\n const out = new Uint8Array(32)\n for (let i = 0; i < 8; i++) {\n out[4 * i] = (this.h[i] >>> 24) & 0xff\n out[4 * i + 1] = (this.h[i] >>> 16) & 0xff\n out[4 * i + 2] = (this.h[i] >>> 8) & 0xff\n out[4 * i + 3] = this.h[i] & 0xff\n }\n return out\n }\n\n private compress(): void {\n const w = this.w\n const b = this.block\n for (let i = 0; i < 16; i++) {\n w[i] = (b[4 * i] << 24) | (b[4 * i + 1] << 16) | (b[4 * i + 2] << 8) | b[4 * i + 3]\n }\n for (let i = 16; i < 64; i++) {\n const x = w[i - 15]\n const y = w[i - 2]\n const s0 = ((x >>> 7) | (x << 25)) ^ ((x >>> 18) | (x << 14)) ^ (x >>> 3)\n const s1 = ((y >>> 17) | (y << 15)) ^ ((y >>> 19) | (y << 13)) ^ (y >>> 10)\n w[i] = (w[i - 16] + s0 + w[i - 7] + s1) | 0\n }\n let a = this.h[0]\n let bb = this.h[1]\n let c = this.h[2]\n let d = this.h[3]\n let e = this.h[4]\n let f = this.h[5]\n let g = this.h[6]\n let hh = this.h[7]\n for (let i = 0; i < 64; i++) {\n const S1 = ((e >>> 6) | (e << 26)) ^ ((e >>> 11) | (e << 21)) ^ ((e >>> 25) | (e << 7))\n const ch = (e & f) ^ (~e & g)\n const t1 = (hh + S1 + ch + K[i] + w[i]) | 0\n const S0 = ((a >>> 2) | (a << 30)) ^ ((a >>> 13) | (a << 19)) ^ ((a >>> 22) | (a << 10))\n const maj = (a & bb) ^ (a & c) ^ (bb & c)\n const t2 = (S0 + maj) | 0\n hh = g\n g = f\n f = e\n e = (d + t1) | 0\n d = c\n c = bb\n bb = a\n a = (t1 + t2) | 0\n }\n this.h[0] = (this.h[0] + a) | 0\n this.h[1] = (this.h[1] + bb) | 0\n this.h[2] = (this.h[2] + c) | 0\n this.h[3] = (this.h[3] + d) | 0\n this.h[4] = (this.h[4] + e) | 0\n this.h[5] = (this.h[5] + f) | 0\n this.h[6] = (this.h[6] + g) | 0\n this.h[7] = (this.h[7] + hh) | 0\n }\n}\n\n/** One-shot convenience: SHA-256 of a single buffer. */\nexport function sha256(data: Uint8Array): Uint8Array {\n return new Sha256().update(data).digest()\n}\n"],"mappings":"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"}
|
package/dist/lib/speechAudio.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"speechAudio.js","names":[],"sources":["../../src/lib/speechAudio.ts"],"sourcesContent":["/**\n * 语音编解码(3GPP AMR / 腾讯 SILK)→ WAV 的浏览器内解码器 —— 按需懒加载 WASM。\n *\n * 浏览器原生 <audio> 不认 3GPP AMR,也不认腾讯 SILK(微信 / QQ 语音;文件常见 `.silk` / `.slk`,也\n * 大量以 `.amr` 为扩展名而内容其实是 SILK)。两者都是 CELP 系语音编解码,无法手搓,故各引一个编译好的\n * WASM 解码器(与 lib/brotli、lib/sqlite 的 wasm 同思路:仅在真正播放这类文件时才 `import()`):\n * - AMR-NB(8kHz) / AMR-WB(16kHz):`@audio/decode-amr`(opencore-amr 编译;wasm 内联进 .cjs,无独立资源)\n * - SILK v3:`silk-wasm`(腾讯 SILK 编解码;独立 `silk.wasm`,Vite 经 `import.meta.url` 产出同源资源)\n *\n * 解出 PCM(s16le) 后用手写的 44 字节 PCM WAV 头包成 WAV,交给 <audio> 原生播放(同 QMC 解密后播放)。\n * 识别一律按**文件头魔数**而非扩展名 —— 微信 `.amr` 实为 SILK,故 `.amr` 既可能是真 AMR 也可能是 SILK。\n */\n\nexport type SpeechCodec = 'silk' | 'amr-nb' | 'amr-wb'\n\n// \"#!AMR\\n\" / \"#!AMR-WB\\n\"(RFC 4867 单声道存储头);\"#!SILK_V3\"(SILK 容器标记)。\nconst MAGIC_AMR_NB = [0x23, 0x21, 0x41, 0x4d, 0x52, 0x0a]\nconst MAGIC_AMR_WB = [0x23, 0x21, 0x41, 0x4d, 0x52, 0x2d, 0x57, 0x42, 0x0a]\nconst MAGIC_SILK = [0x23, 0x21, 0x53, 0x49, 0x4c, 0x4b, 0x5f, 0x56, 0x33]\n\nfunction matchAt(b: Uint8Array, off: number, magic: number[]): boolean {\n if (off + magic.length > b.length) return false\n for (let i = 0; i < magic.length; i++) if (b[off + i] !== magic[i]) return false\n return true\n}\n\n/**\n * 按文件头判别语音编解码;非语音编解码(普通 mp3/wav/… 或无法识别)返回 null。\n * SILK 容器有两种头:Skype 裸头 `#!SILK_V3`(偏移 0)与腾讯头 `\\x02#!SILK_V3`(偏移 1)。\n * AMR-WB 必须先于 AMR-NB 判断(`#!AMR` 是 `#!AMR-WB` 的前缀)。\n */\nexport function sniffSpeechCodec(bytes: Uint8Array): SpeechCodec | null {\n if (matchAt(bytes, 0, MAGIC_SILK) || matchAt(bytes, 1, MAGIC_SILK)) return 'silk'\n if (matchAt(bytes, 0, MAGIC_AMR_WB)) return 'amr-wb'\n if (matchAt(bytes, 0, MAGIC_AMR_NB)) return 'amr-nb'\n return null\n}\n\n// SILK 容器不在头部记采样率(SILK 本身与采样率解耦),业界解码器(silk-v3-decoder 等)一律按 24000Hz\n// 还原微信 / QQ 语音,这里沿用该约定。\nconst SILK_SAMPLE_RATE = 24000\n\nexport interface DecodedSpeech {\n /** 完整 WAV 字节(44 字节头 + PCM s16le)。 */\n wav: Uint8Array\n codec: SpeechCodec\n sampleRate: number\n channels: number\n durationSec: number\n}\n\nexport class SpeechDecodeError extends Error {}\n\n/** Float32(−1..1) → PCM s16le 字节。 */\nfunction floatToS16le(f: Float32Array): Uint8Array {\n const out = new Uint8Array(f.length * 2)\n const dv = new DataView(out.buffer)\n for (let i = 0; i < f.length; i++) {\n const s = Math.max(-1, Math.min(1, f[i]))\n dv.setInt16(i * 2, s < 0 ? s * 0x8000 : s * 0x7fff, true)\n }\n return out\n}\n\n/** 用 PCM s16le 数据包出一段标准 44 字节头的 WAV(单声道、16 位)。 */\nexport function wavFromPcmS16le(pcm: Uint8Array, sampleRate: number, channels = 1): Uint8Array {\n const blockAlign = channels * 2\n const out = new Uint8Array(44 + pcm.length)\n const dv = new DataView(out.buffer)\n const tag = (o: number, s: string): void => {\n for (let i = 0; i < s.length; i++) out[o + i] = s.charCodeAt(i)\n }\n tag(0, 'RIFF')\n dv.setUint32(4, 36 + pcm.length, true)\n tag(8, 'WAVE')\n tag(12, 'fmt ')\n dv.setUint32(16, 16, true) // fmt chunk 长度(PCM)\n dv.setUint16(20, 1, true) // 格式 = PCM\n dv.setUint16(22, channels, true)\n dv.setUint32(24, sampleRate, true)\n dv.setUint32(28, sampleRate * blockAlign, true) // 字节率\n dv.setUint16(32, blockAlign, true)\n dv.setUint16(34, 16, true) // 位深\n tag(36, 'data')\n dv.setUint32(40, pcm.length, true)\n out.set(pcm, 44)\n return out\n}\n\nasync function decodeSilk(bytes: Uint8Array): Promise<DecodedSpeech> {\n const { decode } = await import('silk-wasm')\n const { data, duration } = await decode(bytes, SILK_SAMPLE_RATE)\n if (!data.length) throw new SpeechDecodeError('SILK 解码结果为空')\n return {\n wav: wavFromPcmS16le(data, SILK_SAMPLE_RATE),\n codec: 'silk',\n sampleRate: SILK_SAMPLE_RATE,\n channels: 1,\n durationSec: duration / 1000,\n }\n}\n\nasync function decodeAmr(bytes: Uint8Array, codec: 'amr-nb' | 'amr-wb'): Promise<DecodedSpeech> {\n const { default: amrDecode } = await import('@audio/decode-amr')\n const { channelData, sampleRate } = await amrDecode(bytes)\n const mono = channelData[0]\n if (!mono || !mono.length) throw new SpeechDecodeError('AMR 解码结果为空')\n return {\n wav: wavFromPcmS16le(floatToS16le(mono), sampleRate),\n codec,\n sampleRate,\n channels: 1,\n durationSec: mono.length / sampleRate,\n }\n}\n\n/**\n * 把一段 AMR / SILK 语音字节解码成可播放的 WAV:先 {@link sniffSpeechCodec} 判型,再懒加载对应 WASM\n * 解码器解出 PCM 并包成 WAV。非 AMR/SILK 抛 {@link SpeechDecodeError}(调用方据此回退原生播放)。\n */\nexport async function decodeSpeechToWav(input: ArrayBuffer | Uint8Array): Promise<DecodedSpeech> {\n const bytes = input instanceof Uint8Array ? input : new Uint8Array(input)\n const codec = sniffSpeechCodec(bytes)\n if (!codec) throw new SpeechDecodeError('不是可识别的 AMR / SILK 语音文件')\n try {\n if (codec === 'silk') return await decodeSilk(bytes)\n return await decodeAmr(bytes, codec)\n } catch (e) {\n if (e instanceof SpeechDecodeError) throw e\n throw new SpeechDecodeError(e instanceof Error ? e.message : String(e))\n }\n}\n"],"mappings":"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"}
|
package/dist/lib/sqlite.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite.js","names":[],"sources":["../../src/lib/sqlite.ts"],"sourcesContent":["/**\n * SQLite 只读读取层 —— 基于 sql.js(WASM),不依赖 Vue / DOM。\n *\n * 移植自 sqlite-viewer 项目(D:\\web-work\\sqlite-viewer\\src\\db\\engine.ts),但**裁剪为只读预览**:仅\n * 保留「加载库 → 列出表/视图 → 按需分页读行 → 执行任意 SQL」这条链路,去掉建表/改表/编辑/事务等写操作\n * (文件查看器是只读的,且各组件都强制 attachment 下载、不回写)。\n *\n * sql.js 的 wasm 走 Vite `?url` 同源资源(与 lib/rar.ts 的 unrar.wasm 同款),不连任何 CDN;引擎实例\n * 全局复用一次初始化。本模块被 SqliteViewer.vue 动态 import,故 sql.js + wasm 仅在打开 .sqlite 时才下载。\n */\nimport initSqlJs from 'sql.js'\nimport type { Database, SqlJsStatic, SqlValue } from 'sql.js'\nimport wasmUrl from 'sql.js/dist/sql-wasm.wasm?url'\n\nlet sqlPromise: Promise<SqlJsStatic> | null = null\n\n/** 加载并初始化 sql.js(WASM)。多次调用复用同一实例。 */\nexport function initSqlEngine(): Promise<SqlJsStatic> {\n if (!sqlPromise) sqlPromise = initSqlJs({ locateFile: () => wasmUrl })\n return sqlPromise\n}\n\n/** 从 .sqlite / .db 文件字节加载一个库(一次性入内存,sql.js 全在内存运行)。 */\nexport function loadDatabase(SQL: SqlJsStatic, bytes: Uint8Array): Database {\n return new SQL.Database(bytes)\n}\n\nexport type CellValue = string | number | null\n\nexport interface Column {\n name: string\n type: string\n pk?: boolean\n fk?: boolean\n idx?: boolean\n notnull?: boolean\n}\n\n/** 一张表 / 视图的概要(不含行数据,行数据按需分页读取)。 */\nexport interface TableInfo {\n name: string\n kind: 'table' | 'view'\n rowCount: number\n columns: Column[]\n}\n\nexport interface QueryResult {\n columns: string[]\n rows: CellValue[][]\n rowCount: number\n elapsedMs: number\n error: string\n}\n\n/** 标识符加双引号转义,安全拼进 SQL(表名来自库自身,仍按规范转义)。 */\nfunction quoteIdent(name: string): string {\n return '\"' + name.replace(/\"/g, '\"\"') + '\"'\n}\n\n/** SqlValue → CellValue(BLOB 以占位文本展示,避免把二进制塞进表格)。 */\nfunction toCell(v: SqlValue): CellValue {\n if (v == null) return null\n if (v instanceof Uint8Array) return `[BLOB ${v.length}B]`\n return v\n}\n\n/** 读出某表/视图的列结构(含 pk / fk / idx / notnull 徽标信息)。 */\nexport function readColumns(db: Database, table: string): Column[] {\n const qi = quoteIdent(table)\n\n // 外键列\n const fkCols = new Set<string>()\n const fkRes = db.exec(`PRAGMA foreign_key_list(${qi})`)\n if (fkRes.length) {\n const fromIdx = fkRes[0].columns.indexOf('from')\n for (const r of fkRes[0].values) fkCols.add(String(r[fromIdx]))\n }\n\n // 非主键索引涉及的列(用于列徽标)\n const idxCols = new Set<string>()\n const idxList = db.exec(`PRAGMA index_list(${qi})`)\n if (idxList.length) {\n const ic = idxList[0].columns\n const nameIdx = ic.indexOf('name')\n const originIdx = ic.indexOf('origin')\n for (const r of idxList[0].values) {\n const iname = String(r[nameIdx])\n const origin = originIdx >= 0 ? String(r[originIdx]) : 'c'\n if (origin === 'pk') continue\n const infoRes = db.exec(`PRAGMA index_info(${quoteIdent(iname)})`)\n if (infoRes.length) {\n const colIdx = infoRes[0].columns.indexOf('name')\n for (const ir of infoRes[0].values) {\n if (ir[colIdx] != null) idxCols.add(String(ir[colIdx]))\n }\n }\n }\n }\n\n const columns: Column[] = []\n const info = db.exec(`PRAGMA table_info(${qi})`)\n if (info.length) {\n const c = info[0].columns\n const nameI = c.indexOf('name')\n const typeI = c.indexOf('type')\n const pkI = c.indexOf('pk')\n const nnI = c.indexOf('notnull')\n for (const r of info[0].values) {\n const col: Column = { name: String(r[nameI]), type: String(r[typeI] ?? '') }\n if (Number(r[pkI]) > 0) col.pk = true\n if (nnI >= 0 && Number(r[nnI]) === 1) col.notnull = true\n if (fkCols.has(col.name)) col.fk = true\n if (idxCols.has(col.name)) col.idx = true\n columns.push(col)\n }\n }\n return columns\n}\n\n/** 表/视图行数(视图可能为表达式,失败回退 0)。 */\nexport function rowCount(db: Database, table: string): number {\n try {\n const res = db.exec(`SELECT COUNT(*) FROM ${quoteIdent(table)}`)\n return res.length && res[0].values.length ? Number(res[0].values[0][0]) : 0\n } catch {\n return 0\n }\n}\n\n/** 列出所有用户表与视图(含列结构与行数),按名称排序;排除 sqlite_ 内部表。 */\nexport function listTables(db: Database): TableInfo[] {\n const res = db.exec(\n \"SELECT name, type FROM sqlite_master WHERE type IN ('table','view') AND name NOT LIKE 'sqlite_%' ORDER BY type, name\",\n )\n if (!res.length) return []\n const out: TableInfo[] = []\n for (const r of res[0].values) {\n const name = String(r[0])\n const kind = String(r[1]) === 'view' ? 'view' : 'table'\n out.push({ name, kind, rowCount: rowCount(db, name), columns: readColumns(db, name) })\n }\n return out\n}\n\n/** 按页读取某表/视图的行(LIMIT/OFFSET)。仅取数据,不带列结构(列结构走 readColumns)。 */\nexport function readPage(\n db: Database,\n table: string,\n limit: number,\n offset: number,\n): CellValue[][] {\n const res = db.exec(`SELECT * FROM ${quoteIdent(table)} LIMIT ${limit} OFFSET ${offset}`)\n return res.length ? res[0].values.map((r) => r.map(toCell)) : []\n}\n\n/** 执行任意 SQL,返回最后一个结果集;出错时 error 非空(只读预览,不强制只读但不暴露写 UI)。 */\nexport function execSql(db: Database, sql: string): QueryResult {\n const start = performance.now()\n try {\n const res = db.exec(sql)\n const elapsed = performance.now() - start\n const last = res.length ? res[res.length - 1] : null\n if (last) {\n return {\n columns: last.columns,\n rows: last.values.map((r) => r.map(toCell)),\n rowCount: last.values.length,\n elapsedMs: elapsed,\n error: '',\n }\n }\n return { columns: [], rows: [], rowCount: 0, elapsedMs: elapsed, error: '' }\n } catch (e) {\n return {\n columns: [],\n rows: [],\n rowCount: 0,\n elapsedMs: performance.now() - start,\n error: e instanceof Error ? e.message : String(e),\n }\n }\n}\n"],"mappings":"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"}
|
package/dist/lib/subtitle.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subtitle.js","names":[],"sources":["../../src/lib/subtitle.ts"],"sourcesContent":["/**\n * Hand-rolled, zero-dependency subtitle parser for the side viewer.\n *\n * Parses the three common caption formats into a uniform cue list:\n * - SubRip (.srt): block = optional index line, `HH:MM:SS,mmm --> HH:MM:SS,mmm`, then text lines\n * - WebVTT (.vtt): a `WEBVTT` header, optional cue ids / NOTE / STYLE / REGION blocks, and\n * `HH:MM:SS.mmm --> HH:MM:SS.mmm <settings>` timing lines; inline tags stripped\n * - SubStation Alpha (.ass/.ssa): an `[Events]` section with a `Format:` line + `Dialogue:` rows;\n * override tags `{\\…}` and the `\\N` / `\\h` escapes are handled\n *\n * The viewer only needs start/end/text (plus an optional speaker from the ASS Name field), so styling,\n * positioning, karaoke timing, etc. are intentionally discarded — this is a readable transcript, not a\n * pixel-accurate renderer. Times are normalized to milliseconds.\n */\n\nexport interface SubtitleCue {\n start: number // ms\n end: number // ms\n text: string // plain text, lines joined with '\\n', markup stripped\n speaker?: string // ASS \"Name\"/actor field, when present\n}\n\nexport interface ParsedSubtitle {\n format: 'srt' | 'vtt' | 'ass'\n cues: SubtitleCue[]\n}\n\n/** A clock anywhere in a string: optional hours, then MM:SS, then `.`/`,` and 1–3 fractional digits. */\nconst CLOCK_RE = /(?:(\\d+):)?(\\d{1,2}):(\\d{1,2})[.,](\\d{1,3})/\n\n// Extract the first clock found in `s` (so trailing VTT cue settings / SRT coords are ignored) → ms.\n// ASS centiseconds (`.cc`) round-trip correctly: `.05` → \"050\" → 50ms, `.5` → \"500\" → 500ms.\nfunction extractClock(s: string): number | null {\n const m = s.match(CLOCK_RE)\n if (!m) return null\n const h = m[1] ? parseInt(m[1], 10) : 0\n const min = parseInt(m[2], 10)\n const sec = parseInt(m[3], 10)\n const frac = parseInt(m[4].padEnd(3, '0').slice(0, 3), 10)\n return ((h * 60 + min) * 60 + sec) * 1000 + frac\n}\n\n// Strip WebVTT / SubRip inline markup: HTML-ish tags (<b>, <i>, <c.color>, <v Speaker>) and the\n// `<00:00:01.000>` karaoke timestamps. Leaves the readable text.\nfunction stripCueMarkup(s: string): string {\n return s\n .replace(/<[^>]*>/g, '')\n .replace(/\\{\\\\[^}]*\\}/g, '')\n .replace(/[ \\t]+\\n/g, '\\n')\n .trim()\n}\n\nfunction parseSrtVtt(text: string, vtt: boolean): SubtitleCue[] {\n const cues: SubtitleCue[] = []\n for (const block of text.split(/\\n{2,}/)) {\n // VTT preamble / metadata blocks never carry a cue.\n if (vtt && /^\\s*(WEBVTT|NOTE|STYLE|REGION)\\b/.test(block)) continue\n const lines = block.split('\\n')\n const tIdx = lines.findIndex((l) => l.includes('-->'))\n if (tIdx < 0) continue\n const [left, right = ''] = lines[tIdx].split('-->')\n const start = extractClock(left)\n const end = extractClock(right)\n if (start === null || end === null) continue\n // Lines before the timing line are an optional index (SRT) or cue id (VTT) — dropped.\n const body = lines\n .slice(tIdx + 1)\n .join('\\n')\n .trim()\n cues.push({ start, end, text: stripCueMarkup(body) })\n }\n return cues\n}\n\n// Standard ASS/SSA Events field order, used when a `[Events]` section omits its `Format:` line.\nconst DEFAULT_ASS_FMT = [\n 'layer',\n 'start',\n 'end',\n 'style',\n 'name',\n 'marginl',\n 'marginr',\n 'marginv',\n 'effect',\n 'text',\n]\n\n// The ASS Text field is always last and may itself contain commas, so split into exactly `count`\n// fields — the first count-1 on commas, the remainder (text) kept whole.\nfunction splitAssFields(rest: string, count: number): string[] {\n if (count <= 1) return [rest]\n const out: string[] = []\n let i = 0\n for (let f = 0; f < count - 1; f++) {\n const c = rest.indexOf(',', i)\n if (c < 0) break\n out.push(rest.slice(i, c))\n i = c + 1\n }\n out.push(rest.slice(i))\n return out\n}\n\n// Drop override blocks `{\\…}`, turn `\\N`/`\\n` (hard breaks) into newlines and `\\h` into a space.\nfunction cleanAssText(s: string): string {\n return s\n .replace(/\\{[^}]*\\}/g, '')\n .replace(/\\\\N/gi, '\\n')\n .replace(/\\\\h/gi, ' ')\n .trim()\n}\n\nfunction parseAss(text: string): SubtitleCue[] {\n const cues: SubtitleCue[] = []\n let inEvents = false\n let fmt: string[] = []\n for (const raw of text.split('\\n')) {\n const line = raw.trim()\n if (line.startsWith('[')) {\n inEvents = /^\\[events\\]/i.test(line)\n continue\n }\n if (!inEvents || !line) continue\n if (/^format\\s*:/i.test(line)) {\n fmt = line\n .slice(line.indexOf(':') + 1)\n .split(',')\n .map((s) => s.trim().toLowerCase())\n continue\n }\n if (/^dialogue\\s*:/i.test(line)) {\n const order = fmt.length ? fmt : DEFAULT_ASS_FMT\n const fields = splitAssFields(line.slice(line.indexOf(':') + 1), order.length)\n const get = (key: string): string => {\n const i = order.indexOf(key)\n return i >= 0 ? (fields[i] ?? '') : ''\n }\n const start = extractClock(get('start'))\n const end = extractClock(get('end'))\n if (start === null || end === null) continue\n const speaker = get('name').trim()\n cues.push({\n start,\n end,\n text: cleanAssText(get('text') || fields[fields.length - 1] || ''),\n speaker: speaker || undefined,\n })\n }\n }\n return cues\n}\n\n/** Parse subtitle source text into a sorted cue list. `ext` (when known) disambiguates the format. */\nexport function parseSubtitle(input: string, ext?: string): ParsedSubtitle {\n // Strip a leading UTF-8 BOM (U+FEFF) and normalize CRLF/CR line endings.\n const text = (input.charCodeAt(0) === 0xfeff ? input.slice(1) : input).replace(/\\r\\n?/g, '\\n')\n const e = (ext || '').toLowerCase()\n let result: ParsedSubtitle\n if (e === 'ass' || e === 'ssa' || /^\\s*\\[script info\\]/i.test(text)) {\n result = { format: 'ass', cues: parseAss(text) }\n } else if (e === 'vtt' || /^\\s*WEBVTT/.test(text)) {\n result = { format: 'vtt', cues: parseSrtVtt(text, true) }\n } else {\n result = { format: 'srt', cues: parseSrtVtt(text, false) }\n }\n result.cues.sort((a, b) => a.start - b.start || a.end - b.end)\n return result\n}\n"],"mappings":"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"}
|
package/dist/lib/tarball.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tarball.js","names":[],"sources":["../../src/lib/tarball.ts"],"sourcesContent":["/**\n * In-browser reader for the tar family — `.tar` and its compressed wrappers `.gz`/`.tgz`, `.xz`/`.txz`,\n * `.bz2`/`.tbz2`, `.br`/`.tar.br` — plus a lone compressed file (`foo.json.gz`). No dependency beyond the\n * browser's native gzip; xz reuses the project's hand-rolled LZMA2 decoder ({@link lib/lzma}), bzip2 its BWT\n * decoder ({@link lib/bzip2}), and Brotli a lazily-loaded WASM decoder ({@link lib/brotli}).\n *\n * Unlike ZIP/7z (random-access central directories), a tar is solid: the whole stream is decompressed to\n * memory once, then tar headers are walked to index each file as an (offset, size) slice into that buffer —\n * so extraction is a cheap, synchronous sub-array copy. The result is exposed through the shared\n * {@link OpenedArchive} interface so {@link components/ArchiveBrowser} can browse it like any other archive.\n *\n * If the decompressed bytes aren't a tar (a bare `foo.txt.gz`), the archive is presented as a single entry\n * named by stripping the compression suffix.\n */\n\nimport { bcjX86Decode, lzma2Decode } from '@/lib/lzma'\nimport { bzip2Decode } from '@/lib/bzip2'\nimport { decodeFileNameBytes } from '@/lib/encoding'\nimport type { ArchiveEntry, OpenedArchive } from '@/lib/archive'\n\nconst GZIP_MAGIC = [0x1f, 0x8b]\nconst XZ_MAGIC = [0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00]\nconst BZIP2_MAGIC = [0x42, 0x5a, 0x68] // \"BZh\"\n\nfunction hasMagic(b: Uint8Array, magic: number[], off = 0): boolean {\n if (b.length < off + magic.length) return false\n return magic.every((m, i) => b[off + i] === m)\n}\n\n// ── gzip (browser-native) ───────────────────────────────────────────────────────────────────────\n\nasync function gunzip(data: Uint8Array): Promise<Uint8Array> {\n if (typeof DecompressionStream === 'undefined') {\n throw new Error('当前浏览器不支持解压 gzip(缺少 DecompressionStream)')\n }\n const stream = new Blob([data]).stream().pipeThrough(new DecompressionStream('gzip'))\n return new Uint8Array(await new Response(stream).arrayBuffer())\n}\n\n// ── xz (LZMA2 in the xz container) ────────────────────────────────────────────────────────────────\n\n// Check sizes per the stream-flags check id (0/4/8/16/32/64). None=0, CRC32=1, CRC64=4, SHA256=10.\nconst XZ_CHECK_SIZE = [0, 4, 4, 4, 8, 8, 8, 16, 16, 16, 32, 32, 32, 64, 64, 64]\n\n/** Read an xz/LZMA2 variable-length integer (base-128 little-endian). */\nfunction readVli(b: Uint8Array, pos: number): { value: number; next: number } {\n let value = 0\n let shift = 0\n let i = pos\n for (;;) {\n const byte = b[i++]\n value += (byte & 0x7f) * 2 ** shift\n if ((byte & 0x80) === 0) break\n shift += 7\n if (shift > 63) throw new Error('xz VLI 过长')\n }\n return { value, next: i }\n}\n\n/**\n * Walk an LZMA2 chunk stream (without decoding) to find where it ends (its 0x00 terminator) and sum the\n * uncompressed size — xz block headers usually omit both, but the chunk framing carries them. Mirrors the\n * framing in {@link lzma2Decode}.\n */\nfunction scanLzma2(b: Uint8Array, start: number): { compLen: number; uncompSize: number } {\n let ip = start\n let uncomp = 0\n for (;;) {\n const control = b[ip++]\n if (control === 0) break\n if (control >= 0x80) {\n const unpackSize = ((control & 0x1f) << 16) + (b[ip] << 8) + b[ip + 1] + 1\n ip += 2\n const packSize = (b[ip] << 8) + b[ip + 1] + 1\n ip += 2\n if (((control >> 5) & 3) >= 2) ip++ // props byte\n ip += packSize\n uncomp += unpackSize\n } else {\n if (control > 2) throw new Error(`xz LZMA2 非法控制字节 0x${control.toString(16)}`)\n const size = (b[ip] << 8) + b[ip + 1] + 1\n ip += 2\n ip += size\n uncomp += size\n }\n }\n return { compLen: ip - start, uncompSize: uncomp }\n}\n\nfunction xzDecode(data: Uint8Array): Uint8Array {\n if (!hasMagic(data, XZ_MAGIC)) throw new Error('不是有效的 xz 文件')\n const checkSize = XZ_CHECK_SIZE[data[7] & 0x0f]\n const chunks: Uint8Array[] = []\n let pos = 12 // past the 12-byte stream header\n while (pos < data.length) {\n const hsByte = data[pos]\n if (hsByte === 0x00) break // index indicator — blocks are done\n const headerSize = (hsByte + 1) * 4\n const flags = data[pos + 1]\n const numFilters = (flags & 0x03) + 1\n let c = pos + 2\n if (flags & 0x40) c = readVli(data, c).next // compressed size present (unused — scan finds it)\n let declaredUncomp = -1\n if (flags & 0x80) {\n const r = readVli(data, c)\n declaredUncomp = r.value\n c = r.next\n }\n let lzma2Props = new Uint8Array(0)\n let useBcj = false\n for (let f = 0; f < numFilters; f++) {\n const id = readVli(data, c)\n c = id.next\n const ps = readVli(data, c)\n c = ps.next\n const props = data.subarray(c, c + ps.value)\n c += ps.value\n if (id.value === 0x21) lzma2Props = props.slice()\n else if (id.value === 0x04)\n useBcj = true // x86 BCJ\n else throw new Error(`xz 不支持的过滤器 0x${id.value.toString(16)}`)\n }\n const dataStart = pos + headerSize\n const { compLen, uncompSize } = scanLzma2(data, dataStart)\n const outSize = declaredUncomp >= 0 ? declaredUncomp : uncompSize\n const out = lzma2Decode(data.subarray(dataStart, dataStart + compLen), lzma2Props, outSize)\n if (useBcj) bcjX86Decode(out)\n chunks.push(out)\n let next = dataStart + compLen\n next += (4 - (compLen % 4)) % 4 // block padding aligns compressed data to 4 bytes\n next += checkSize\n pos = next\n }\n let total = 0\n for (const c of chunks) total += c.length\n const out = new Uint8Array(total)\n let off = 0\n for (const c of chunks) {\n out.set(c, off)\n off += c.length\n }\n return out\n}\n\n// ── tar ───────────────────────────────────────────────────────────────────────────────────────────\n\ninterface TarItem {\n path: string\n name: string\n size: number\n isDir: boolean\n /** Byte offset of this file's data within the decompressed tar buffer. */\n offset: number\n}\n\nconst td = new TextDecoder('utf-8')\n\n/** Read a NUL-terminated field from a 512-byte tar header block. */\nfunction field(block: Uint8Array, off: number, len: number): string {\n let end = off\n while (end < off + len && block[end] !== 0) end++\n return td.decode(block.subarray(off, end))\n}\n\n/** Like {@link field}, but for a name field — sniffs the encoding (UTF-8 → GBK → \\uXXXX) instead of forcing UTF-8. */\nfunction nameField(block: Uint8Array, off: number, len: number): string {\n let end = off\n while (end < off + len && block[end] !== 0) end++\n return decodeFileNameBytes(block.subarray(off, end))\n}\n\n/** Parse a tar numeric field — octal, or GNU base-256 when the high bit of the first byte is set. */\nfunction numField(block: Uint8Array, off: number, len: number): number {\n if (block[off] & 0x80) {\n let v = block[off] & 0x7f\n for (let i = 1; i < len; i++) v = v * 256 + block[off + i]\n return v\n }\n const s = field(block, off, len).trim()\n return s ? parseInt(s, 8) || 0 : 0\n}\n\n/** Extract `path=` / `size=` overrides from a pax extended header's \"<len> key=value\\n\" records. */\nfunction parsePax(text: string): { path?: string; size?: number } {\n const out: { path?: string; size?: number } = {}\n let i = 0\n while (i < text.length) {\n const sp = text.indexOf(' ', i)\n if (sp < 0) break\n const len = parseInt(text.slice(i, sp), 10)\n if (!len || len <= 0) break\n const record = text.slice(sp + 1, i + len - 1) // drop the trailing newline\n const eq = record.indexOf('=')\n if (eq > 0) {\n const key = record.slice(0, eq)\n const val = record.slice(eq + 1)\n if (key === 'path') out.path = val\n else if (key === 'size') out.size = parseInt(val, 10) || 0\n }\n i += len\n }\n return out\n}\n\nconst ceil512 = (n: number): number => (n + 511) & ~511\n\n/** Walk a decompressed tar buffer into a flat file list. Handles GNU long names ('L') and pax ('x') headers. */\nfunction parseTar(data: Uint8Array): TarItem[] {\n const items: TarItem[] = []\n let off = 0\n let longName = '' // pending GNU 'L' override for the next entry\n let pax: { path?: string; size?: number } = {} // pending pax override for the next entry\n while (off + 512 <= data.length) {\n const block = data.subarray(off, off + 512)\n // An all-zero block marks end-of-archive.\n let allZero = true\n for (let i = 0; i < 512; i++) {\n if (block[i] !== 0) {\n allZero = false\n break\n }\n }\n if (allZero) break\n\n const name = nameField(block, 0, 100)\n const prefix = nameField(block, 345, 155)\n const type = String.fromCharCode(block[156])\n let size = numField(block, 124, 12)\n const dataOff = off + 512\n\n if (type === 'L') {\n // GNU long name: the real name is this entry's payload.\n longName = nameField(data.subarray(dataOff, dataOff + size), 0, size)\n off = dataOff + ceil512(size)\n continue\n }\n if (type === 'K') {\n // GNU long link target — irrelevant to browsing; skip its payload.\n off = dataOff + ceil512(size)\n continue\n }\n if (type === 'x' || type === 'g') {\n // pax extended header: parse path/size for the following entry (g = global, applied likewise).\n pax = parsePax(td.decode(data.subarray(dataOff, dataOff + size)))\n off = dataOff + ceil512(size)\n continue\n }\n\n let full = longName || pax.path || (prefix ? `${prefix}/${name}` : name)\n if (pax.size !== undefined) size = pax.size\n longName = ''\n pax = {}\n\n const isDir = type === '5' || full.endsWith('/')\n full = full.replace(/\\/+$/, '')\n if (full) {\n items.push({\n path: full,\n name: full.split('/').pop() ?? full,\n size: isDir ? 0 : size,\n isDir,\n offset: dataOff,\n })\n }\n off = dataOff + (isDir ? 0 : ceil512(size))\n }\n return items\n}\n\n// ── format dispatch ─────────────────────────────────────────────────────────────────────────────\n\n/** A decompressed buffer is a tar if it carries the ustar magic at offset 257. */\nfunction isTar(data: Uint8Array): boolean {\n return data.length >= 512 && field(data.subarray(257, 263), 0, 5) === 'ustar'\n}\n\n/** Strip a compression suffix to name the single inner file of a non-tar `.gz`/`.xz`/`.bz2`. */\nfunction strippedName(name: string): string {\n const lower = name.toLowerCase()\n for (const [suf, rep] of [\n ['.tar.gz', '.tar'],\n ['.tar.xz', '.tar'],\n ['.tar.bz2', '.tar'],\n ['.tgz', '.tar'],\n ['.txz', '.tar'],\n ['.tbz2', '.tar'],\n ['.tbz', '.tar'],\n ['.tar.br', '.tar'],\n ['.tbr', '.tar'],\n ] as const) {\n if (lower.endsWith(suf)) return name.slice(0, -suf.length) + rep\n }\n for (const suf of ['.gz', '.xz', '.bz2', '.bz', '.br']) {\n if (lower.endsWith(suf)) return name.slice(0, -suf.length)\n }\n return `${name}.out`\n}\n\n/**\n * Open a tar-family archive: decompress the outer wrapper (gzip/xz/bzip2, by magic) if present, then either\n * index it as a tar or, failing that, surface the single decompressed file.\n */\nexport async function openTarball(buf: ArrayBuffer, name: string): Promise<OpenedArchive> {\n let data: Uint8Array = new Uint8Array(buf)\n if (hasMagic(data, GZIP_MAGIC)) data = await gunzip(data)\n else if (hasMagic(data, XZ_MAGIC)) data = xzDecode(data)\n else if (hasMagic(data, BZIP2_MAGIC)) data = bzip2Decode(data)\n else if (!isTar(data)) {\n // Brotli streams carry NO magic bytes. Anything routed here that isn't gzip/xz/bzip2 and isn't already a\n // bare tar (the only other no-magic tar-family member) is treated as Brotli (.br / .tar.br). The WASM\n // decoder is dynamic-imported so its ~250KB wasm loads only when a Brotli file is actually opened.\n const { brotliDecode } = await import('@/lib/brotli')\n data = await brotliDecode(data)\n }\n\n if (isTar(data)) {\n const items = parseTar(data)\n const entries: ArchiveEntry[] = items.map((it, i) => ({\n path: it.path,\n name: it.name,\n size: it.size,\n isDir: it.isDir,\n ref: i,\n }))\n const bytes = data\n return {\n entries,\n extract(e) {\n const it = items[e.ref]\n if (!it || it.isDir) return new Uint8Array(0)\n return bytes.subarray(it.offset, it.offset + it.size).slice()\n },\n }\n }\n\n // Not a tar — a lone compressed file. Present it as one entry.\n const inner = strippedName(name)\n const single = data\n return {\n entries: [{ path: inner, name: inner, size: single.length, isDir: false, ref: 0 }],\n extract() {\n return single\n },\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"viewerFormats.js","names":[],"sources":["../../src/lib/viewerFormats.ts"],"sourcesContent":["/**\n * The \"open with\" formatter set — the universal escape hatch for the side viewer.\n *\n * Auto-detection ({@link classifyViewerFile}) routes a file to a specialized viewer by extension/mime, but that\n * guess can be wrong (a mislabeled file, a `.voice` blob that's really SILK, bytes worth eyeballing as hex).\n * Each formatter here REINTERPRETS the same name/url/size/content as a different {@link ViewerFile}, so it\n * applies to ANY file regardless of what detection picked. The list is shared by:\n * - {@link FileViewerPanel} — renders the header \"打开方式\" menu (available for every file), and\n * - {@link FileViewerBody} — applies the choice and also offers the same chips on its unpreviewable-binary\n * card (the archive-browser context, where there is no host header).\n *\n * These are deliberately the GENERIC reinterpretations that work on raw bytes — hex / text / markdown / html /\n * svg / image / audio / video. The heavy structured viewers (docx / xlsx / zip / …) stay auto-detected only:\n * forcing arbitrary bytes through them just errors, so they'd be noise in a \"force open as\" list.\n */\nimport type { StrokeIconName } from '@/components/icons/iconPaths'\nimport type { ViewerFile } from '@/composables/useFileViewer'\n\nexport interface ViewerFormatter {\n /** Stable id (used for the active-selection highlight and the body↔header sync). */\n key: string\n label: string\n icon: StrokeIconName\n /** Merged onto the base file to force this interpretation; `lang` is always set (it drives the source view). */\n patch: Partial<ViewerFile> & { lang: string }\n}\n\nexport const VIEWER_FORMATTERS: ViewerFormatter[] = [\n { key: 'hex', label: '十六进制', icon: 'hex', patch: { lang: '', hex: true } },\n { key: 'text', label: '文本 / 源码', icon: 'code', patch: { lang: '' } },\n { key: 'markdown', label: 'Markdown', icon: 'fileOutline', patch: { lang: 'markdown' } },\n { key: 'html', label: 'HTML', icon: 'globe', patch: { lang: 'xml', html: true } },\n { key: 'svg', label: 'SVG', icon: 'image', patch: { lang: 'xml', svg: true } },\n { key: 'image', label: '图片', icon: 'image', patch: { lang: '', image: true } },\n { key: 'audio', label: '音频', icon: 'music', patch: { lang: '', audio: true } },\n { key: 'video', label: '视频', icon: 'play', patch: { lang: '', video: true } },\n]\n\n/** Look a formatter up by its {@link ViewerFormatter.key}; undefined for an unknown key. */\nexport function findFormatter(key: string): ViewerFormatter | undefined {\n return VIEWER_FORMATTERS.find((f) => f.key === key)\n}\n\n/**\n * Build the EFFECTIVE file for a chosen formatter: same name/url/size/content as the original, but only the\n * formatter's flags (no carried-over detection flags), so the body's dispatch re-runs cleanly as that format.\n */\nexport function applyFormatter(file: ViewerFile, f: ViewerFormatter): ViewerFile {\n return { name: file.name, url: file.url, size: file.size, content: file.content, ...f.patch }\n}\n"],"mappings":";AA2BA,IAAa,IAAuC;CAClD;EAAE,KAAK;EAAO,OAAO;EAAQ,MAAM;EAAO,OAAO;GAAE,MAAM;GAAI,KAAK;EAAK;CAAE;CACzE;EAAE,KAAK;EAAQ,OAAO;EAAW,MAAM;EAAQ,OAAO,EAAE,MAAM,GAAG;CAAE;CACnE;EAAE,KAAK;EAAY,OAAO;EAAY,MAAM;EAAe,OAAO,EAAE,MAAM,WAAW;CAAE;CACvF;EAAE,KAAK;EAAQ,OAAO;EAAQ,MAAM;EAAS,OAAO;GAAE,MAAM;GAAO,MAAM;EAAK;CAAE;CAChF;EAAE,KAAK;EAAO,OAAO;EAAO,MAAM;EAAS,OAAO;GAAE,MAAM;GAAO,KAAK;EAAK;CAAE;CAC7E;EAAE,KAAK;EAAS,OAAO;EAAM,MAAM;EAAS,OAAO;GAAE,MAAM;GAAI,OAAO;EAAK;CAAE;CAC7E;EAAE,KAAK;EAAS,OAAO;EAAM,MAAM;EAAS,OAAO;GAAE,MAAM;GAAI,OAAO;EAAK;CAAE;CAC7E;EAAE,KAAK;EAAS,OAAO;EAAM,MAAM;EAAQ,OAAO;GAAE,MAAM;GAAI,OAAO;EAAK;CAAE;AAC9E;AAGA,SAAgB,EAAc,GAA0C;CACtE,OAAO,EAAkB,MAAM,MAAM,EAAE,QAAQ,CAAG;AACpD;AAMA,SAAgB,EAAe,GAAkB,GAAgC;CAC/E,OAAO;EAAE,MAAM,EAAK;EAAM,KAAK,EAAK;EAAK,MAAM,EAAK;EAAM,SAAS,EAAK;EAAS,GAAG,EAAE;CAAM;AAC9F"}
|