@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":"PdfNavigationPane.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../src/components/PdfNavigationPane.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, nextTick, shallowRef, useTemplateRef, watch } from 'vue'\n\nimport Icon from '@/components/icons/Icon.vue'\nimport type { PdfNavigationItem } from '../lib/pdfNavigation'\n\nconst props = defineProps<{\n outlineItems: readonly PdfNavigationItem[]\n pageCount: number\n pageLabels: readonly string[]\n pageThumbnails: readonly (string | null)[]\n activePage: number\n}>()\n\nconst collapsed = defineModel<boolean>('collapsed', { default: false })\nconst view = defineModel<'outline' | 'pages'>('view', { default: 'outline' })\nconst emit = defineEmits<{\n navigate: [pageNumber: number]\n}>()\n\nconst listEl = useTemplateRef<HTMLElement>('listEl')\nconst autoScrollPending = shallowRef(false)\nconst hasOutline = computed(() => props.outlineItems.length > 0)\nconst hasThumbnails = computed(() => props.pageThumbnails.some(Boolean))\nconst panelClasses = computed(() => ({\n 'pn--collapsed': collapsed.value,\n 'pn--outline': hasOutline.value && view.value === 'outline',\n 'pn--compact': view.value === 'pages' && !hasThumbnails.value,\n}))\n\nconst activeOutlineId = computed(() => {\n let active: PdfNavigationItem | undefined\n for (const item of props.outlineItems) {\n if (item.pageNumber === null || item.pageNumber > props.activePage) continue\n const activePageNumber = active?.pageNumber ?? 0\n if (\n !active ||\n item.pageNumber > activePageNumber ||\n (item.pageNumber === activePageNumber && item.level > active.level)\n )\n active = item\n }\n return active?.id ?? ''\n})\n\nfunction pageLabel(pageNumber: number): string {\n return props.pageLabels[pageNumber - 1] || `第 ${pageNumber} 页`\n}\n\nfunction navigate(pageNumber: number | null): void {\n if (pageNumber !== null) emit('navigate', pageNumber)\n}\n\nasync function keepActiveItemVisible(): Promise<void> {\n if (collapsed.value || autoScrollPending.value) return\n autoScrollPending.value = true\n await nextTick()\n listEl.value\n ?.querySelector<HTMLElement>('.pn__item--active')\n ?.scrollIntoView({ block: 'nearest' })\n autoScrollPending.value = false\n}\n\nwatch(\n () => props.outlineItems.length,\n (count) => {\n if (!count && view.value === 'outline') view.value = 'pages'\n },\n { immediate: true },\n)\nwatch(() => [props.activePage, view.value, collapsed.value], keepActiveItemVisible)\n</script>\n\n<template>\n <aside class=\"pn\" :class=\"panelClasses\" aria-label=\"PDF 导航\">\n <button\n v-if=\"collapsed\"\n type=\"button\"\n class=\"pn__reopen\"\n aria-label=\"展开 PDF 导航\"\n title=\"展开 PDF 导航\"\n @click=\"collapsed = false\"\n >\n <Icon name=\"chevronRight\" :size=\"15\" />\n </button>\n\n <template v-else>\n <header class=\"pn__header\">\n <div v-if=\"hasOutline\" class=\"pn__tabs\" role=\"tablist\" aria-label=\"导航类型\">\n <button\n class=\"pn__tab\"\n :class=\"{ 'pn__tab--active': view === 'outline' }\"\n type=\"button\"\n role=\"tab\"\n :aria-selected=\"view === 'outline'\"\n @click=\"view = 'outline'\"\n >\n 目录\n </button>\n <button\n class=\"pn__tab\"\n :class=\"{ 'pn__tab--active': view === 'pages' }\"\n type=\"button\"\n role=\"tab\"\n :aria-selected=\"view === 'pages'\"\n @click=\"view = 'pages'\"\n >\n 页面\n </button>\n </div>\n <div v-else class=\"pn__meta\">\n <strong class=\"pn__title\">页面</strong>\n <span class=\"pn__count\">{{ pageCount }} 页</span>\n </div>\n <button\n class=\"pn__collapse\"\n type=\"button\"\n title=\"收起 PDF 导航\"\n aria-label=\"收起 PDF 导航\"\n @click=\"collapsed = true\"\n >\n <Icon name=\"chevronLeft\" :size=\"15\" />\n </button>\n </header>\n\n <nav ref=\"listEl\" class=\"pn__list\" aria-label=\"PDF 导航列表\">\n <template v-if=\"view === 'outline' && hasOutline\">\n <button\n v-for=\"item in outlineItems\"\n :key=\"item.id\"\n class=\"pn__item pn__outline\"\n :class=\"{\n 'pn__item--active': item.id === activeOutlineId,\n 'pn__item--disabled': item.pageNumber === null,\n }\"\n :style=\"{ '--pn-level': item.level }\"\n type=\"button\"\n :disabled=\"item.pageNumber === null\"\n :title=\"item.title\"\n :aria-current=\"item.id === activeOutlineId ? 'location' : undefined\"\n @click=\"navigate(item.pageNumber)\"\n >\n <span class=\"pn__outline-title\">{{ item.title }}</span>\n <span v-if=\"item.pageNumber !== null\" class=\"pn__outline-page\">{{\n item.pageNumber\n }}</span>\n </button>\n </template>\n\n <template v-else>\n <button\n v-for=\"pageNumber in pageCount\"\n :key=\"pageNumber\"\n class=\"pn__item pn__page\"\n :class=\"{\n 'pn__item--active': pageNumber === activePage,\n 'pn__page--fallback': !pageThumbnails[pageNumber - 1],\n }\"\n type=\"button\"\n :data-nav-page-number=\"pageNumber\"\n :aria-current=\"pageNumber === activePage ? 'page' : undefined\"\n :title=\"`跳转到第 ${pageNumber} 页`\"\n @click=\"navigate(pageNumber)\"\n >\n <template v-if=\"pageThumbnails[pageNumber - 1]\">\n <span class=\"pn__preview-wrap\">\n <img\n class=\"pn__preview\"\n :src=\"pageThumbnails[pageNumber - 1] || ''\"\n alt=\"\"\n loading=\"lazy\"\n draggable=\"false\"\n />\n </span>\n <span class=\"pn__number\">{{ pageNumber }}</span>\n </template>\n <span v-else class=\"pn__sheet\">{{ pageNumber }}</span>\n <span class=\"pn__page-label\">{{ pageLabel(pageNumber) }}</span>\n </button>\n </template>\n </nav>\n </template>\n </aside>\n</template>\n\n<style scoped>\n.pn {\n position: relative;\n width: 142px;\n min-width: 0;\n display: flex;\n flex: 0 0 142px;\n flex-direction: column;\n color: var(--muted);\n background: color-mix(in srgb, var(--surface) 94%, var(--surface-2));\n border-right: 1px solid var(--border);\n transition:\n flex-basis 160ms ease,\n width 160ms ease;\n}\n\n.pn--outline {\n width: 190px;\n flex-basis: 190px;\n}\n\n.pn--compact {\n width: 96px;\n flex-basis: 96px;\n}\n\n.pn--collapsed {\n width: 0;\n flex-basis: 0;\n border-right: 0;\n}\n\n.pn__header {\n min-height: 45px;\n padding: 7px 7px 7px 10px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 6px;\n border-bottom: 1px solid var(--border);\n}\n\n.pn__tabs {\n min-width: 0;\n display: flex;\n flex: 1;\n gap: 3px;\n}\n\n.pn__tab,\n.pn__collapse,\n.pn__reopen,\n.pn__item {\n color: inherit;\n font: inherit;\n cursor: pointer;\n background: transparent;\n border: 0;\n}\n\n.pn__tab {\n position: relative;\n min-width: 0;\n padding: 7px 5px;\n flex: 1;\n color: var(--faint);\n font-size: 11.5px;\n border-radius: 6px;\n}\n\n.pn__tab:hover {\n color: var(--text-strong);\n background: var(--bubble);\n}\n\n.pn__tab--active {\n color: var(--text-strong);\n background: var(--accent-soft);\n}\n\n.pn__tab--active::after {\n position: absolute;\n right: 10px;\n bottom: 2px;\n left: 10px;\n height: 2px;\n content: '';\n background: var(--accent);\n border-radius: 2px;\n}\n\n.pn__meta {\n min-width: 0;\n}\n\n.pn__title {\n display: block;\n color: var(--text-strong);\n font-size: 12.5px;\n font-weight: 650;\n}\n\n.pn__count {\n display: block;\n margin-top: 1px;\n color: var(--faint);\n font-size: 10.5px;\n}\n\n.pn__collapse {\n width: 27px;\n height: 27px;\n display: grid;\n flex: none;\n place-items: center;\n border-radius: 7px;\n}\n\n.pn__collapse:hover {\n color: var(--text-strong);\n background: var(--bubble);\n}\n\n.pn__reopen {\n position: absolute;\n z-index: 3;\n top: 12px;\n right: -28px;\n width: 28px;\n height: 36px;\n padding: 0;\n display: grid;\n place-items: center;\n color: var(--muted);\n background: var(--surface);\n border: 1px solid var(--border-2);\n border-radius: 0 9px 9px 0;\n box-shadow: 2px 1px 6px rgba(0, 0, 0, 0.12);\n}\n\n.pn__reopen:hover {\n color: var(--text-strong);\n background: var(--bubble);\n}\n\n.pn__list {\n min-height: 0;\n padding: 8px 7px 22px;\n flex: 1 1 auto;\n overflow: auto;\n scrollbar-width: thin;\n}\n\n.pn__item {\n width: 100%;\n min-width: 0;\n color: var(--muted);\n text-align: left;\n border: 1px solid transparent;\n border-radius: 8px;\n}\n\n.pn__item:hover {\n color: var(--text-strong);\n background: var(--bubble);\n}\n\n.pn__item--active {\n color: var(--text-strong);\n background: var(--accent-soft);\n border-color: color-mix(in srgb, var(--accent) 70%, transparent);\n}\n\n.pn__item--disabled {\n cursor: default;\n opacity: 0.52;\n}\n\n.pn__item--disabled:hover {\n color: var(--muted);\n background: transparent;\n}\n\n.pn__outline {\n gap: 8px;\n padding: 7px 8px 7px calc(9px + min(var(--pn-level), 5) * 14px);\n display: flex;\n align-items: center;\n margin: 1px 0;\n font-size: 12px;\n line-height: 1.35;\n}\n\n.pn__outline-title {\n min-width: 0;\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.pn__outline-page {\n color: var(--faint);\n font-size: 10px;\n font-variant-numeric: tabular-nums;\n}\n\n.pn__page {\n position: relative;\n padding: 5px;\n display: block;\n margin: 0 0 8px;\n}\n\n.pn__preview-wrap {\n min-width: 0;\n min-height: 62px;\n overflow: hidden;\n display: flex;\n align-items: center;\n justify-content: center;\n background: #fff;\n border: 1px solid var(--border-2);\n border-radius: 4px;\n box-shadow: 0 1px 4px rgba(0, 0, 0, 0.12);\n}\n\n.pn__item--active .pn__preview-wrap {\n border-color: var(--accent);\n box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent) 35%, transparent);\n}\n\n.pn__preview {\n width: 104px;\n max-width: 100%;\n max-height: 142px;\n display: block;\n object-fit: contain;\n pointer-events: none;\n}\n\n.pn__number {\n position: absolute;\n z-index: 2;\n top: 9px;\n left: 9px;\n min-width: 20px;\n height: 20px;\n padding: 0 5px;\n display: grid;\n place-items: center;\n color: #fff;\n font-size: 9.5px;\n font-weight: 650;\n font-variant-numeric: tabular-nums;\n background: rgba(20, 20, 20, 0.68);\n border-radius: 5px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);\n backdrop-filter: blur(4px);\n}\n\n.pn__item--active .pn__number {\n background: var(--accent);\n}\n\n.pn__page-label {\n min-width: 0;\n margin-top: 5px;\n overflow: hidden;\n display: -webkit-box;\n color: inherit;\n font-size: 10.5px;\n line-height: 1.35;\n -webkit-box-orient: vertical;\n -webkit-line-clamp: 2;\n}\n\n.pn__page--fallback {\n padding: 5px;\n display: flex;\n align-items: center;\n gap: 6px;\n margin-bottom: 5px;\n}\n\n.pn__sheet {\n width: 28px;\n height: 38px;\n display: grid;\n flex: none;\n place-items: center;\n color: #526072;\n font-size: 9.5px;\n font-variant-numeric: tabular-nums;\n background: #fff;\n border: 1px solid var(--border-2);\n border-radius: 3px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12);\n}\n\n.pn__item--active .pn__sheet {\n color: var(--accent);\n border-color: var(--accent);\n}\n\n.pn__page--fallback .pn__page-label {\n margin-top: 0;\n flex: 1;\n}\n\n@container (max-width: 500px) {\n .pn {\n width: 124px;\n flex-basis: 124px;\n }\n\n .pn--outline {\n width: 158px;\n flex-basis: 158px;\n }\n\n .pn--compact {\n width: 84px;\n flex-basis: 84px;\n }\n\n .pn--collapsed {\n width: 0;\n flex-basis: 0;\n }\n\n .pn__preview {\n max-height: 126px;\n }\n}\n</style>\n"],"mappings":"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"}
|
|
@@ -2,6 +2,8 @@ import e from "../_virtual/_plugin-vue_export-helper.js";
|
|
|
2
2
|
import t from "./PdfViewer.vue_vue_type_script_setup_true_lang.js";
|
|
3
3
|
/* empty css */
|
|
4
4
|
//#region src/components/PdfViewer.vue
|
|
5
|
-
var n = /*#__PURE__*/ e(t, [["__scopeId", "data-v-
|
|
5
|
+
var n = /*#__PURE__*/ e(t, [["__scopeId", "data-v-7de14862"]]);
|
|
6
6
|
//#endregion
|
|
7
7
|
export { n as default };
|
|
8
|
+
|
|
9
|
+
//# sourceMappingURL=PdfViewer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PdfViewer.js","names":[],"sources":["../../src/components/PdfViewer.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { onBeforeUnmount, onMounted, shallowRef, useTemplateRef, watch } from 'vue'\nimport * as pdfjsLib from 'pdfjs-dist'\nimport type { PDFDocumentProxy, RenderTask } from 'pdfjs-dist'\n// pdfjs runs its parser/rasterizer in a web worker. Vite's `?url` emits the real worker asset and returns a\n// servable URL (root-relative in the same-origin prod build, a dev URL under `pnpm dev`).\nimport pdfWorkerUrl from 'pdfjs-dist/build/pdf.worker.min.mjs?url'\n\nimport PdfNavigationPane from './PdfNavigationPane.vue'\nimport { loadPdfNavigation, type PdfNavigationItem } from '../lib/pdfNavigation'\n\npdfjsLib.GlobalWorkerOptions.workerSrc = pdfWorkerUrl\n\nconst props = defineProps<{\n url?: string\n name: string\n size?: number\n}>()\n\ntype Status = 'loading' | 'ready' | 'error'\nconst status = shallowRef<Status>('loading')\nconst errorMsg = shallowRef('')\nconst pageCount = shallowRef(0)\nconst activePage = shallowRef(1)\nconst outlineItems = shallowRef<PdfNavigationItem[]>([])\nconst pageLabels = shallowRef<string[]>([])\nconst pageThumbnails = shallowRef<Array<string | null>>([])\nconst navigationCollapsed = shallowRef(false)\nconst navigationView = shallowRef<'outline' | 'pages'>('outline')\nconst docEl = useTemplateRef<HTMLElement>('docEl')\n\nlet pdfDoc: PDFDocumentProxy | null = null\nlet renderTasks: RenderTask[] = []\nlet renderToken = 0\nlet resizeTimer: ReturnType<typeof setTimeout> | undefined\nlet lastSize = ''\nlet resizeObserver: ResizeObserver | null = null\nlet scrollFrame = 0\nconst MAX_SCALE = 2\nconst PAGE_GUTTER = 24\nconst THUMBNAIL_WIDTH = 104\n\nfunction cancelRenders(): void {\n for (const task of renderTasks) {\n try {\n task.cancel()\n } catch {\n // The task may already have settled.\n }\n }\n renderTasks = []\n}\n\nfunction disposeDoc(): void {\n cancelRenders()\n if (!pdfDoc) return\n try {\n pdfDoc.destroy()\n } catch {\n // The worker may already have been torn down.\n }\n pdfDoc = null\n}\n\nfunction createPageThumbnail(canvas: HTMLCanvasElement): string | null {\n if (!canvas.width || !canvas.height) return null\n try {\n const thumbnail = document.createElement('canvas')\n thumbnail.width = THUMBNAIL_WIDTH\n thumbnail.height = Math.max(1, Math.round(THUMBNAIL_WIDTH * (canvas.height / canvas.width)))\n const context = thumbnail.getContext('2d')\n if (!context) return null\n context.fillStyle = '#fff'\n context.fillRect(0, 0, thumbnail.width, thumbnail.height)\n context.drawImage(canvas, 0, 0, thumbnail.width, thumbnail.height)\n return thumbnail.toDataURL('image/jpeg', 0.78)\n } catch {\n return null\n }\n}\n\nfunction scrollToPage(pageNumber: number, behavior: ScrollBehavior = 'smooth'): void {\n const el = docEl.value\n const target = el?.querySelector<HTMLElement>(`[data-page-number=\"${pageNumber}\"]`)\n if (!el || !target) return\n activePage.value = pageNumber\n const rootRect = el.getBoundingClientRect()\n const targetRect = target.getBoundingClientRect()\n const paddingTop = Number.parseFloat(getComputedStyle(el).paddingTop)\n el.scrollTo({\n top: el.scrollTop + targetRect.top - rootRect.top - paddingTop,\n behavior,\n })\n}\n\nfunction syncActivePage(): void {\n if (scrollFrame) return\n scrollFrame = window.requestAnimationFrame(() => {\n scrollFrame = 0\n const el = docEl.value\n if (!el) return\n const rootRect = el.getBoundingClientRect()\n const targetY = rootRect.top + Math.min(el.clientHeight * 0.36, 220)\n let nearestPage = activePage.value\n let nearestDistance = Number.POSITIVE_INFINITY\n for (const page of el.querySelectorAll<HTMLElement>('[data-page-number]')) {\n const rect = page.getBoundingClientRect()\n const pageTarget = Math.min(Math.max(targetY, rect.top), rect.bottom)\n const distance = Math.abs(targetY - pageTarget)\n if (distance >= nearestDistance) continue\n nearestDistance = distance\n nearestPage = Number(page.dataset.pageNumber) || nearestPage\n }\n activePage.value = nearestPage\n })\n}\n\nasync function renderPages(): Promise<void> {\n const el = docEl.value\n const doc = pdfDoc\n if (!el || !doc) return\n const token = ++renderToken\n const preservedPage = activePage.value\n const preserveScroll = el.scrollTop > 0\n cancelRenders()\n el.innerHTML = ''\n\n const style = getComputedStyle(el)\n const padX = Number.parseFloat(style.paddingLeft) + Number.parseFloat(style.paddingRight)\n const padY = Number.parseFloat(style.paddingTop) + Number.parseFloat(style.paddingBottom)\n const availableWidth = Math.max(120, el.clientWidth - padX - PAGE_GUTTER)\n const availableHeight = Math.max(160, el.clientHeight - padY - PAGE_GUTTER)\n const outputScale = Math.min(window.devicePixelRatio || 1, MAX_SCALE)\n const thumbnails = Array<string | null>(doc.numPages).fill(null)\n\n for (let pageNumber = 1; pageNumber <= doc.numPages; pageNumber += 1) {\n let page\n try {\n page = await doc.getPage(pageNumber)\n } catch {\n if (token !== renderToken) return\n continue\n }\n if (token !== renderToken) return\n\n const base = page.getViewport({ scale: 1 })\n const cssScale = Math.min(availableWidth / base.width, availableHeight / base.height)\n const cssWidth = Math.round(base.width * cssScale)\n const cssHeight = Math.round(base.height * cssScale)\n const viewport = page.getViewport({ scale: cssScale * outputScale })\n\n const pageWrap = document.createElement('div')\n pageWrap.className = 'pf__page-wrap'\n pageWrap.dataset.pageNumber = `${pageNumber}`\n const canvas = document.createElement('canvas')\n canvas.className = 'pf__page'\n canvas.width = Math.ceil(viewport.width)\n canvas.height = Math.ceil(viewport.height)\n canvas.style.width = `${cssWidth}px`\n canvas.style.height = `${cssHeight}px`\n canvas.setAttribute('aria-label', `PDF 第 ${pageNumber} 页`)\n if (!canvas.getContext('2d')) continue\n pageWrap.appendChild(canvas)\n el.appendChild(pageWrap)\n\n const task = page.render({ canvas, viewport })\n renderTasks.push(task)\n let rendered = true\n try {\n await task.promise\n } catch {\n if (token !== renderToken) return\n rendered = false\n }\n if (token !== renderToken) return\n if (rendered) thumbnails[pageNumber - 1] = createPageThumbnail(canvas)\n }\n\n pageThumbnails.value = thumbnails\n if (preserveScroll) scrollToPage(preservedPage, 'auto')\n syncActivePage()\n}\n\nasync function load(): Promise<void> {\n const el = docEl.value\n if (!props.url || !el) return\n status.value = 'loading'\n errorMsg.value = ''\n outlineItems.value = []\n pageLabels.value = []\n pageThumbnails.value = []\n disposeDoc()\n el.innerHTML = ''\n const url = props.url\n\n try {\n const response = await fetch(url)\n if (!response.ok) throw new Error(`HTTP ${response.status}`)\n const bytes = new Uint8Array(await response.arrayBuffer())\n if (props.url !== url || docEl.value !== el) return\n\n const document = await pdfjsLib.getDocument({ data: bytes }).promise\n if (props.url !== url || docEl.value !== el) {\n document.destroy()\n return\n }\n pdfDoc = document\n pageCount.value = document.numPages\n activePage.value = 1\n const navigation = await loadPdfNavigation(document)\n if (props.url !== url || docEl.value !== el) return\n outlineItems.value = navigation.outline\n pageLabels.value = navigation.pageLabels\n navigationView.value = navigation.outline.length ? 'outline' : 'pages'\n await renderPages()\n if (props.url !== url || docEl.value !== el) return\n status.value = 'ready'\n } catch (cause) {\n errorMsg.value = cause instanceof Error ? cause.message : String(cause)\n status.value = 'error'\n }\n}\n\nfunction onResize(): void {\n const el = docEl.value\n if (!el || !pdfDoc) return\n const size = `${el.clientWidth}x${el.clientHeight}`\n if (!el.clientWidth || !el.clientHeight || size === lastSize) return\n lastSize = size\n clearTimeout(resizeTimer)\n resizeTimer = setTimeout(() => {\n if (pdfDoc) void renderPages()\n }, 180)\n}\n\nonMounted(() => {\n void load()\n if (typeof ResizeObserver !== 'undefined' && docEl.value) {\n lastSize = `${docEl.value.clientWidth}x${docEl.value.clientHeight}`\n resizeObserver = new ResizeObserver(onResize)\n resizeObserver.observe(docEl.value)\n }\n})\nwatch(() => props.url, load)\nonBeforeUnmount(() => {\n clearTimeout(resizeTimer)\n resizeObserver?.disconnect()\n if (scrollFrame) window.cancelAnimationFrame(scrollFrame)\n renderToken += 1\n disposeDoc()\n if (docEl.value) docEl.value.innerHTML = ''\n})\n</script>\n\n<template>\n <div class=\"pf\">\n <PdfNavigationPane\n v-if=\"status === 'ready'\"\n v-model:collapsed=\"navigationCollapsed\"\n v-model:view=\"navigationView\"\n :outline-items=\"outlineItems\"\n :page-count=\"pageCount\"\n :page-labels=\"pageLabels\"\n :page-thumbnails=\"pageThumbnails\"\n :active-page=\"activePage\"\n @navigate=\"scrollToPage\"\n />\n <div class=\"pf__stage\">\n <div ref=\"docEl\" class=\"pf__doc\" @scroll.passive=\"syncActivePage\" />\n <div v-if=\"status === 'loading'\" class=\"pf__overlay\">\n <p class=\"pf__msg\">正在渲染 PDF…</p>\n </div>\n <div v-else-if=\"status === 'error'\" class=\"pf__overlay\">\n <div class=\"pf__msg pf__msg--error\">\n <p>渲染失败:{{ errorMsg }}</p>\n <button type=\"button\" class=\"pf__retry\" @click=\"load\">重试</button>\n </div>\n </div>\n </div>\n </div>\n</template>\n\n<style scoped>\n.pf {\n position: relative;\n min-width: 0;\n min-height: 0;\n display: flex;\n flex: 1 1 auto;\n overflow: hidden;\n container-type: inline-size;\n background: var(--surface);\n}\n\n.pf__stage {\n position: relative;\n min-width: 0;\n min-height: 0;\n display: flex;\n flex: 1 1 0;\n overflow: hidden;\n}\n\n.pf__doc {\n width: 100%;\n min-width: 0;\n min-height: 0;\n flex: 1 1 0;\n overflow: auto;\n overscroll-behavior: contain;\n padding: 24px 20px 42px;\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 20px;\n box-sizing: border-box;\n background: var(--surface-2);\n scrollbar-color: #555961 transparent;\n scrollbar-width: thin;\n}\n\n.pf__doc :deep(.pf__page-wrap) {\n max-width: 100%;\n display: flex;\n flex: none;\n justify-content: center;\n}\n\n.pf__doc :deep(.pf__page) {\n max-width: 100%;\n display: block;\n background: #fff;\n border-radius: 2px;\n box-shadow: 0 4px 18px rgba(0, 0, 0, 0.28);\n}\n\n.pf__overlay {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: flex-start;\n justify-content: flex-start;\n background: var(--surface);\n}\n\n.pf__msg {\n padding: 20px 16px;\n color: var(--muted);\n font-size: 13px;\n}\n\n.pf__msg--error {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 10px;\n color: var(--danger);\n}\n\n.pf__retry {\n padding: 5px 12px;\n color: var(--text);\n font-family: inherit;\n font-size: 12.5px;\n cursor: pointer;\n background: var(--surface);\n border: 1px solid var(--border-2);\n border-radius: 8px;\n}\n\n.pf__retry:hover {\n color: var(--accent);\n border-color: var(--accent);\n}\n</style>\n"],"mappings":""}
|
|
@@ -1,15 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Side-viewer PDF (`.pdf`) renderer. Fetches the document bytes from the download URL and rasterizes each
|
|
3
|
-
* page to a <canvas> entirely in-browser via pdfjs-dist (https://github.com/mozilla/pdf.js) — a vertically
|
|
4
|
-
* scrollable list of pages (white sheets on a neutral "desk", like the docx/pptx viewers). No CDN / network
|
|
5
|
-
* beyond the file fetch: pdfjs bundles its own worker, fetched lazily as a same-origin asset (see above).
|
|
6
|
-
*
|
|
7
|
-
* Two entry shapes mirror the other viewers (docx/xlsx/pptx/zip): a real download URL for a held attachment,
|
|
8
|
-
* or a blob URL minted by the archive browsers from already-extracted bytes. Pages re-rasterize when the pane
|
|
9
|
-
* is resized (debounced) so text stays crisp at the new width.
|
|
10
|
-
*/
|
|
11
1
|
type __VLS_Props = {
|
|
12
|
-
/** Download/blob URL of the .pdf bytes. */
|
|
13
2
|
url?: string;
|
|
14
3
|
name: string;
|
|
15
4
|
size?: number;
|
|
@@ -1,114 +1,178 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import d from "
|
|
1
|
+
import e from "./PdfNavigationPane.js";
|
|
2
|
+
import { loadPdfNavigation as t } from "../lib/pdfNavigation.js";
|
|
3
|
+
import { createBlock as n, createCommentVNode as r, createElementBlock as i, createElementVNode as a, defineComponent as o, onBeforeUnmount as s, onMounted as c, openBlock as l, shallowRef as u, toDisplayString as d, useTemplateRef as f, watch as p } from "vue";
|
|
4
|
+
import * as m from "pdfjs-dist";
|
|
5
|
+
import h from "pdfjs-dist/build/pdf.worker.min.mjs?url";
|
|
4
6
|
//#region src/components/PdfViewer.vue?vue&type=script&setup=true&lang.ts
|
|
5
|
-
var
|
|
7
|
+
var g = { class: "pf" }, _ = { class: "pf__stage" }, v = {
|
|
6
8
|
key: 0,
|
|
7
9
|
class: "pf__overlay"
|
|
8
|
-
},
|
|
10
|
+
}, y = {
|
|
9
11
|
key: 1,
|
|
10
12
|
class: "pf__overlay"
|
|
11
|
-
},
|
|
13
|
+
}, b = { class: "pf__msg pf__msg--error" }, x = 2, S = 24, C = 104, w = /*@__PURE__*/ o({
|
|
12
14
|
__name: "PdfViewer",
|
|
13
15
|
props: {
|
|
14
16
|
url: {},
|
|
15
17
|
name: {},
|
|
16
18
|
size: {}
|
|
17
19
|
},
|
|
18
|
-
setup(
|
|
19
|
-
|
|
20
|
-
let
|
|
21
|
-
function
|
|
22
|
-
for (let e of
|
|
20
|
+
setup(o) {
|
|
21
|
+
m.GlobalWorkerOptions.workerSrc = h;
|
|
22
|
+
let w = o, T = u("loading"), E = u(""), D = u(0), O = u(1), k = u([]), A = u([]), j = u([]), M = u(!1), N = u("outline"), P = f("docEl"), F = null, I = [], L = 0, R, z = "", B = null, V = 0;
|
|
23
|
+
function H() {
|
|
24
|
+
for (let e of I) try {
|
|
23
25
|
e.cancel();
|
|
24
26
|
} catch {}
|
|
25
|
-
|
|
27
|
+
I = [];
|
|
26
28
|
}
|
|
27
|
-
function
|
|
28
|
-
if (
|
|
29
|
+
function U() {
|
|
30
|
+
if (H(), F) {
|
|
29
31
|
try {
|
|
30
|
-
|
|
32
|
+
F.destroy();
|
|
31
33
|
} catch {}
|
|
32
|
-
|
|
34
|
+
F = null;
|
|
33
35
|
}
|
|
34
36
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
+
function W(e) {
|
|
38
|
+
if (!e.width || !e.height) return null;
|
|
39
|
+
try {
|
|
40
|
+
let t = document.createElement("canvas");
|
|
41
|
+
t.width = C, t.height = Math.max(1, Math.round(C * (e.height / e.width)));
|
|
42
|
+
let n = t.getContext("2d");
|
|
43
|
+
return n ? (n.fillStyle = "#fff", n.fillRect(0, 0, t.width, t.height), n.drawImage(e, 0, 0, t.width, t.height), t.toDataURL("image/jpeg", .78)) : null;
|
|
44
|
+
} catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function G(e, t = "smooth") {
|
|
49
|
+
let n = P.value, r = n?.querySelector(`[data-page-number="${e}"]`);
|
|
50
|
+
if (!n || !r) return;
|
|
51
|
+
O.value = e;
|
|
52
|
+
let i = n.getBoundingClientRect(), a = r.getBoundingClientRect(), o = Number.parseFloat(getComputedStyle(n).paddingTop);
|
|
53
|
+
n.scrollTo({
|
|
54
|
+
top: n.scrollTop + a.top - i.top - o,
|
|
55
|
+
behavior: t
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
function K() {
|
|
59
|
+
V ||= window.requestAnimationFrame(() => {
|
|
60
|
+
V = 0;
|
|
61
|
+
let e = P.value;
|
|
62
|
+
if (!e) return;
|
|
63
|
+
let t = e.getBoundingClientRect().top + Math.min(e.clientHeight * .36, 220), n = O.value, r = Infinity;
|
|
64
|
+
for (let i of e.querySelectorAll("[data-page-number]")) {
|
|
65
|
+
let e = i.getBoundingClientRect(), a = Math.min(Math.max(t, e.top), e.bottom), o = Math.abs(t - a);
|
|
66
|
+
o >= r || (r = o, n = Number(i.dataset.pageNumber) || n);
|
|
67
|
+
}
|
|
68
|
+
O.value = n;
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
async function q() {
|
|
72
|
+
let e = P.value, t = F;
|
|
37
73
|
if (!e || !t) return;
|
|
38
|
-
let n = ++
|
|
39
|
-
|
|
40
|
-
let
|
|
41
|
-
for (let r = 1; r <= t.numPages; r
|
|
74
|
+
let n = ++L, r = O.value, i = e.scrollTop > 0;
|
|
75
|
+
H(), e.innerHTML = "";
|
|
76
|
+
let a = getComputedStyle(e), o = Number.parseFloat(a.paddingLeft) + Number.parseFloat(a.paddingRight), s = Number.parseFloat(a.paddingTop) + Number.parseFloat(a.paddingBottom), c = Math.max(120, e.clientWidth - o - S), l = Math.max(160, e.clientHeight - s - S), u = Math.min(window.devicePixelRatio || 1, x), d = Array(t.numPages).fill(null);
|
|
77
|
+
for (let r = 1; r <= t.numPages; r += 1) {
|
|
42
78
|
let i;
|
|
43
79
|
try {
|
|
44
80
|
i = await t.getPage(r);
|
|
45
81
|
} catch {
|
|
46
|
-
if (n !==
|
|
82
|
+
if (n !== L) return;
|
|
47
83
|
continue;
|
|
48
84
|
}
|
|
49
|
-
if (n !==
|
|
50
|
-
let
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
85
|
+
if (n !== L) return;
|
|
86
|
+
let a = i.getViewport({ scale: 1 }), o = Math.min(c / a.width, l / a.height), s = Math.round(a.width * o), f = Math.round(a.height * o), p = i.getViewport({ scale: o * u }), m = document.createElement("div");
|
|
87
|
+
m.className = "pf__page-wrap", m.dataset.pageNumber = `${r}`;
|
|
88
|
+
let h = document.createElement("canvas");
|
|
89
|
+
if (h.className = "pf__page", h.width = Math.ceil(p.width), h.height = Math.ceil(p.height), h.style.width = `${s}px`, h.style.height = `${f}px`, h.setAttribute("aria-label", `PDF 第 ${r} 页`), !h.getContext("2d")) continue;
|
|
90
|
+
m.appendChild(h), e.appendChild(m);
|
|
91
|
+
let g = i.render({
|
|
92
|
+
canvas: h,
|
|
93
|
+
viewport: p
|
|
56
94
|
});
|
|
57
|
-
|
|
95
|
+
I.push(g);
|
|
96
|
+
let _ = !0;
|
|
58
97
|
try {
|
|
59
|
-
await
|
|
98
|
+
await g.promise;
|
|
60
99
|
} catch {
|
|
61
|
-
if (n !==
|
|
100
|
+
if (n !== L) return;
|
|
101
|
+
_ = !1;
|
|
62
102
|
}
|
|
63
|
-
if (n !==
|
|
103
|
+
if (n !== L) return;
|
|
104
|
+
_ && (d[r - 1] = W(h));
|
|
64
105
|
}
|
|
106
|
+
j.value = d, i && G(r, "auto"), K();
|
|
65
107
|
}
|
|
66
|
-
async function
|
|
67
|
-
let e =
|
|
68
|
-
if (!
|
|
69
|
-
|
|
70
|
-
let
|
|
108
|
+
async function J() {
|
|
109
|
+
let e = P.value;
|
|
110
|
+
if (!w.url || !e) return;
|
|
111
|
+
T.value = "loading", E.value = "", k.value = [], A.value = [], j.value = [], U(), e.innerHTML = "";
|
|
112
|
+
let n = w.url;
|
|
71
113
|
try {
|
|
72
|
-
let
|
|
73
|
-
if (!
|
|
74
|
-
let
|
|
75
|
-
if (
|
|
76
|
-
let
|
|
77
|
-
if (
|
|
78
|
-
|
|
79
|
-
i.destroy();
|
|
80
|
-
} catch {}
|
|
114
|
+
let r = await fetch(n);
|
|
115
|
+
if (!r.ok) throw Error(`HTTP ${r.status}`);
|
|
116
|
+
let i = new Uint8Array(await r.arrayBuffer());
|
|
117
|
+
if (w.url !== n || P.value !== e) return;
|
|
118
|
+
let a = await m.getDocument({ data: i }).promise;
|
|
119
|
+
if (w.url !== n || P.value !== e) {
|
|
120
|
+
a.destroy();
|
|
81
121
|
return;
|
|
82
122
|
}
|
|
83
|
-
|
|
84
|
-
|
|
123
|
+
F = a, D.value = a.numPages, O.value = 1;
|
|
124
|
+
let o = await t(a);
|
|
125
|
+
if (w.url !== n || P.value !== e || (k.value = o.outline, A.value = o.pageLabels, N.value = o.outline.length ? "outline" : "pages", await q(), w.url !== n || P.value !== e)) return;
|
|
126
|
+
T.value = "ready";
|
|
85
127
|
} catch (e) {
|
|
86
|
-
|
|
128
|
+
E.value = e instanceof Error ? e.message : String(e), T.value = "error";
|
|
87
129
|
}
|
|
88
130
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
S && D();
|
|
131
|
+
function Y() {
|
|
132
|
+
let e = P.value;
|
|
133
|
+
if (!e || !F) return;
|
|
134
|
+
let t = `${e.clientWidth}x${e.clientHeight}`;
|
|
135
|
+
!e.clientWidth || !e.clientHeight || t === z || (z = t, clearTimeout(R), R = setTimeout(() => {
|
|
136
|
+
F && q();
|
|
96
137
|
}, 180));
|
|
97
138
|
}
|
|
98
|
-
return
|
|
99
|
-
|
|
100
|
-
}),
|
|
101
|
-
clearTimeout(
|
|
102
|
-
}), (
|
|
139
|
+
return c(() => {
|
|
140
|
+
J(), typeof ResizeObserver < "u" && P.value && (z = `${P.value.clientWidth}x${P.value.clientHeight}`, B = new ResizeObserver(Y), B.observe(P.value));
|
|
141
|
+
}), p(() => w.url, J), s(() => {
|
|
142
|
+
clearTimeout(R), B?.disconnect(), V && window.cancelAnimationFrame(V), L += 1, U(), P.value && (P.value.innerHTML = "");
|
|
143
|
+
}), (t, o) => (l(), i("div", g, [T.value === "ready" ? (l(), n(e, {
|
|
144
|
+
key: 0,
|
|
145
|
+
collapsed: M.value,
|
|
146
|
+
"onUpdate:collapsed": o[0] ||= (e) => M.value = e,
|
|
147
|
+
view: N.value,
|
|
148
|
+
"onUpdate:view": o[1] ||= (e) => N.value = e,
|
|
149
|
+
"outline-items": k.value,
|
|
150
|
+
"page-count": D.value,
|
|
151
|
+
"page-labels": A.value,
|
|
152
|
+
"page-thumbnails": j.value,
|
|
153
|
+
"active-page": O.value,
|
|
154
|
+
onNavigate: G
|
|
155
|
+
}, null, 8, [
|
|
156
|
+
"collapsed",
|
|
157
|
+
"view",
|
|
158
|
+
"outline-items",
|
|
159
|
+
"page-count",
|
|
160
|
+
"page-labels",
|
|
161
|
+
"page-thumbnails",
|
|
162
|
+
"active-page"
|
|
163
|
+
])) : r("", !0), a("div", _, [a("div", {
|
|
103
164
|
ref_key: "docEl",
|
|
104
|
-
ref:
|
|
105
|
-
class: "pf__doc"
|
|
106
|
-
|
|
165
|
+
ref: P,
|
|
166
|
+
class: "pf__doc",
|
|
167
|
+
onScrollPassive: K
|
|
168
|
+
}, null, 544), T.value === "loading" ? (l(), i("div", v, [...o[2] ||= [a("p", { class: "pf__msg" }, "正在渲染 PDF…", -1)]])) : T.value === "error" ? (l(), i("div", y, [a("div", b, [a("p", null, "渲染失败:" + d(E.value), 1), a("button", {
|
|
107
169
|
type: "button",
|
|
108
170
|
class: "pf__retry",
|
|
109
|
-
onClick:
|
|
110
|
-
}, "重试")])])) :
|
|
171
|
+
onClick: J
|
|
172
|
+
}, "重试")])])) : r("", !0)])]));
|
|
111
173
|
}
|
|
112
174
|
});
|
|
113
175
|
//#endregion
|
|
114
|
-
export {
|
|
176
|
+
export { w as default };
|
|
177
|
+
|
|
178
|
+
//# sourceMappingURL=PdfViewer.vue_vue_type_script_setup_true_lang.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PdfViewer.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../src/components/PdfViewer.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { onBeforeUnmount, onMounted, shallowRef, useTemplateRef, watch } from 'vue'\nimport * as pdfjsLib from 'pdfjs-dist'\nimport type { PDFDocumentProxy, RenderTask } from 'pdfjs-dist'\n// pdfjs runs its parser/rasterizer in a web worker. Vite's `?url` emits the real worker asset and returns a\n// servable URL (root-relative in the same-origin prod build, a dev URL under `pnpm dev`).\nimport pdfWorkerUrl from 'pdfjs-dist/build/pdf.worker.min.mjs?url'\n\nimport PdfNavigationPane from './PdfNavigationPane.vue'\nimport { loadPdfNavigation, type PdfNavigationItem } from '../lib/pdfNavigation'\n\npdfjsLib.GlobalWorkerOptions.workerSrc = pdfWorkerUrl\n\nconst props = defineProps<{\n url?: string\n name: string\n size?: number\n}>()\n\ntype Status = 'loading' | 'ready' | 'error'\nconst status = shallowRef<Status>('loading')\nconst errorMsg = shallowRef('')\nconst pageCount = shallowRef(0)\nconst activePage = shallowRef(1)\nconst outlineItems = shallowRef<PdfNavigationItem[]>([])\nconst pageLabels = shallowRef<string[]>([])\nconst pageThumbnails = shallowRef<Array<string | null>>([])\nconst navigationCollapsed = shallowRef(false)\nconst navigationView = shallowRef<'outline' | 'pages'>('outline')\nconst docEl = useTemplateRef<HTMLElement>('docEl')\n\nlet pdfDoc: PDFDocumentProxy | null = null\nlet renderTasks: RenderTask[] = []\nlet renderToken = 0\nlet resizeTimer: ReturnType<typeof setTimeout> | undefined\nlet lastSize = ''\nlet resizeObserver: ResizeObserver | null = null\nlet scrollFrame = 0\nconst MAX_SCALE = 2\nconst PAGE_GUTTER = 24\nconst THUMBNAIL_WIDTH = 104\n\nfunction cancelRenders(): void {\n for (const task of renderTasks) {\n try {\n task.cancel()\n } catch {\n // The task may already have settled.\n }\n }\n renderTasks = []\n}\n\nfunction disposeDoc(): void {\n cancelRenders()\n if (!pdfDoc) return\n try {\n pdfDoc.destroy()\n } catch {\n // The worker may already have been torn down.\n }\n pdfDoc = null\n}\n\nfunction createPageThumbnail(canvas: HTMLCanvasElement): string | null {\n if (!canvas.width || !canvas.height) return null\n try {\n const thumbnail = document.createElement('canvas')\n thumbnail.width = THUMBNAIL_WIDTH\n thumbnail.height = Math.max(1, Math.round(THUMBNAIL_WIDTH * (canvas.height / canvas.width)))\n const context = thumbnail.getContext('2d')\n if (!context) return null\n context.fillStyle = '#fff'\n context.fillRect(0, 0, thumbnail.width, thumbnail.height)\n context.drawImage(canvas, 0, 0, thumbnail.width, thumbnail.height)\n return thumbnail.toDataURL('image/jpeg', 0.78)\n } catch {\n return null\n }\n}\n\nfunction scrollToPage(pageNumber: number, behavior: ScrollBehavior = 'smooth'): void {\n const el = docEl.value\n const target = el?.querySelector<HTMLElement>(`[data-page-number=\"${pageNumber}\"]`)\n if (!el || !target) return\n activePage.value = pageNumber\n const rootRect = el.getBoundingClientRect()\n const targetRect = target.getBoundingClientRect()\n const paddingTop = Number.parseFloat(getComputedStyle(el).paddingTop)\n el.scrollTo({\n top: el.scrollTop + targetRect.top - rootRect.top - paddingTop,\n behavior,\n })\n}\n\nfunction syncActivePage(): void {\n if (scrollFrame) return\n scrollFrame = window.requestAnimationFrame(() => {\n scrollFrame = 0\n const el = docEl.value\n if (!el) return\n const rootRect = el.getBoundingClientRect()\n const targetY = rootRect.top + Math.min(el.clientHeight * 0.36, 220)\n let nearestPage = activePage.value\n let nearestDistance = Number.POSITIVE_INFINITY\n for (const page of el.querySelectorAll<HTMLElement>('[data-page-number]')) {\n const rect = page.getBoundingClientRect()\n const pageTarget = Math.min(Math.max(targetY, rect.top), rect.bottom)\n const distance = Math.abs(targetY - pageTarget)\n if (distance >= nearestDistance) continue\n nearestDistance = distance\n nearestPage = Number(page.dataset.pageNumber) || nearestPage\n }\n activePage.value = nearestPage\n })\n}\n\nasync function renderPages(): Promise<void> {\n const el = docEl.value\n const doc = pdfDoc\n if (!el || !doc) return\n const token = ++renderToken\n const preservedPage = activePage.value\n const preserveScroll = el.scrollTop > 0\n cancelRenders()\n el.innerHTML = ''\n\n const style = getComputedStyle(el)\n const padX = Number.parseFloat(style.paddingLeft) + Number.parseFloat(style.paddingRight)\n const padY = Number.parseFloat(style.paddingTop) + Number.parseFloat(style.paddingBottom)\n const availableWidth = Math.max(120, el.clientWidth - padX - PAGE_GUTTER)\n const availableHeight = Math.max(160, el.clientHeight - padY - PAGE_GUTTER)\n const outputScale = Math.min(window.devicePixelRatio || 1, MAX_SCALE)\n const thumbnails = Array<string | null>(doc.numPages).fill(null)\n\n for (let pageNumber = 1; pageNumber <= doc.numPages; pageNumber += 1) {\n let page\n try {\n page = await doc.getPage(pageNumber)\n } catch {\n if (token !== renderToken) return\n continue\n }\n if (token !== renderToken) return\n\n const base = page.getViewport({ scale: 1 })\n const cssScale = Math.min(availableWidth / base.width, availableHeight / base.height)\n const cssWidth = Math.round(base.width * cssScale)\n const cssHeight = Math.round(base.height * cssScale)\n const viewport = page.getViewport({ scale: cssScale * outputScale })\n\n const pageWrap = document.createElement('div')\n pageWrap.className = 'pf__page-wrap'\n pageWrap.dataset.pageNumber = `${pageNumber}`\n const canvas = document.createElement('canvas')\n canvas.className = 'pf__page'\n canvas.width = Math.ceil(viewport.width)\n canvas.height = Math.ceil(viewport.height)\n canvas.style.width = `${cssWidth}px`\n canvas.style.height = `${cssHeight}px`\n canvas.setAttribute('aria-label', `PDF 第 ${pageNumber} 页`)\n if (!canvas.getContext('2d')) continue\n pageWrap.appendChild(canvas)\n el.appendChild(pageWrap)\n\n const task = page.render({ canvas, viewport })\n renderTasks.push(task)\n let rendered = true\n try {\n await task.promise\n } catch {\n if (token !== renderToken) return\n rendered = false\n }\n if (token !== renderToken) return\n if (rendered) thumbnails[pageNumber - 1] = createPageThumbnail(canvas)\n }\n\n pageThumbnails.value = thumbnails\n if (preserveScroll) scrollToPage(preservedPage, 'auto')\n syncActivePage()\n}\n\nasync function load(): Promise<void> {\n const el = docEl.value\n if (!props.url || !el) return\n status.value = 'loading'\n errorMsg.value = ''\n outlineItems.value = []\n pageLabels.value = []\n pageThumbnails.value = []\n disposeDoc()\n el.innerHTML = ''\n const url = props.url\n\n try {\n const response = await fetch(url)\n if (!response.ok) throw new Error(`HTTP ${response.status}`)\n const bytes = new Uint8Array(await response.arrayBuffer())\n if (props.url !== url || docEl.value !== el) return\n\n const document = await pdfjsLib.getDocument({ data: bytes }).promise\n if (props.url !== url || docEl.value !== el) {\n document.destroy()\n return\n }\n pdfDoc = document\n pageCount.value = document.numPages\n activePage.value = 1\n const navigation = await loadPdfNavigation(document)\n if (props.url !== url || docEl.value !== el) return\n outlineItems.value = navigation.outline\n pageLabels.value = navigation.pageLabels\n navigationView.value = navigation.outline.length ? 'outline' : 'pages'\n await renderPages()\n if (props.url !== url || docEl.value !== el) return\n status.value = 'ready'\n } catch (cause) {\n errorMsg.value = cause instanceof Error ? cause.message : String(cause)\n status.value = 'error'\n }\n}\n\nfunction onResize(): void {\n const el = docEl.value\n if (!el || !pdfDoc) return\n const size = `${el.clientWidth}x${el.clientHeight}`\n if (!el.clientWidth || !el.clientHeight || size === lastSize) return\n lastSize = size\n clearTimeout(resizeTimer)\n resizeTimer = setTimeout(() => {\n if (pdfDoc) void renderPages()\n }, 180)\n}\n\nonMounted(() => {\n void load()\n if (typeof ResizeObserver !== 'undefined' && docEl.value) {\n lastSize = `${docEl.value.clientWidth}x${docEl.value.clientHeight}`\n resizeObserver = new ResizeObserver(onResize)\n resizeObserver.observe(docEl.value)\n }\n})\nwatch(() => props.url, load)\nonBeforeUnmount(() => {\n clearTimeout(resizeTimer)\n resizeObserver?.disconnect()\n if (scrollFrame) window.cancelAnimationFrame(scrollFrame)\n renderToken += 1\n disposeDoc()\n if (docEl.value) docEl.value.innerHTML = ''\n})\n</script>\n\n<template>\n <div class=\"pf\">\n <PdfNavigationPane\n v-if=\"status === 'ready'\"\n v-model:collapsed=\"navigationCollapsed\"\n v-model:view=\"navigationView\"\n :outline-items=\"outlineItems\"\n :page-count=\"pageCount\"\n :page-labels=\"pageLabels\"\n :page-thumbnails=\"pageThumbnails\"\n :active-page=\"activePage\"\n @navigate=\"scrollToPage\"\n />\n <div class=\"pf__stage\">\n <div ref=\"docEl\" class=\"pf__doc\" @scroll.passive=\"syncActivePage\" />\n <div v-if=\"status === 'loading'\" class=\"pf__overlay\">\n <p class=\"pf__msg\">正在渲染 PDF…</p>\n </div>\n <div v-else-if=\"status === 'error'\" class=\"pf__overlay\">\n <div class=\"pf__msg pf__msg--error\">\n <p>渲染失败:{{ errorMsg }}</p>\n <button type=\"button\" class=\"pf__retry\" @click=\"load\">重试</button>\n </div>\n </div>\n </div>\n </div>\n</template>\n\n<style scoped>\n.pf {\n position: relative;\n min-width: 0;\n min-height: 0;\n display: flex;\n flex: 1 1 auto;\n overflow: hidden;\n container-type: inline-size;\n background: var(--surface);\n}\n\n.pf__stage {\n position: relative;\n min-width: 0;\n min-height: 0;\n display: flex;\n flex: 1 1 0;\n overflow: hidden;\n}\n\n.pf__doc {\n width: 100%;\n min-width: 0;\n min-height: 0;\n flex: 1 1 0;\n overflow: auto;\n overscroll-behavior: contain;\n padding: 24px 20px 42px;\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 20px;\n box-sizing: border-box;\n background: var(--surface-2);\n scrollbar-color: #555961 transparent;\n scrollbar-width: thin;\n}\n\n.pf__doc :deep(.pf__page-wrap) {\n max-width: 100%;\n display: flex;\n flex: none;\n justify-content: center;\n}\n\n.pf__doc :deep(.pf__page) {\n max-width: 100%;\n display: block;\n background: #fff;\n border-radius: 2px;\n box-shadow: 0 4px 18px rgba(0, 0, 0, 0.28);\n}\n\n.pf__overlay {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: flex-start;\n justify-content: flex-start;\n background: var(--surface);\n}\n\n.pf__msg {\n padding: 20px 16px;\n color: var(--muted);\n font-size: 13px;\n}\n\n.pf__msg--error {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 10px;\n color: var(--danger);\n}\n\n.pf__retry {\n padding: 5px 12px;\n color: var(--text);\n font-family: inherit;\n font-size: 12.5px;\n cursor: pointer;\n background: var(--surface);\n border: 1px solid var(--border-2);\n border-radius: 8px;\n}\n\n.pf__retry:hover {\n color: var(--accent);\n border-color: var(--accent);\n}\n</style>\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PptxNavigationPane.js","names":[],"sources":["../../src/components/PptxNavigationPane.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n nextTick,\n onBeforeUnmount,\n onMounted,\n useTemplateRef,\n watch,\n type ComponentPublicInstance,\n} from 'vue'\nimport type { PptxViewer, SlideHandle } from '@aiden0z/pptx-renderer'\nimport Icon from '@/components/icons/Icon.vue'\nimport type { PptxNavigationItem } from '@/lib/pptxNavigation'\n\nconst props = defineProps<{\n viewer: PptxViewer\n items: readonly PptxNavigationItem[]\n activeIndex: number\n}>()\n\nconst emit = defineEmits<{ navigate: [index: number] }>()\nconst collapsed = defineModel<boolean>('collapsed', { default: false })\nconst listEl = useTemplateRef<HTMLElement>('list')\n\nconst thumbElements = new Map<number, HTMLElement>()\nconst thumbHandles = new Map<number, SlideHandle>()\nlet observer: IntersectionObserver | null = null\n\nfunction disposeThumbnails(): void {\n observer?.disconnect()\n observer = null\n for (const handle of thumbHandles.values()) handle.dispose()\n thumbHandles.clear()\n for (const element of thumbElements.values()) element.innerHTML = ''\n}\n\nfunction mountThumbnail(index: number, element: HTMLElement): void {\n if (thumbHandles.has(index)) return\n const handle = props.viewer.renderThumbnailToContainer(index, element, { width: 104 })\n if (!handle) return\n thumbHandles.set(index, handle)\n void handle.ready.catch(() => {\n if (thumbHandles.get(index) !== handle) return\n handle.dispose()\n thumbHandles.delete(index)\n element.innerHTML = ''\n })\n}\n\nasync function setupThumbnails(): Promise<void> {\n disposeThumbnails()\n if (collapsed.value) return\n await nextTick()\n const root = listEl.value\n if (!root) return\n\n if (typeof IntersectionObserver === 'undefined') {\n for (const [index, element] of thumbElements) mountThumbnail(index, element)\n return\n }\n\n observer = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n if (!entry.isIntersecting) continue\n const element = entry.target as HTMLElement\n const index = Number(element.dataset.slideIndex)\n if (Number.isInteger(index)) mountThumbnail(index, element)\n observer?.unobserve(element)\n }\n },\n { root, rootMargin: '140px 0px' },\n )\n for (const element of thumbElements.values()) observer.observe(element)\n}\n\nfunction setThumbElement(index: number, value: Element | ComponentPublicInstance | null): void {\n const previous = thumbElements.get(index)\n if (previous && previous !== value) observer?.unobserve(previous)\n if (!(value instanceof HTMLElement)) {\n thumbElements.delete(index)\n return\n }\n thumbElements.set(index, value)\n observer?.observe(value)\n}\n\nwatch(() => props.viewer, setupThumbnails, { flush: 'post' })\nwatch(() => props.items, setupThumbnails, { flush: 'post' })\nwatch(collapsed, setupThumbnails, { flush: 'post' })\nwatch(\n () => props.activeIndex,\n async (index) => {\n await nextTick()\n listEl.value\n ?.querySelector<HTMLElement>(`[data-nav-slide-index=\"${index}\"]`)\n ?.scrollIntoView({ block: 'nearest' })\n },\n)\n\nonMounted(setupThumbnails)\nonBeforeUnmount(disposeThumbnails)\n</script>\n\n<template>\n <aside class=\"pn\" :class=\"{ 'pn--collapsed': collapsed }\" aria-label=\"幻灯片导航\">\n <button\n v-if=\"collapsed\"\n type=\"button\"\n class=\"pn__reopen\"\n aria-label=\"展开幻灯片导航\"\n title=\"展开幻灯片导航\"\n @click=\"collapsed = false\"\n >\n <Icon name=\"chevronRight\" :size=\"15\" />\n </button>\n\n <template v-else>\n <header class=\"pn__header\">\n <div>\n <strong class=\"pn__title\">幻灯片</strong>\n <span class=\"pn__count\">{{ items.length }} 张</span>\n </div>\n <button\n type=\"button\"\n class=\"pn__collapse\"\n aria-label=\"收起幻灯片导航\"\n title=\"收起幻灯片导航\"\n @click=\"collapsed = true\"\n >\n <Icon name=\"chevronLeft\" :size=\"15\" />\n </button>\n </header>\n\n <nav ref=\"list\" class=\"pn__list\" aria-label=\"幻灯片列表\">\n <button\n v-for=\"item in items\"\n :key=\"item.index\"\n type=\"button\"\n class=\"pn__item\"\n :class=\"{ 'pn__item--active': item.index === activeIndex }\"\n :data-nav-slide-index=\"item.index\"\n :aria-current=\"item.index === activeIndex ? 'page' : undefined\"\n :title=\"`${item.index + 1}. ${item.title}`\"\n @click=\"emit('navigate', item.index)\"\n >\n <span class=\"pn__number\">{{ item.index + 1 }}</span>\n <span class=\"pn__preview-wrap\">\n <span\n :ref=\"(value) => setThumbElement(item.index, value)\"\n class=\"pn__preview\"\n :data-slide-index=\"item.index\"\n aria-hidden=\"true\"\n />\n </span>\n <span class=\"pn__label\">{{ item.title }}</span>\n </button>\n </nav>\n </template>\n </aside>\n</template>\n\n<style scoped>\n.pn {\n position: relative;\n flex: 0 0 142px;\n width: 142px;\n min-width: 0;\n display: flex;\n flex-direction: column;\n border-right: 1px solid var(--border);\n background: color-mix(in srgb, var(--surface) 94%, var(--surface-2));\n transition: flex-basis 160ms ease;\n}\n.pn--collapsed {\n flex-basis: 0;\n width: 0;\n border-right: 0;\n}\n.pn__header {\n min-height: 45px;\n padding: 9px 8px 8px 12px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 6px;\n border-bottom: 1px solid var(--border);\n}\n.pn__title {\n display: block;\n color: var(--text-strong);\n font-size: 12.5px;\n font-weight: 650;\n}\n.pn__count {\n display: block;\n margin-top: 1px;\n color: var(--faint);\n font-size: 10.5px;\n}\n.pn__collapse,\n.pn__reopen {\n border: 0;\n color: var(--muted);\n background: transparent;\n cursor: pointer;\n}\n.pn__collapse {\n width: 27px;\n height: 27px;\n display: grid;\n place-items: center;\n border-radius: 7px;\n}\n.pn__collapse:hover {\n color: var(--text-strong);\n background: var(--bubble);\n}\n.pn__reopen {\n position: absolute;\n z-index: 3;\n top: 12px;\n right: -28px;\n width: 28px;\n height: 36px;\n padding: 0;\n display: grid;\n place-items: center;\n border: 1px solid var(--border-2);\n border-radius: 0 9px 9px 0;\n background: var(--surface);\n box-shadow: 2px 1px 6px rgba(0, 0, 0, 0.12);\n}\n.pn__reopen:hover {\n color: var(--text-strong);\n background: var(--bubble);\n}\n.pn__list {\n flex: 1 1 auto;\n min-height: 0;\n overflow: auto;\n padding: 8px 7px 22px;\n scrollbar-width: thin;\n}\n.pn__item {\n position: relative;\n width: 100%;\n min-width: 0;\n margin: 0 0 8px;\n padding: 5px;\n display: block;\n border: 1px solid transparent;\n border-radius: 8px;\n color: var(--muted);\n background: transparent;\n font: inherit;\n text-align: left;\n cursor: pointer;\n}\n.pn__item:hover {\n color: var(--text-strong);\n background: var(--bubble);\n}\n.pn__item--active {\n border-color: color-mix(in srgb, var(--accent) 70%, transparent);\n color: var(--text-strong);\n background: var(--accent-soft);\n}\n.pn__number {\n position: absolute;\n z-index: 2;\n top: 9px;\n left: 9px;\n min-width: 20px;\n height: 20px;\n padding: 0 5px;\n display: grid;\n place-items: center;\n border-radius: 5px;\n color: #fff;\n background: rgba(20, 20, 20, 0.68);\n font-size: 9.5px;\n font-weight: 650;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);\n backdrop-filter: blur(4px);\n}\n.pn__item--active .pn__number {\n color: #fff;\n background: var(--accent);\n}\n.pn__preview-wrap {\n height: 76px;\n min-width: 0;\n overflow: hidden;\n display: flex;\n align-items: center;\n justify-content: center;\n border: 1px solid var(--border-2);\n border-radius: 4px;\n background: #fff;\n box-shadow: 0 1px 4px rgba(0, 0, 0, 0.12);\n}\n.pn__item--active .pn__preview-wrap {\n border-color: var(--accent);\n box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent) 35%, transparent);\n}\n.pn__preview {\n width: 104px;\n min-height: 1px;\n display: block;\n overflow: hidden;\n pointer-events: none;\n}\n.pn__label {\n min-width: 0;\n margin-top: 5px;\n overflow: hidden;\n display: -webkit-box;\n -webkit-box-orient: vertical;\n -webkit-line-clamp: 2;\n font-size: 10.5px;\n line-height: 1.35;\n}\n\n@container (max-width: 500px) {\n .pn {\n flex-basis: 124px;\n width: 124px;\n }\n .pn--collapsed {\n flex-basis: 0;\n width: 0;\n }\n .pn__preview-wrap {\n height: 66px;\n }\n}\n</style>\n"],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PptxNavigationPane.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../src/components/PptxNavigationPane.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n nextTick,\n onBeforeUnmount,\n onMounted,\n useTemplateRef,\n watch,\n type ComponentPublicInstance,\n} from 'vue'\nimport type { PptxViewer, SlideHandle } from '@aiden0z/pptx-renderer'\nimport Icon from '@/components/icons/Icon.vue'\nimport type { PptxNavigationItem } from '@/lib/pptxNavigation'\n\nconst props = defineProps<{\n viewer: PptxViewer\n items: readonly PptxNavigationItem[]\n activeIndex: number\n}>()\n\nconst emit = defineEmits<{ navigate: [index: number] }>()\nconst collapsed = defineModel<boolean>('collapsed', { default: false })\nconst listEl = useTemplateRef<HTMLElement>('list')\n\nconst thumbElements = new Map<number, HTMLElement>()\nconst thumbHandles = new Map<number, SlideHandle>()\nlet observer: IntersectionObserver | null = null\n\nfunction disposeThumbnails(): void {\n observer?.disconnect()\n observer = null\n for (const handle of thumbHandles.values()) handle.dispose()\n thumbHandles.clear()\n for (const element of thumbElements.values()) element.innerHTML = ''\n}\n\nfunction mountThumbnail(index: number, element: HTMLElement): void {\n if (thumbHandles.has(index)) return\n const handle = props.viewer.renderThumbnailToContainer(index, element, { width: 104 })\n if (!handle) return\n thumbHandles.set(index, handle)\n void handle.ready.catch(() => {\n if (thumbHandles.get(index) !== handle) return\n handle.dispose()\n thumbHandles.delete(index)\n element.innerHTML = ''\n })\n}\n\nasync function setupThumbnails(): Promise<void> {\n disposeThumbnails()\n if (collapsed.value) return\n await nextTick()\n const root = listEl.value\n if (!root) return\n\n if (typeof IntersectionObserver === 'undefined') {\n for (const [index, element] of thumbElements) mountThumbnail(index, element)\n return\n }\n\n observer = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n if (!entry.isIntersecting) continue\n const element = entry.target as HTMLElement\n const index = Number(element.dataset.slideIndex)\n if (Number.isInteger(index)) mountThumbnail(index, element)\n observer?.unobserve(element)\n }\n },\n { root, rootMargin: '140px 0px' },\n )\n for (const element of thumbElements.values()) observer.observe(element)\n}\n\nfunction setThumbElement(index: number, value: Element | ComponentPublicInstance | null): void {\n const previous = thumbElements.get(index)\n if (previous && previous !== value) observer?.unobserve(previous)\n if (!(value instanceof HTMLElement)) {\n thumbElements.delete(index)\n return\n }\n thumbElements.set(index, value)\n observer?.observe(value)\n}\n\nwatch(() => props.viewer, setupThumbnails, { flush: 'post' })\nwatch(() => props.items, setupThumbnails, { flush: 'post' })\nwatch(collapsed, setupThumbnails, { flush: 'post' })\nwatch(\n () => props.activeIndex,\n async (index) => {\n await nextTick()\n listEl.value\n ?.querySelector<HTMLElement>(`[data-nav-slide-index=\"${index}\"]`)\n ?.scrollIntoView({ block: 'nearest' })\n },\n)\n\nonMounted(setupThumbnails)\nonBeforeUnmount(disposeThumbnails)\n</script>\n\n<template>\n <aside class=\"pn\" :class=\"{ 'pn--collapsed': collapsed }\" aria-label=\"幻灯片导航\">\n <button\n v-if=\"collapsed\"\n type=\"button\"\n class=\"pn__reopen\"\n aria-label=\"展开幻灯片导航\"\n title=\"展开幻灯片导航\"\n @click=\"collapsed = false\"\n >\n <Icon name=\"chevronRight\" :size=\"15\" />\n </button>\n\n <template v-else>\n <header class=\"pn__header\">\n <div>\n <strong class=\"pn__title\">幻灯片</strong>\n <span class=\"pn__count\">{{ items.length }} 张</span>\n </div>\n <button\n type=\"button\"\n class=\"pn__collapse\"\n aria-label=\"收起幻灯片导航\"\n title=\"收起幻灯片导航\"\n @click=\"collapsed = true\"\n >\n <Icon name=\"chevronLeft\" :size=\"15\" />\n </button>\n </header>\n\n <nav ref=\"list\" class=\"pn__list\" aria-label=\"幻灯片列表\">\n <button\n v-for=\"item in items\"\n :key=\"item.index\"\n type=\"button\"\n class=\"pn__item\"\n :class=\"{ 'pn__item--active': item.index === activeIndex }\"\n :data-nav-slide-index=\"item.index\"\n :aria-current=\"item.index === activeIndex ? 'page' : undefined\"\n :title=\"`${item.index + 1}. ${item.title}`\"\n @click=\"emit('navigate', item.index)\"\n >\n <span class=\"pn__number\">{{ item.index + 1 }}</span>\n <span class=\"pn__preview-wrap\">\n <span\n :ref=\"(value) => setThumbElement(item.index, value)\"\n class=\"pn__preview\"\n :data-slide-index=\"item.index\"\n aria-hidden=\"true\"\n />\n </span>\n <span class=\"pn__label\">{{ item.title }}</span>\n </button>\n </nav>\n </template>\n </aside>\n</template>\n\n<style scoped>\n.pn {\n position: relative;\n flex: 0 0 142px;\n width: 142px;\n min-width: 0;\n display: flex;\n flex-direction: column;\n border-right: 1px solid var(--border);\n background: color-mix(in srgb, var(--surface) 94%, var(--surface-2));\n transition: flex-basis 160ms ease;\n}\n.pn--collapsed {\n flex-basis: 0;\n width: 0;\n border-right: 0;\n}\n.pn__header {\n min-height: 45px;\n padding: 9px 8px 8px 12px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 6px;\n border-bottom: 1px solid var(--border);\n}\n.pn__title {\n display: block;\n color: var(--text-strong);\n font-size: 12.5px;\n font-weight: 650;\n}\n.pn__count {\n display: block;\n margin-top: 1px;\n color: var(--faint);\n font-size: 10.5px;\n}\n.pn__collapse,\n.pn__reopen {\n border: 0;\n color: var(--muted);\n background: transparent;\n cursor: pointer;\n}\n.pn__collapse {\n width: 27px;\n height: 27px;\n display: grid;\n place-items: center;\n border-radius: 7px;\n}\n.pn__collapse:hover {\n color: var(--text-strong);\n background: var(--bubble);\n}\n.pn__reopen {\n position: absolute;\n z-index: 3;\n top: 12px;\n right: -28px;\n width: 28px;\n height: 36px;\n padding: 0;\n display: grid;\n place-items: center;\n border: 1px solid var(--border-2);\n border-radius: 0 9px 9px 0;\n background: var(--surface);\n box-shadow: 2px 1px 6px rgba(0, 0, 0, 0.12);\n}\n.pn__reopen:hover {\n color: var(--text-strong);\n background: var(--bubble);\n}\n.pn__list {\n flex: 1 1 auto;\n min-height: 0;\n overflow: auto;\n padding: 8px 7px 22px;\n scrollbar-width: thin;\n}\n.pn__item {\n position: relative;\n width: 100%;\n min-width: 0;\n margin: 0 0 8px;\n padding: 5px;\n display: block;\n border: 1px solid transparent;\n border-radius: 8px;\n color: var(--muted);\n background: transparent;\n font: inherit;\n text-align: left;\n cursor: pointer;\n}\n.pn__item:hover {\n color: var(--text-strong);\n background: var(--bubble);\n}\n.pn__item--active {\n border-color: color-mix(in srgb, var(--accent) 70%, transparent);\n color: var(--text-strong);\n background: var(--accent-soft);\n}\n.pn__number {\n position: absolute;\n z-index: 2;\n top: 9px;\n left: 9px;\n min-width: 20px;\n height: 20px;\n padding: 0 5px;\n display: grid;\n place-items: center;\n border-radius: 5px;\n color: #fff;\n background: rgba(20, 20, 20, 0.68);\n font-size: 9.5px;\n font-weight: 650;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);\n backdrop-filter: blur(4px);\n}\n.pn__item--active .pn__number {\n color: #fff;\n background: var(--accent);\n}\n.pn__preview-wrap {\n height: 76px;\n min-width: 0;\n overflow: hidden;\n display: flex;\n align-items: center;\n justify-content: center;\n border: 1px solid var(--border-2);\n border-radius: 4px;\n background: #fff;\n box-shadow: 0 1px 4px rgba(0, 0, 0, 0.12);\n}\n.pn__item--active .pn__preview-wrap {\n border-color: var(--accent);\n box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent) 35%, transparent);\n}\n.pn__preview {\n width: 104px;\n min-height: 1px;\n display: block;\n overflow: hidden;\n pointer-events: none;\n}\n.pn__label {\n min-width: 0;\n margin-top: 5px;\n overflow: hidden;\n display: -webkit-box;\n -webkit-box-orient: vertical;\n -webkit-line-clamp: 2;\n font-size: 10.5px;\n line-height: 1.35;\n}\n\n@container (max-width: 500px) {\n .pn {\n flex-basis: 124px;\n width: 124px;\n }\n .pn--collapsed {\n flex-basis: 0;\n width: 0;\n }\n .pn__preview-wrap {\n height: 66px;\n }\n}\n</style>\n"],"mappings":"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"}
|
|
@@ -2,6 +2,8 @@ import e from "../_virtual/_plugin-vue_export-helper.js";
|
|
|
2
2
|
import t from "./PptxViewer.vue_vue_type_script_setup_true_lang.js";
|
|
3
3
|
/* empty css */
|
|
4
4
|
//#region src/components/PptxViewer.vue
|
|
5
|
-
var n = /*#__PURE__*/ e(t, [["__scopeId", "data-v-
|
|
5
|
+
var n = /*#__PURE__*/ e(t, [["__scopeId", "data-v-72d11ba8"]]);
|
|
6
6
|
//#endregion
|
|
7
7
|
export { n as default };
|
|
8
|
+
|
|
9
|
+
//# sourceMappingURL=PptxViewer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PptxViewer.js","names":[],"sources":["../../src/components/PptxViewer.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { onBeforeUnmount, onMounted, shallowRef, useTemplateRef, watch } from 'vue'\nimport { PptxViewer, RECOMMENDED_ZIP_LIMITS } from '@aiden0z/pptx-renderer'\nimport PptxNavigationPane from '@/components/PptxNavigationPane.vue'\nimport { buildPptxNavigation, type PptxNavigationItem } from '@/lib/pptxNavigation'\n// pdfjs is only used as a fallback to rasterize SmartArt / EMF graphics the native renderer can't draw. The\n// library dynamic-imports these two ESM files inside its OWN web worker, so we hand it servable URLs (Vite's\n// `?url` emits the real assets and returns their URL — root-relative in the same-origin prod build, a dev\n// URL under `pnpm dev`). They're fetched lazily by that worker only when a slide actually needs the fallback.\nimport pdfModuleUrl from 'pdfjs-dist/build/pdf.min.mjs?url'\nimport pdfWorkerUrl from 'pdfjs-dist/build/pdf.worker.min.mjs?url'\n\n/**\n * Side-viewer PowerPoint (`.pptx`/`.pptm`/`.ppsx`) renderer. Fetches the deck bytes from the download URL and\n * renders the OOXML PresentationML to HTML/SVG entirely in-browser via @aiden0z/pptx-renderer\n * (https://github.com/aiden0z/pptx-renderer) — a vertically scrollable list of slides, each scaled to fit the\n * pane (`fitMode: 'contain'`, the library default). No CDN / network beyond the file fetch (+ the lazy pdfjs\n * fallback above): the renderer bundles its own jszip + echarts (charts) and inlines media. The library writes\n * inline styles only, so nothing leaks onto the page.\n *\n * Two entry shapes mirror the other viewers (docx/xlsx/zip): a real download URL for a held attachment, or a\n * blob URL minted by the archive browsers from already-extracted bytes.\n */\nconst props = defineProps<{\n /** Download/blob URL of the .pptx bytes. */\n url?: string\n name: string\n size?: number\n}>()\n\ntype Status = 'loading' | 'ready' | 'error'\nconst status = shallowRef<Status>('loading')\nconst errorMsg = shallowRef('')\n// Keep the scroll viewport separate from the renderer mount: `fitMode: 'contain'` measures the mount's\n// `clientWidth`, so putting visual padding on that same element makes the slide consume the padded width and\n// overflow horizontally. Both stay mounted while loading so `open()` always sees real dimensions.\nconst scrollEl = useTemplateRef<HTMLElement>('scrollEl')\nconst docEl = useTemplateRef<HTMLElement>('docEl')\nconst navViewer = shallowRef<PptxViewer | null>(null)\nconst navItems = shallowRef<PptxNavigationItem[]>([])\nconst activeSlide = shallowRef(0)\nconst navCollapsed = shallowRef(false)\n// Live viewer instance — destroyed on re-render / unmount (it holds echarts instances, observers, and its\n// pdfjs worker, none of which GC on their own).\nlet viewer: PptxViewer | null = null\nlet navigationLock: number | null = null\nlet navigationLockTimer: ReturnType<typeof setTimeout> | undefined\n\nfunction disposeViewer(): void {\n navigationLock = null\n if (navigationLockTimer) clearTimeout(navigationLockTimer)\n navigationLockTimer = undefined\n navViewer.value = null\n navItems.value = []\n if (viewer) {\n try {\n viewer.destroy()\n } catch {\n /* already torn down — ignore */\n }\n viewer = null\n }\n}\n\nasync function render(): Promise<void> {\n const el = docEl.value\n const scroll = scrollEl.value\n if (!props.url || !el || !scroll) return\n status.value = 'loading'\n errorMsg.value = ''\n disposeViewer()\n el.innerHTML = '' // drop any prior render (e.g. on retry / switching files)\n const url = props.url\n try {\n const res = await fetch(url)\n if (!res.ok) throw new Error(`HTTP ${res.status}`)\n const buf = await res.arrayBuffer()\n // Stale guard: a newer render may have started while we awaited the fetch.\n if (props.url !== url || docEl.value !== el || scrollEl.value !== scroll) return\n const v = await PptxViewer.open(buf, el, {\n zipLimits: RECOMMENDED_ZIP_LIMITS,\n scrollContainer: scroll, // the outer viewport drives which slides the windowed list mounts\n listOptions: { windowed: true }, // virtualize: only mount slides near the viewport (large decks stay fast)\n pdfjs: { moduleUrl: pdfModuleUrl, workerUrl: pdfWorkerUrl },\n onSlideChange: (index) => {\n if (navigationLock !== null && index !== navigationLock) return\n if (props.url === url) activeSlide.value = index\n },\n })\n // Stale guard again: the file may have changed while parse/render ran — tear the new viewer down.\n if (props.url !== url || docEl.value !== el || scrollEl.value !== scroll) {\n try {\n v.destroy()\n } catch {\n /* ignore */\n }\n return\n }\n viewer = v\n navViewer.value = v\n navItems.value = v.presentationData ? buildPptxNavigation(v.presentationData) : []\n activeSlide.value = v.currentSlideIndex\n status.value = 'ready'\n } catch (e) {\n errorMsg.value = e instanceof Error ? e.message : String(e)\n status.value = 'error'\n }\n}\n\nfunction navigateToSlide(index: number): void {\n navigationLock = index\n activeSlide.value = index\n if (navigationLockTimer) clearTimeout(navigationLockTimer)\n navigationLockTimer = setTimeout(() => {\n navigationLock = null\n navigationLockTimer = undefined\n }, 1000)\n void viewer?.goToSlide(index, { behavior: 'smooth', block: 'start' })\n}\n\nonMounted(render)\nwatch(() => props.url, render)\nonBeforeUnmount(() => {\n disposeViewer()\n if (docEl.value) docEl.value.innerHTML = ''\n})\n</script>\n\n<template>\n <div class=\"pv\">\n <PptxNavigationPane\n v-if=\"status === 'ready' && navViewer\"\n v-model:collapsed=\"navCollapsed\"\n :viewer=\"navViewer\"\n :items=\"navItems\"\n :active-index=\"activeSlide\"\n @navigate=\"navigateToSlide\"\n />\n <div class=\"pv__stage\">\n <!-- The padded scroll viewport wraps a padding-free mount whose clientWidth is the real slide width. -->\n <div ref=\"scrollEl\" class=\"pv__scroll\">\n <div ref=\"docEl\" class=\"pv__doc\" />\n </div>\n <!-- Loading / error as an overlay so the container underneath keeps its dimensions. -->\n <div v-if=\"status === 'loading'\" class=\"pv__overlay\">\n <p class=\"pv__msg\">正在渲染 PPT…</p>\n </div>\n <div v-else-if=\"status === 'error'\" class=\"pv__overlay\">\n <div class=\"pv__msg pv__msg--error\">\n <p>渲染失败:{{ errorMsg }}</p>\n <button type=\"button\" class=\"pv__retry\" @click=\"render\">重试</button>\n </div>\n </div>\n </div>\n </div>\n</template>\n\n<style scoped>\n.pv {\n flex: 1 1 auto;\n min-height: 0;\n display: flex;\n flex-direction: row;\n background: var(--surface);\n container-type: inline-size;\n}\n.pv__stage {\n position: relative;\n flex: 1 1 auto;\n min-width: 0;\n min-height: 0;\n display: flex;\n flex-direction: column;\n}\n/* Scroll region holding the rendered slides. The deck keeps its native appearance (the library scales each\n * slide to fit this width); we only tint the surrounding \"desk\" to the app surface so it sits naturally in\n * both light and dark themes. The renderer writes inline styles, so no :deep overrides are needed. */\n.pv__scroll {\n flex: 1 1 auto;\n min-height: 0;\n overflow: auto;\n overscroll-behavior: contain;\n background: var(--surface-2);\n padding: 16px 14px 40px;\n}\n.pv__doc {\n width: 100%;\n min-width: 0;\n}\n/* Overlay covers the (empty) container while loading or on error, without unmounting it. */\n.pv__overlay {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: flex-start;\n justify-content: flex-start;\n background: var(--surface);\n}\n.pv__msg {\n padding: 20px 16px;\n color: var(--muted);\n font-size: 13px;\n}\n.pv__msg--error {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 10px;\n color: var(--danger);\n}\n.pv__retry {\n font-family: inherit;\n font-size: 12.5px;\n color: var(--text);\n background: var(--surface);\n border: 1px solid var(--border-2);\n border-radius: 8px;\n padding: 5px 12px;\n cursor: pointer;\n}\n.pv__retry:hover {\n border-color: var(--accent);\n color: var(--accent);\n}\n</style>\n"],"mappings":""}
|