@l1yp/file-viewer 0.1.0 → 0.1.2
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 +2 -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 +9 -0
- package/dist/components/DocxNavigationPane.js.map +1 -0
- package/dist/components/DocxNavigationPane.vue.d.ts +23 -0
- package/dist/components/DocxNavigationPane.vue_vue_type_script_setup_true_lang.js +98 -0
- package/dist/components/DocxNavigationPane.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/DocxViewer.js +3 -1
- package/dist/components/DocxViewer.js.map +1 -0
- package/dist/components/DocxViewer.vue_vue_type_script_setup_true_lang.js +72 -37
- 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.d.ts +5 -0
- package/dist/components/FileViewerBody.vue_vue_type_script_setup_true_lang.js +250 -247
- 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 +2 -0
- 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/PdfViewer.js +2 -0
- package/dist/components/PdfViewer.js.map +1 -0
- package/dist/components/PdfViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/PdfViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/PptxNavigationPane.js +9 -0
- package/dist/components/PptxNavigationPane.js.map +1 -0
- package/dist/components/PptxNavigationPane.vue.d.ts +20 -0
- package/dist/components/PptxNavigationPane.vue_vue_type_script_setup_true_lang.js +120 -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 +71 -39
- 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 +3 -1
- package/dist/components/XlsxViewer.js.map +1 -0
- package/dist/components/XlsxViewer.vue_vue_type_script_setup_true_lang.js +301 -304
- 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 +3 -1
- package/dist/components/ZipBrowser.js.map +1 -0
- package/dist/components/ZipBrowser.vue.d.ts +3 -1
- package/dist/components/ZipBrowser.vue_vue_type_script_setup_true_lang.js +154 -122
- package/dist/components/ZipBrowser.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/ZoomImage.js +2 -0
- package/dist/components/ZoomImage.js.map +1 -0
- package/dist/components/ZoomImage.vue_vue_type_script_setup_true_lang.js +2 -0
- 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.d.ts +21 -0
- package/dist/composables/useElementNavigation.js +63 -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.d.ts +49 -0
- package/dist/lib/archiveHtml.js +377 -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.d.ts +31 -0
- package/dist/lib/docxNavigation.js +65 -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/pkcs12.js +2 -0
- package/dist/lib/pkcs12.js.map +1 -0
- package/dist/lib/pptxNavigation.d.ts +7 -0
- package/dist/lib/pptxNavigation.js +29 -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":"XlsxViewer.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../src/components/XlsxViewer.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, nextTick, onMounted, onBeforeUnmount, ref, watch } from 'vue'\nimport { numToCol, parseXlsx, type XlsxCell, type XlsxModel, type XlsxSheet } from '@/lib/xlsx'\nimport { parseXlsxCharts, type XlsxChart } from '@/lib/xlsxCharts'\nimport XlsxChartView from '@/components/XlsxChart.vue'\n\n/**\n * Side-viewer spreadsheet (`.xlsx`/`.xlsm`) renderer. Fetches the workbook bytes and parses them entirely\n * in-browser via exceljs (see lib/xlsx.ts), then renders each worksheet as a plain HTML `<table>` — merged\n * cells, column widths, font sizes and the common cell styling carried over so the preview reads like the\n * original. On top of the static table it adds the spreadsheet interactions: A/B/C… + row-number headers,\n * collapsed hidden rows/columns with click-to-expand, frozen panes (with a 2px boundary rule), drag-to-resize\n * columns/rows, drag-to-select a cell range (with a status-bar sum/avg), and embedded charts rendered as SVG\n * (see lib/xlsxCharts.ts) overlaid at their drawing anchor. No CDN / network beyond the file fetch; a bottom\n * tab bar switches sheets.\n *\n * Two entry shapes mirror the other viewers (DICOM/docx/zip): a real download URL for a held attachment, or\n * a blob URL minted by the archive browsers from already-extracted bytes.\n */\nconst props = defineProps<{\n /** Download/blob URL of the .xlsx bytes. */\n url?: string\n name: string\n size?: number\n}>()\n\ntype Status = 'loading' | 'ready' | 'error'\nconst status = ref<Status>('loading')\nconst errorMsg = ref('')\nconst model = ref<XlsxModel | null>(null)\nconst charts = ref<Map<string, XlsxChart[]>>(new Map())\nconst active = ref(0)\n\nconst sheets = computed(() => model.value?.sheets ?? [])\nconst sheet = computed<XlsxSheet | null>(() => sheets.value[active.value] ?? null)\n\n// Geometry constants. Gutter width and header height match the CSS; auto-sized rows use a nominal height.\nconst GUTTER_W = 42\nconst HEADER_H = 22\nconst DEFAULT_ROW_H = 20\nconst MIN_COL_W = 24\nconst MIN_ROW_H = 14\n\n// ---- per-sheet UI state (resize overrides + which hidden runs the user expanded) --------------------------\ninterface SheetUi {\n colW: Record<number, number> // 1-based col → overridden width px\n rowH: Record<number, number> // 1-based row → overridden height px\n revealCol: Record<number, boolean> // 1-based col forced visible\n revealRow: Record<number, boolean> // 1-based row forced visible\n revealAll: boolean\n}\nconst EMPTY_UI: SheetUi = { colW: {}, rowH: {}, revealCol: {}, revealRow: {}, revealAll: false }\nconst perSheet = ref<Record<number, SheetUi>>({})\nconst curUi = computed<SheetUi>(() => perSheet.value[active.value] ?? EMPTY_UI)\nfunction ensureUi(): SheetUi {\n let s = perSheet.value[active.value]\n if (!s) {\n s = { colW: {}, rowH: {}, revealCol: {}, revealRow: {}, revealAll: false }\n perSheet.value[active.value] = s\n }\n return s\n}\nconst hasOverrides = computed(() => {\n const u = curUi.value\n return (\n u.revealAll ||\n Object.keys(u.colW).length > 0 ||\n Object.keys(u.rowH).length > 0 ||\n Object.keys(u.revealCol).length > 0 ||\n Object.keys(u.revealRow).length > 0\n )\n})\nfunction resetUi(): void {\n perSheet.value[active.value] = {\n colW: {},\n rowH: {},\n revealCol: {},\n revealRow: {},\n revealAll: false,\n }\n scheduleMeasure()\n}\nfunction toggleRevealAll(): void {\n ensureUi().revealAll = !curUi.value.revealAll\n scheduleMeasure()\n}\n\n// ---- effective sizes / visibility ------------------------------------------------------------------------\nconst colCount = computed(() => sheet.value?.colWidths.length ?? 0)\n\nfunction colHidden(c: number): boolean {\n return !!sheet.value?.colHidden[c - 1] && !curUi.value.revealAll && !curUi.value.revealCol[c]\n}\nfunction rowHiddenAt(i: number): boolean {\n return !!sheet.value?.rows[i]?.hidden && !curUi.value.revealAll && !curUi.value.revealRow[i + 1]\n}\nfunction effColW(c: number): number {\n if (colHidden(c)) return 0\n return curUi.value.colW[c] ?? sheet.value?.colWidths[c - 1] ?? 80\n}\nfunction effRowH(i: number): number {\n if (rowHiddenAt(i)) return 0\n return curUi.value.rowH[i + 1] ?? sheet.value?.rows[i]?.height ?? DEFAULT_ROW_H\n}\n\nconst colLetters = computed(() => {\n const out: string[] = []\n for (let c = 1; c <= colCount.value; c++) out.push(numToCol(c))\n return out\n})\nconst frozenCols = computed(() => sheet.value?.frozenCols ?? 0)\nconst frozenRows = computed(() => sheet.value?.frozenRows ?? 0)\n\n// Cumulative left edge (px) by 1-based column number; index colCount+1 = right edge of the grid.\nconst colLefts = computed(() => {\n const arr: number[] = []\n let acc = GUTTER_W\n for (let c = 1; c <= colCount.value; c++) {\n arr[c] = acc\n acc += effColW(c)\n }\n arr[colCount.value + 1] = acc\n return arr\n})\n// Cumulative top edge (px) by 1-based row number; index rows.length+1 = bottom edge.\nconst rowTops = computed(() => {\n const rows = sheet.value?.rows ?? []\n const arr: number[] = []\n let acc = HEADER_H\n for (let i = 0; i < rows.length; i++) {\n arr[i + 1] = acc\n acc += effRowH(i)\n }\n arr[rows.length + 1] = acc\n return arr\n})\n\n// The cumulative arrays above ASSUME a row height for auto-sized rows, which drifts from the real layout the\n// further down you go — fine as a pre-paint fallback, but it makes overlays (selection box, charts) miss their\n// cells. Once the table is laid out, measure its rendered column widths / row heights (including sub-pixels) and\n// accumulate the natural edges ourselves. Do NOT read offsetLeft/offsetTop from the sticky headers: while\n// scrolled, a frozen header can report its stuck position instead of its natural table position, and feeding that\n// value back into `left`/`top` makes the frozen pane drift permanently after a hide/reveal or resize. Do NOT use\n// offsetWidth/offsetHeight either: they round to integers, so their per-cell error accumulates and makes selection\n// and chart overlays drift progressively farther from cells toward the sheet's bottom-right.\nconst tableEl = ref<HTMLTableElement | null>(null)\nconst scrollEl = ref<HTMLElement | null>(null)\nconst measuredCols = ref<number[]>([])\nconst measuredRows = ref<number[]>([])\nfunction measure(): void {\n const t = tableEl.value\n if (!t) return\n const headRow = t.tHead?.rows[0]\n const heads = headRow?.cells // [corner, col1 … colN]\n const cols: number[] = []\n let left = heads?.[0]?.getBoundingClientRect().width ?? GUTTER_W\n for (let c = 1; c <= colCount.value; c++) {\n cols[c] = left\n left += heads?.[c]?.getBoundingClientRect().width ?? effColW(c)\n }\n cols[colCount.value + 1] = left\n\n const bodyRows = t.tBodies[0]?.rows\n const rows: number[] = []\n let top = headRow?.getBoundingClientRect().height ?? HEADER_H\n const rowCount = sheet.value?.rows.length ?? 0\n for (let i = 0; i < rowCount; i++) {\n rows[i + 1] = top\n top += bodyRows?.[i]?.getBoundingClientRect().height ?? effRowH(i)\n }\n rows[rowCount + 1] = top\n measuredCols.value = cols\n measuredRows.value = rows\n}\nfunction scheduleMeasure(): void {\n // Any caller may have just changed the table geometry. Drop the old edges synchronously so the DOM patch uses\n // the source-derived fallback instead of briefly laying out sticky cells with another geometry's offsets.\n measuredCols.value = []\n measuredRows.value = []\n nextTick(measure)\n}\nconst colLeftAt = (c: number): number => measuredCols.value[c] ?? colLefts.value[c]\nconst rowTopAt = (r: number): number => measuredRows.value[r] ?? rowTops.value[r]\n\n// ---- hidden-run expanders --------------------------------------------------------------------------------\ninterface HiddenRun {\n start: number\n end: number\n count: number\n}\n// For each visible column, the run of hidden columns immediately preceding it (so a chip can expand them).\nconst colExpanders = computed(() => {\n const map = new Map<number, HiddenRun>()\n const s = sheet.value\n if (!s) return map\n let runStart = 0\n for (let c = 1; c <= colCount.value; c++) {\n if (colHidden(c)) {\n if (!runStart) runStart = c\n } else {\n if (runStart) map.set(c, { start: runStart, end: c - 1, count: c - runStart })\n runStart = 0\n }\n }\n return map\n})\n// For each visible row index, the run of hidden rows immediately preceding it.\nconst rowExpanders = computed(() => {\n const map = new Map<number, HiddenRun>()\n const rows = sheet.value?.rows ?? []\n let runStart = 0\n for (let i = 0; i < rows.length; i++) {\n if (rowHiddenAt(i)) {\n if (!runStart) runStart = i + 1\n } else {\n if (runStart) map.set(i, { start: runStart, end: i, count: i + 1 - runStart })\n runStart = 0\n }\n }\n return map\n})\nfunction revealColRun(run: HiddenRun): void {\n const u = ensureUi()\n for (let c = run.start; c <= run.end; c++) u.revealCol[c] = true\n scheduleMeasure()\n}\nfunction revealRowRun(run: HiddenRun): void {\n const u = ensureUi()\n for (let r = run.start; r <= run.end; r++) u.revealRow[r] = true\n scheduleMeasure()\n}\n\n// ---- Excel-style text spill ------------------------------------------------------------------------------\n// In Excel a cell whose content is wider than its column \"spills\" over the adjacent EMPTY cells (until a\n// non-empty cell or the data edge), showing in full; it only truncates when even that run isn't wide enough.\n// Direction follows alignment: left/general text spills right, right-aligned (numbers) spills left, centered\n// spills both. An HTML `<td>` clips to its own box, so without this every long title/label shows \"…\". We\n// precompute, per spillable cell, the total width (own column + the empty run) and how much of that run is to\n// the LEFT; the renderer caps an inner span to the total width (its own ellipsis only past the whole run),\n// shifts it left by the left run, and lets the cell overflow. Keyed `rowIdx:col`; recomputes on width changes.\ninterface Spill {\n w: number\n left: number\n}\n// Accurately measure a cell's rendered text width (px) via a shared canvas, using the same font stack as the\n// table (Calibri first, see .xv__table) so the fit/overflow decision matches what actually paints. Falls back\n// to a rough CJK≈1em / ASCII≈0.6em estimate when there's no DOM (SSR / pnpm smoke).\nlet measureCtx: CanvasRenderingContext2D | null | undefined\nfunction measureTextPx(text: string, fontPx: number, bold?: boolean): number {\n if (typeof document === 'undefined') {\n let w = 0\n for (const ch of text) w += ch.codePointAt(0)! > 0x2e7f ? fontPx : fontPx * 0.6\n return w\n }\n if (measureCtx === undefined) measureCtx = document.createElement('canvas').getContext('2d')\n const ctx = measureCtx\n if (!ctx) {\n let w = 0\n for (const ch of text) w += ch.codePointAt(0)! > 0x2e7f ? fontPx : fontPx * 0.6\n return w\n }\n ctx.font = `${bold ? '700 ' : ''}${fontPx}px Calibri, 'Segoe UI', system-ui, sans-serif`\n return ctx.measureText(text).width\n}\n// HTML chrome each spillable cell reserves on top of its content (padding 4px ×2 + ~1px gridline), matching\n// colWidthToPx's +9; text wider than (column − this) overflows and earns a spill.\nconst CELL_CHROME = 9\nconst spillMap = computed(() => {\n const m = new Map<string, Spill>()\n const rows = sheet.value?.rows ?? []\n for (let r = 0; r < rows.length; r++) {\n const cells = rows[r].cells\n for (let idx = 0; idx < cells.length; idx++) {\n const cell = cells[idx]\n if (!cell.text.trim() || cell.wrap) continue\n if (colHidden(cell.col)) continue\n // Merged cells (colspan/rowspan > 1) never spill: Excel/WPS clip or wrap WITHIN the merge box, and the\n // td already spans the merged columns. `own` below is a SINGLE column's width (the merge's left edge),\n // so treating a merge as spillable both mis-measures it (single column < merged width → bogus \"doesn't\n // fit\") and then shifts it: cellStyle forces text-align:left and spillStyle hands the inner span a\n // negative margin-left, pulling the text off-centre across the empty cells to its left. Let the colspan'd\n // td centre/clip its own text instead.\n if (cell.colSpan > 1 || cell.rowSpan > 1) continue\n const own = effColW(cell.col)\n const contentW = measureTextPx(cell.text, cell.fontSize ?? 16, cell.bold)\n // Fits its own column → no spill (don't shift short centred/right labels across empty neighbours).\n if (contentW <= own - CELL_CHROME) continue\n // Width of the run of empty cells to the right and to the left (until content / a merge gap / data edge).\n let rightW = 0\n let nextCol = cell.col + cell.colSpan\n for (let k = idx + 1; k < cells.length; k++) {\n const nc = cells[k]\n if (nc.col !== nextCol || nc.text.trim() !== '') break\n nextCol = nc.col + nc.colSpan\n if (!colHidden(nc.col)) rightW += effColW(nc.col) // hidden empties keep the run but add no width\n }\n let leftW = 0\n let prevEnd = cell.col\n for (let k = idx - 1; k >= 0; k--) {\n const pc = cells[k]\n if (pc.col + pc.colSpan !== prevEnd || pc.text.trim() !== '') break\n prevEnd = pc.col\n if (!colHidden(pc.col)) leftW += effColW(pc.col)\n }\n // Direction follows alignment, mirroring Excel: left/general spills right, right spills left, centre\n // spills BOTH and stays centred on its own cell (only overflowing as far as the run on each side allows —\n // so it doesn't slide across a wide empty block). `w` caps the inner span (its own ellipsis past the run);\n // `left` is how far the span starts left of the column (negative margin), so the text sits correctly.\n let w: number\n let left: number\n if (cell.align === 'right') {\n w = Math.min(contentW, own + leftW)\n left = w - own // overflow goes entirely left\n } else if (cell.align === 'center') {\n w = Math.min(contentW, own + leftW + rightW)\n const overflow = w - own\n // Centre on the cell: give half the overflow to the right (capped by its run), the rest to the left;\n // if the left run is the shorter one, clamp there and the right side absorbs the remainder (it always\n // can, since w ≤ own + leftW + rightW). The span width `w` then sits centred over [left … right].\n left = Math.min(leftW, overflow - Math.min(rightW, overflow / 2))\n } else {\n w = Math.min(contentW, own + rightW)\n left = 0 // left/general anchors at the column's left edge, overflow goes right\n }\n if (w > own + 0.5) m.set(`${r}:${cell.col}`, { w, left })\n }\n }\n return m\n})\n// Inline style for a spillable cell's inner span: cap it to the run width and pull it left over the left run\n// (text-align inside the span — inherited from the cell — keeps left/centre/right behaviour within that box).\nfunction spillStyle(cell: XlsxCell, rowIdx: number): Record<string, string> | undefined {\n const s = spillMap.value.get(`${rowIdx}:${cell.col}`)\n if (!s) return undefined\n // The cell (td) is forced to text-align:left for spill (see cellStyle) so this inline-block anchors at the\n // column's left edge; margin-left pulls it over the left run, and the span carries the real alignment so\n // the text sits left/centre/right within the whole [left run … right run] box.\n return { maxWidth: `${s.w}px`, marginLeft: `${-s.left}px`, textAlign: cell.align ?? 'left' }\n}\n\n// ---- frozen-pane edge detection (a 2px rule on the boundary, so the pinned region reads clearly) ----------\nfunction frzEdgeCol(c: number): boolean {\n return frozenCols.value > 0 && c === frozenCols.value\n}\nfunction frzEdgeRowIdx(i: number): boolean {\n return frozenRows.value > 0 && i + 1 === frozenRows.value\n}\n\n// ---- per-cell style + classes ----------------------------------------------------------------------------\nfunction cellStyle(cell: XlsxCell, rowIdx: number): Record<string, string | undefined> {\n const r = rowIdx + 1\n const fcol = cell.col <= frozenCols.value\n const frow = r <= frozenRows.value\n const style: Record<string, string | undefined> = {\n color: cell.color,\n // Use backgroundColor (longhand), NOT the `background` shorthand: a selected cell's highlight is painted by\n // .xv__cell--sel as a `background-image` gradient layered ON TOP of this colour, and an inline `background`\n // shorthand would reset background-image to none (inline wins over the class), wiping the highlight —\n // notably on frozen cells, which always set a colour here to stay opaque.\n backgroundColor: cell.bg,\n // Spill cells anchor their inner span at the column's left edge (the span then re-applies the real\n // alignment), so force the td to left here regardless of the source alignment.\n textAlign: spillMap.value.has(`${rowIdx}:${cell.col}`) ? 'left' : cell.align,\n verticalAlign: cell.valign,\n fontSize: cell.fontSize ? `${cell.fontSize}px` : undefined,\n }\n if (fcol) style.left = `${colLeftAt(cell.col)}px`\n if (frow) style.top = `${rowTopAt(r)}px`\n if (fcol || frow) style.backgroundColor = cell.bg || '#fff'\n return style\n}\nfunction gutterStyle(rowIdx: number): Record<string, string | undefined> {\n return rowIdx + 1 <= frozenRows.value ? { top: `${rowTopAt(rowIdx + 1)}px` } : {}\n}\n// A column-letter header over a frozen column pins horizontally too (sticky `left`), so it stays above its\n// frozen cells when scrolling right — the column-axis mirror of gutterStyle's vertical pin for frozen rows.\n// (The `.xv__colhead` rule already pins it to the top; this adds the left offset for the frozen-column case.)\nfunction colHeadStyle(c: number): Record<string, string | undefined> {\n return c <= frozenCols.value ? { left: `${colLeftAt(c)}px` } : {}\n}\n\n// ---- column / row resize (drag the header edges) ---------------------------------------------------------\ninterface ResizeState {\n kind: 'col' | 'row'\n index: number // col number or row number\n startPos: number\n startSize: number\n}\nlet resizing: ResizeState | null = null\nfunction onColResizeStart(c: number, e: MouseEvent): void {\n resizing = { kind: 'col', index: c, startPos: e.clientX, startSize: effColW(c) }\n document.body.style.cursor = 'col-resize'\n}\nfunction onRowResizeStart(rowNum: number, e: MouseEvent): void {\n resizing = { kind: 'row', index: rowNum, startPos: e.clientY, startSize: effRowH(rowNum - 1) }\n document.body.style.cursor = 'row-resize'\n}\nfunction onResizeMove(e: MouseEvent): void {\n if (!resizing) return\n const u = ensureUi()\n if (resizing.kind === 'col') {\n u.colW[resizing.index] = Math.max(\n MIN_COL_W,\n Math.round(resizing.startSize + (e.clientX - resizing.startPos)),\n )\n } else {\n u.rowH[resizing.index] = Math.max(\n MIN_ROW_H,\n Math.round(resizing.startSize + (e.clientY - resizing.startPos)),\n )\n }\n}\nfunction endResize(): void {\n if (resizing) {\n resizing = null\n document.body.style.cursor = ''\n scheduleMeasure()\n }\n}\n\n// ---- drag-select cell ranges (multi-range, Excel-style) ---------------------------------------------------\ninterface CellRef {\n r: number // 0-based row index\n c: number // 1-based column number\n}\n// A selection range: anchor `a` (where it started) + focus `f` (the moving end). `kind` records how it was\n// made — a 'cell' range merge-expands its bounds (selecting part of a merge selects the whole box), but a\n// whole-column / whole-row select (a header click/drag) deliberately does NOT, so clicking the header of a\n// column that crosses a horizontal merge highlights just that column, not the merge's neighbours.\ntype RangeKind = 'cell' | 'col' | 'row'\ninterface SelRange {\n a: CellRef\n f: CellRef\n kind: RangeKind\n}\n// All active ranges. Empty = nothing selected. The LAST range is the \"active\" one — its anchor is the name-box\n// cell, and Shift / drag extend it; Ctrl-click pushes another range for a discontiguous (multi-block) selection.\nconst selection = ref<SelRange[]>([])\nlet selecting = false\nlet dragKind: RangeKind = 'cell'\nfunction activeRange(): SelRange | null {\n return selection.value[selection.value.length - 1] ?? null\n}\nfunction lastRowIdx(): number {\n return Math.max(0, (sheet.value?.rows.length ?? 1) - 1)\n}\nfunction lastColNum(): number {\n return Math.max(1, colCount.value)\n}\n// The cell the user double-clicked to \"enter\" — only that cell allows native text selection (so a normal drag\n// highlights a range of cells instead of selecting text across them, like a spreadsheet).\nconst editCell = ref<CellRef | null>(null)\nfunction isEditCell(r: number, c: number): boolean {\n return !!editCell.value && editCell.value.r === r && editCell.value.c === c\n}\n// Start a selection gesture (shared by cell + header). Plain → replace with one range; Shift → re-anchor the\n// active range at `extendAnchor` and extend to `focus` (keeps other ranges); Ctrl/Cmd → push a new range.\nfunction beginSelect(\n e: MouseEvent,\n kind: RangeKind,\n anchor: CellRef,\n focus: CellRef,\n extendAnchor: CellRef,\n): void {\n editCell.value = null\n if (e.shiftKey && selection.value.length) {\n const sel = selection.value.slice()\n sel[sel.length - 1] = { a: extendAnchor, f: focus, kind }\n selection.value = sel\n } else if (e.ctrlKey || e.metaKey) {\n selection.value = [...selection.value, { a: anchor, f: focus, kind }]\n } else {\n selection.value = [{ a: anchor, f: focus, kind }]\n }\n selecting = true\n dragKind = kind\n // Focus the grid so the arrow keys drive the active cell straight after a click (no extra tab needed).\n scrollEl.value?.focus({ preventScroll: true })\n}\nfunction onCellDown(rowIdx: number, col: number, e: MouseEvent): void {\n if (e.button !== 0) return\n // Already in this cell's text-select mode → let the browser handle native selection (don't start a drag).\n if (isEditCell(rowIdx, col)) return\n const ref = { r: rowIdx, c: col }\n beginSelect(e, 'cell', ref, ref, activeRange()?.a ?? ref)\n}\nfunction onCellDblClick(rowIdx: number, col: number): void {\n editCell.value = { r: rowIdx, c: col }\n selection.value = [{ a: { r: rowIdx, c: col }, f: { r: rowIdx, c: col }, kind: 'cell' }]\n}\n// Extend the active range to a hovered cell/header while dragging. The focus respects the drag kind, so a\n// column-header drag keeps spanning full rows (only the focus column moves) and a row-header drag full columns.\nfunction extendActive(rowIdx: number, col: number): void {\n const sel = selection.value\n if (!sel.length) return\n const last = sel[sel.length - 1]\n let f: CellRef\n if (dragKind === 'col') f = { r: lastRowIdx(), c: col }\n else if (dragKind === 'row') f = { r: rowIdx, c: lastColNum() }\n else f = { r: rowIdx, c: col }\n const next = sel.slice()\n next[next.length - 1] = { a: last.a, f, kind: dragKind }\n selection.value = next\n}\nfunction onCellEnter(rowIdx: number, col: number): void {\n if (selecting) extendActive(rowIdx, col)\n}\nfunction endSelect(): void {\n selecting = false\n}\n// Click / drag a column letter to select whole column(s); Shift extends, Ctrl adds a disjoint column block.\nfunction onColHeadDown(c: number, e: MouseEvent): void {\n if (e.button !== 0) return\n beginSelect(e, 'col', { r: 0, c }, { r: lastRowIdx(), c }, { r: 0, c: activeRange()?.a.c ?? c })\n}\nfunction onColHeadEnter(c: number): void {\n if (selecting && dragKind === 'col') extendActive(0, c)\n}\n// Click / drag a row number to select whole row(s); Shift extends, Ctrl adds a disjoint row block.\nfunction onRowHeadDown(rowIdx: number, e: MouseEvent): void {\n if (e.button !== 0) return\n beginSelect(\n e,\n 'row',\n { r: rowIdx, c: 1 },\n { r: rowIdx, c: lastColNum() },\n { r: activeRange()?.a.r ?? rowIdx, c: 1 },\n )\n}\nfunction onRowHeadEnter(rowIdx: number): void {\n if (selecting && dragKind === 'row') extendActive(rowIdx, 1)\n}\n\n// ---- keyboard navigation (the grid is focusable; arrows move the active cell, Shift extends the range) -----\n// Step the row index by `dir`, skipping hidden rows; stays put at the edge.\nfunction stepRow(r: number, dir: number): number {\n const len = sheet.value?.rows.length ?? 0\n let i = r\n for (let n = 0; n < len; n++) {\n i += dir\n if (i < 0 || i >= len) return r\n if (!rowHiddenAt(i)) return i\n }\n return r\n}\n// Step the (1-based) column by `dir`, skipping hidden columns; stays put at the edge.\nfunction stepCol(c: number, dir: number): number {\n const n = colCount.value\n let i = c\n for (let s = 0; s < n; s++) {\n i += dir\n if (i < 1 || i > n) return c\n if (!colHidden(i)) return i\n }\n return c\n}\n// Scroll the grid so the cell at (rowIdx, col) is fully visible, allowing for the sticky gutter/header and any\n// frozen panes pinned over the scroll area. Runs after the DOM settles so measured edges are current.\nfunction scrollCellIntoView(rowIdx: number, col: number): void {\n nextTick(() => {\n const el = scrollEl.value\n if (!el) return\n const left = colLeftAt(col)\n const right = colLeftAt(col + 1)\n const top = rowTopAt(rowIdx + 1)\n const bottom = rowTopAt(rowIdx + 2)\n if (left == null || right == null || top == null || bottom == null) return\n let frozenW = 0\n for (let c = 1; c <= frozenCols.value; c++) frozenW += effColW(c)\n let frozenH = 0\n for (let i = 0; i < frozenRows.value; i++) frozenH += effRowH(i)\n const padL = GUTTER_W + frozenW\n const padT = HEADER_H + frozenH\n if (left - padL < el.scrollLeft) el.scrollLeft = left - padL\n else if (right > el.scrollLeft + el.clientWidth) el.scrollLeft = right - el.clientWidth\n if (top - padT < el.scrollTop) el.scrollTop = top - padT\n else if (bottom > el.scrollTop + el.clientHeight) el.scrollTop = bottom - el.clientHeight\n })\n}\nfunction onGridKeyDown(e: KeyboardEvent): void {\n // While a cell is in text-edit mode, leave the keyboard to the browser (only Escape exits the edit).\n if (editCell.value) {\n if (e.key === 'Escape') {\n editCell.value = null\n e.preventDefault()\n }\n return\n }\n const arrows: Record<string, [number, number]> = {\n ArrowUp: [-1, 0],\n ArrowDown: [1, 0],\n ArrowLeft: [0, -1],\n ArrowRight: [0, 1],\n }\n // Keyboard nav drives a single active cell/range (collapsing any multi-block Ctrl selection), like Excel.\n const act = activeRange()\n if (e.key in arrows) {\n e.preventDefault()\n if (!act) {\n selection.value = [{ a: { r: 0, c: 1 }, f: { r: 0, c: 1 }, kind: 'cell' }]\n scrollCellIntoView(0, 1)\n return\n }\n const [dr, dc] = arrows[e.key]\n const ref = e.shiftKey ? act.f : act.a\n const nr = dr ? stepRow(ref.r, dr) : ref.r\n const nc = dc ? stepCol(ref.c, dc) : ref.c\n selection.value = e.shiftKey\n ? [{ a: act.a, f: { r: nr, c: nc }, kind: 'cell' }]\n : [{ a: { r: nr, c: nc }, f: { r: nr, c: nc }, kind: 'cell' }]\n scrollCellIntoView(nr, nc)\n } else if (e.key === 'Enter') {\n if (!act) return\n e.preventDefault()\n const nr = stepRow(act.a.r, e.shiftKey ? -1 : 1)\n selection.value = [{ a: { r: nr, c: act.a.c }, f: { r: nr, c: act.a.c }, kind: 'cell' }]\n scrollCellIntoView(nr, act.a.c)\n } else if (e.key === 'Tab') {\n if (!act) return\n e.preventDefault()\n const nc = stepCol(act.a.c, e.shiftKey ? -1 : 1)\n selection.value = [{ a: { r: act.a.r, c: nc }, f: { r: act.a.r, c: nc }, kind: 'cell' }]\n scrollCellIntoView(act.a.r, nc)\n } else if (e.key === 'Escape') {\n selection.value = []\n }\n}\n\n// Every merged region on the active sheet as a 0-based row range × 1-based col range, derived from the master\n// cells (a merge keeps only its top-left cell carrying rowSpan/colSpan; the covered cells are dropped from the\n// row — see lib/xlsx). Used to make the selection merge-aware.\nconst merges = computed(() => {\n const out: { r0: number; r1: number; c0: number; c1: number }[] = []\n const rows = sheet.value?.rows ?? []\n for (let r = 0; r < rows.length; r++) {\n for (const cell of rows[r].cells) {\n if (cell.colSpan > 1 || cell.rowSpan > 1)\n out.push({ r0: r, r1: r + cell.rowSpan - 1, c0: cell.col, c1: cell.col + cell.colSpan - 1 })\n }\n }\n return out\n})\ninterface Bounds {\n minR: number\n maxR: number\n minC: number\n maxC: number\n}\n// Bounds of one range as a 0-based row range × 1-based col range. A 'cell' range merge-expands to fully enclose\n// any merge it touches (Excel: selecting part of a merge selects the whole box — and a lone click on a merged\n// cell lands on its top-left master, whose raw range is just one column/row). A 'col'/'row' (header) selection\n// does NOT expand: an entire-column/row select stays exactly that column/row even where it crosses a merge, so\n// clicking a header over a horizontal merge lights only that column, not its neighbours. Loop until stable;\n// merges don't overlap, so it converges within `merges.length` passes.\nfunction rangeBounds(rg: SelRange): Bounds {\n let minR = Math.min(rg.a.r, rg.f.r)\n let maxR = Math.max(rg.a.r, rg.f.r)\n let minC = Math.min(rg.a.c, rg.f.c)\n let maxC = Math.max(rg.a.c, rg.f.c)\n if (rg.kind === 'cell') {\n const ms = merges.value\n for (let pass = 0; pass <= ms.length; pass++) {\n let grew = false\n for (const m of ms) {\n if (m.r0 > maxR || m.r1 < minR || m.c0 > maxC || m.c1 < minC) continue\n if (m.r0 < minR) {\n minR = m.r0\n grew = true\n }\n if (m.r1 > maxR) {\n maxR = m.r1\n grew = true\n }\n if (m.c0 < minC) {\n minC = m.c0\n grew = true\n }\n if (m.c1 > maxC) {\n maxC = m.c1\n grew = true\n }\n }\n if (!grew) break\n }\n }\n return { minR, maxR, minC, maxC }\n}\nconst rangeBoundsList = computed<Bounds[]>(() => selection.value.map(rangeBounds))\nconst activeBounds = computed<Bounds | null>(() => {\n const act = activeRange()\n return act ? rangeBounds(act) : null\n})\n// One highlight rectangle per range, in grid pixels (effective sizes, so it tracks resize/hide).\nconst rangeRects = computed<Record<string, string>[]>(() => {\n const out: Record<string, string>[] = []\n for (const b of rangeBoundsList.value) {\n const left = colLeftAt(b.minC)\n const right = colLeftAt(b.maxC + 1)\n const top = rowTopAt(b.minR + 1)\n const bottom = rowTopAt(b.maxR + 2)\n if (left == null || right == null || top == null || bottom == null) continue\n out.push({\n left: `${left}px`,\n top: `${top}px`,\n width: `${right - left}px`,\n height: `${bottom - top}px`,\n })\n }\n return out\n})\n// Whether a cell's area intersects any selection range — drives its per-cell highlight fill. Tests the cell's\n// FULL span (so a merged cell lights up when any range touches it, incl. a whole-column select crossing it),\n// and because it paints on the cell itself it shows through on frozen cells too (the overlay box, pinned in\n// grid space at z-index 1, is occluded by the frozen panes' opaque sticky cells at z2-4 — see .xv__sel).\nfunction cellSelected(cell: XlsxCell, rowIdx: number): boolean {\n const r0 = rowIdx\n const r1 = rowIdx + cell.rowSpan - 1\n const c0 = cell.col\n const c1 = cell.col + cell.colSpan - 1\n for (const b of rangeBoundsList.value)\n if (r1 >= b.minR && r0 <= b.maxR && c1 >= b.minC && c0 <= b.maxC) return true\n return false\n}\nfunction parseNum(t: string): number | null {\n if (!t) return null\n const n = Number(t.replace(/,/g, '').trim())\n return Number.isFinite(n) ? n : null\n}\n// Excel-like status: the active range's address, plus sum/avg/count of the numeric cells in the WHOLE\n// selection (union of all ranges — each rendered cell counted once, so multi-block Ctrl selections are safe).\nconst selStats = computed(() => {\n const s = sheet.value\n const b = activeBounds.value\n if (!s || !b) return null\n let sum = 0\n let numCount = 0\n for (let r = 0; r < s.rows.length; r++) {\n for (const cell of s.rows[r].cells) {\n if (!cellSelected(cell, r)) continue\n const n = parseNum(cell.text)\n if (n != null) {\n sum += n\n numCount++\n }\n }\n }\n const total = (b.maxR - b.minR + 1) * (b.maxC - b.minC + 1)\n const addr = `${numToCol(b.minC)}${b.minR + 1}${total > 1 ? `:${numToCol(b.maxC)}${b.maxR + 1}` : ''}`\n return { addr, total, numCount, sum, avg: numCount ? sum / numCount : 0 }\n})\nfunction fmtStat(n: number): string {\n const r = Math.round(n * 1000) / 1000\n return r.toLocaleString('en-US')\n}\n\n// Whether a column / row index falls inside ANY selection range — so its letter / number header lights up.\nfunction colSelected(c: number): boolean {\n for (const b of rangeBoundsList.value) if (c >= b.minC && c <= b.maxC) return true\n return false\n}\nfunction rowSelected(i: number): boolean {\n for (const b of rangeBoundsList.value) if (i >= b.minR && i <= b.maxR) return true\n return false\n}\n\n// ---- formula bar: the active cell's address + its source (formula or full text) ---------------------------\n// The active cell is the selection's anchor (where a click/drag started), mirroring Excel's name box.\nfunction cellAt(r: number, c: number): XlsxCell | null {\n return sheet.value?.rows[r]?.cells.find((x) => x.col === c) ?? null\n}\nconst activeAddr = computed(() => {\n const a = activeRange()?.a\n return a ? `${numToCol(a.c)}${a.r + 1}` : ''\n})\nconst activeSource = computed(() => {\n const a = activeRange()?.a\n if (!a) return ''\n const cell = cellAt(a.r, a.c)\n if (!cell) return ''\n return cell.formula ? `=${cell.formula}` : cell.text\n})\n\n// ---- embedded charts overlaid at their anchors -----------------------------------------------------------\nconst sheetCharts = computed<XlsxChart[]>(() =>\n sheet.value ? (charts.value.get(sheet.value.name) ?? []) : [],\n)\nconst chartBoxes = computed(() =>\n sheetCharts.value.map((ch) => {\n const c0 = ch.anchor.fromCol + 1\n const r0 = ch.anchor.fromRow + 1\n const left = (colLeftAt(c0) ?? colLeftAt(colCount.value + 1) ?? GUTTER_W) + ch.anchor.offX\n const top =\n (rowTopAt(r0) ?? rowTopAt((sheet.value?.rows.length ?? 0) + 1) ?? HEADER_H) + ch.anchor.offY\n return { chart: ch, left, top, width: ch.anchor.wPx, height: ch.anchor.hPx }\n }),\n)\n\n// ---- sheet tab bar overflow (many sheets → the bottom strip scrolls; expose arrows + wheel so every tab is\n// reachable in a narrow side panel, where a thin horizontal scrollbar is easy to miss) -----------------------\nconst tabsEl = ref<HTMLElement | null>(null)\nconst tabsOverflow = ref(false) // strip wider than its box → show the ‹ › arrows\nconst tabAtStart = ref(true)\nconst tabAtEnd = ref(false)\nfunction updateTabOverflow(): void {\n const el = tabsEl.value\n if (!el) {\n tabsOverflow.value = false\n return\n }\n const max = el.scrollWidth - el.clientWidth\n tabsOverflow.value = max > 1\n tabAtStart.value = el.scrollLeft <= 1\n tabAtEnd.value = el.scrollLeft >= max - 1\n}\nfunction scrollTabs(dir: number): void {\n const el = tabsEl.value\n if (el) el.scrollBy({ left: dir * Math.max(120, el.clientWidth * 0.7), behavior: 'smooth' })\n}\n// Translate a vertical mouse wheel into horizontal scroll so a plain wheel (no shift, no trackpad) reaches\n// every tab — only while the strip actually overflows, so normal page scrolling is untouched otherwise.\nfunction onTabsWheel(e: WheelEvent): void {\n const el = tabsEl.value\n if (!el || el.scrollWidth <= el.clientWidth) return\n const d = Math.abs(e.deltaY) > Math.abs(e.deltaX) ? e.deltaY : e.deltaX\n if (!d) return\n el.scrollLeft += d\n e.preventDefault()\n updateTabOverflow()\n}\nfunction scrollActiveTabIntoView(): void {\n nextTick(() => {\n tabsEl.value?.querySelector<HTMLElement>('.xv__tab--on')?.scrollIntoView({\n inline: 'nearest',\n block: 'nearest',\n })\n updateTabOverflow()\n })\n}\n// Recompute overflow whenever the strip element appears / changes (model load) and when the panel is resized\n// (the FileViewerPanel drag-resizer changes our width without a window resize, so observe the element itself).\nlet tabRO: ResizeObserver | null = null\nwatch(tabsEl, (el) => {\n tabRO?.disconnect()\n if (el && typeof ResizeObserver !== 'undefined') {\n tabRO = new ResizeObserver(updateTabOverflow)\n tabRO.observe(el)\n updateTabOverflow()\n }\n})\n\n// ---- load --------------------------------------------------------------------------------------------------\nasync function render(): Promise<void> {\n if (!props.url) return\n status.value = 'loading'\n errorMsg.value = ''\n model.value = null\n charts.value = new Map()\n perSheet.value = {}\n selection.value = []\n editCell.value = null\n active.value = 0\n // Drop the PREVIOUS workbook's measured geometry. measuredCols/Rows cache real per-row/col pixel edges and\n // feed the frozen panes' sticky top/left (cellStyle/gutterStyle). FileViewerBody keys nothing, so swapping\n // one .xlsx for another reuses this component — only props.url changes. If the stale edges survive, they\n // become the new file's frozen-cell offsets; and because a STUCK sticky element reports offsetTop at its\n // shifted position, measure() reads those wrong offsets straight back and re-confirms them — a self-\n // reinforcing fixed point that never self-heals, so frozen rows land on top of the rows scrolling beneath\n // them (the garbled \"先开 A 再开 B\" render). Clearing makes the new file fall back to the computed\n // rowTops/colLefts (≈ natural layout) until measure() re-runs — identical to opening B first / standalone.\n measuredCols.value = []\n measuredRows.value = []\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 if (props.url !== url) return\n const parsed = await parseXlsx(buf)\n if (props.url !== url) return\n model.value = parsed\n status.value = 'ready'\n scheduleMeasure()\n // Charts are a best-effort enhancement parsed from the same bytes — never let them break the table.\n try {\n const ch = await parseXlsxCharts(buf)\n if (props.url === url) {\n charts.value = ch\n scheduleMeasure()\n }\n } catch {\n /* ignore — no charts shown */\n }\n } catch (e) {\n errorMsg.value = e instanceof Error ? e.message : String(e)\n status.value = 'error'\n }\n}\n\nwatch(active, () => {\n selection.value = []\n editCell.value = null\n // A different sheet is a different layout: drop the prior sheet's measured edges for the same reason render()\n // does — a frozen-pane sheet would otherwise inherit the previous sheet's row/col offsets and hit the same\n // self-reinforcing sticky-offset bug. Fall back to computed edges until measure() runs for the new sheet.\n measuredCols.value = []\n measuredRows.value = []\n // ALSO reset the scroll origin. Unlike a file swap — which flips status to 'loading', tearing down and later\n // rebuilding .xv__scroll so the new file starts at scroll 0 — switching sheets keeps status==='ready' and\n // REUSES the same scroll container, so it inherits the previous sheet's scrollTop/Left. measure() (queued\n // just below) would then read the new sheet's frozen cells at their STUCK positions, whose offsetTop/Left\n // include that inherited scroll, so the measured edges come out inflated by the scroll amount (top ≈\n // scrollTop + HEADER_H, left ≈ scrollLeft + GUTTER_W). Those feed cellStyle's sticky top/left, pinning the\n // frozen rows/cols that far off the viewport corner — leaving a blank band where they belong, and (since\n // measure never re-runs on plain scroll) never self-healing. Scrolling to the top-left first makes stuck ==\n // natural, so the new sheet measures from a clean origin, exactly like opening it first / standalone.\n const el = scrollEl.value\n if (el) {\n el.scrollTop = 0\n el.scrollLeft = 0\n }\n scheduleMeasure()\n scrollActiveTabIntoView()\n})\n\nonMounted(() => {\n render()\n window.addEventListener('mousemove', onResizeMove)\n window.addEventListener('mouseup', onWindowUp)\n})\nonBeforeUnmount(() => {\n window.removeEventListener('mousemove', onResizeMove)\n window.removeEventListener('mouseup', onWindowUp)\n tabRO?.disconnect()\n})\nfunction onWindowUp(): void {\n endResize()\n endSelect()\n}\nwatch(() => props.url, render)\n</script>\n\n<template>\n <div class=\"xv\">\n <p v-if=\"status === 'loading'\" class=\"xv__msg\">正在解析表格…</p>\n <div v-else-if=\"status === 'error'\" class=\"xv__msg xv__msg--error\">\n <p>解析失败:{{ errorMsg }}</p>\n <button type=\"button\" class=\"xv__retry\" @click=\"render\">重试</button>\n </div>\n\n <template v-else-if=\"status === 'ready' && sheet\">\n <!-- Formula bar (Excel-style): active cell address + its source — the formula `=…` or the full text. -->\n <div class=\"xv__formulabar\">\n <span class=\"xv__fb-addr\">{{ activeAddr || '—' }}</span>\n <span class=\"xv__fb-content\">{{ activeSource }}</span>\n </div>\n\n <div ref=\"scrollEl\" class=\"xv__scroll\" tabindex=\"0\" @keydown=\"onGridKeyDown\">\n <div class=\"xv__grid\">\n <!-- Explicit width = sum of (effective) column widths. Without it, `width:max-content` lets the\n fixed-layout table size 0-width hidden columns up to their content (#REF! etc.) — pinning the\n width makes the browser honour each column exactly, so hidden columns truly collapse to 0. -->\n <table ref=\"tableEl\" class=\"xv__table\" :style=\"{ width: colLefts[colCount + 1] + 'px' }\">\n <colgroup>\n <col class=\"xv__rowhead-col\" />\n <col\n v-for=\"c in colCount\"\n :key=\"c\"\n :style=\"{ width: effColW(c) + 'px', minWidth: effColW(c) + 'px' }\"\n />\n </colgroup>\n <thead>\n <tr>\n <th class=\"xv__corner\"></th>\n <th\n v-for=\"(letter, i) in colLetters\"\n :key=\"i\"\n class=\"xv__colhead\"\n :class=\"{\n 'xv__colhead--coll': colHidden(i + 1),\n 'xv__colhead--frz': i + 1 <= frozenCols,\n 'xv__colhead--frzedge': frzEdgeCol(i + 1),\n 'xv__colhead--sel': colSelected(i + 1),\n }\"\n :style=\"colHeadStyle(i + 1)\"\n title=\"点击选中整列 · 拖动选多列 · Shift 连选 · Ctrl 多选\"\n @mousedown=\"onColHeadDown(i + 1, $event)\"\n @mouseenter=\"onColHeadEnter(i + 1)\"\n >\n <button\n v-if=\"colExpanders.get(i + 1)\"\n type=\"button\"\n class=\"xv__expander xv__expander--col\"\n :title=\"`展开 ${colExpanders.get(i + 1)!.count} 个隐藏列`\"\n @mousedown.stop\n @click.stop=\"revealColRun(colExpanders.get(i + 1)!)\"\n >\n ›‹\n </button>\n <span class=\"xv__colhead-text\">{{ letter }}</span>\n <span\n class=\"xv__col-resize\"\n title=\"拖动调整列宽\"\n @mousedown.stop.prevent=\"onColResizeStart(i + 1, $event)\"\n />\n </th>\n </tr>\n </thead>\n <tbody>\n <tr\n v-for=\"(row, r) in sheet.rows\"\n :key=\"r\"\n :class=\"{ 'xv__row--coll': rowHiddenAt(r) }\"\n :style=\"{ height: effRowH(r) + 'px' }\"\n >\n <th\n class=\"xv__rowhead\"\n :class=\"{\n 'xv__rowhead--frz': r + 1 <= frozenRows,\n 'xv__rowhead--frzedge': frzEdgeRowIdx(r),\n 'xv__rowhead--sel': rowSelected(r),\n }\"\n :style=\"gutterStyle(r)\"\n title=\"点击选中整行 · 拖动选多行 · Shift 连选 · Ctrl 多选\"\n @mousedown=\"onRowHeadDown(r, $event)\"\n @mouseenter=\"onRowHeadEnter(r)\"\n >\n <button\n v-if=\"rowExpanders.get(r)\"\n type=\"button\"\n class=\"xv__expander xv__expander--row\"\n :title=\"`展开 ${rowExpanders.get(r)!.count} 个隐藏行`\"\n @mousedown.stop\n @click.stop=\"revealRowRun(rowExpanders.get(r)!)\"\n >\n ⌄\n </button>\n <span class=\"xv__rowhead-text\">{{ row.num }}</span>\n <span\n class=\"xv__row-resize\"\n title=\"拖动调整行高\"\n @mousedown.stop.prevent=\"onRowResizeStart(row.num, $event)\"\n />\n </th>\n <td\n v-for=\"cell in row.cells\"\n :key=\"cell.col\"\n class=\"xv__cell\"\n :rowspan=\"cell.rowSpan > 1 ? cell.rowSpan : undefined\"\n :colspan=\"cell.colSpan > 1 ? cell.colSpan : undefined\"\n :class=\"{\n 'xv__cell--bold': cell.bold,\n 'xv__cell--italic': cell.italic,\n 'xv__cell--underline': cell.underline,\n 'xv__cell--strike': cell.strike,\n 'xv__cell--wrap': cell.wrap,\n 'xv__cell--bt': cell.bt,\n 'xv__cell--br': cell.br,\n 'xv__cell--bb': cell.bb,\n 'xv__cell--bl': cell.bl,\n 'xv__cell--coll': colHidden(cell.col),\n 'xv__cell--frz-col': cell.col <= frozenCols,\n 'xv__cell--frz-row': r + 1 <= frozenRows,\n 'xv__cell--frzedge-col': frzEdgeCol(cell.col),\n 'xv__cell--frzedge-row': frzEdgeRowIdx(r),\n 'xv__cell--edit': isEditCell(r, cell.col),\n 'xv__cell--spill': spillMap.has(`${r}:${cell.col}`),\n 'xv__cell--sel': cellSelected(cell, r),\n }\"\n :style=\"cellStyle(cell, r)\"\n @mousedown=\"onCellDown(r, cell.col, $event)\"\n @mouseenter=\"onCellEnter(r, cell.col)\"\n @dblclick=\"onCellDblClick(r, cell.col)\"\n >\n <span class=\"xv__cellt\" :style=\"spillStyle(cell, r)\">{{ cell.text }}</span>\n </td>\n </tr>\n </tbody>\n </table>\n\n <!-- selection outline — one box per range (Ctrl multi-block); the per-cell .xv__cell--sel fill carries\n the tint (so it shows on frozen cells too, which would occlude this grid-space box). -->\n <div v-for=\"(rect, i) in rangeRects\" :key=\"i\" class=\"xv__sel\" :style=\"rect\" />\n\n <!-- embedded charts, overlaid at their drawing anchors -->\n <div class=\"xv__charts\">\n <div\n v-for=\"(box, i) in chartBoxes\"\n :key=\"i\"\n class=\"xv__chart\"\n :style=\"{\n left: box.left + 'px',\n top: box.top + 'px',\n width: box.width + 'px',\n height: box.height + 'px',\n }\"\n >\n <XlsxChartView :chart=\"box.chart\" />\n </div>\n </div>\n </div>\n </div>\n\n <!-- status bar: selection address + numeric sum/avg, and a reset for hide/resize tweaks -->\n <div class=\"xv__status\">\n <span v-if=\"selStats\" class=\"xv__status-sel\">\n {{ selStats.addr }}\n <template v-if=\"selStats.numCount > 0\">\n · 计数 {{ selStats.numCount }} · 求和 {{ fmtStat(selStats.sum) }}\n <template v-if=\"selStats.numCount > 1\"> · 平均 {{ fmtStat(selStats.avg) }} </template>\n </template>\n </span>\n <span v-else class=\"xv__status-hint\"\n >拖动 / 方向键选择 · Shift 连选 · Ctrl 多选 · 点击或拖动行列头选整行/列 · 双击编辑文本 ·\n 拖行列头边缘调尺寸</span\n >\n <span class=\"xv__status-right\">\n <button\n type=\"button\"\n class=\"xv__status-btn\"\n :class=\"{ 'xv__status-btn--on': curUi.revealAll }\"\n @click=\"toggleRevealAll\"\n >\n {{ curUi.revealAll ? '折叠隐藏行列' : '显示隐藏行列' }}\n </button>\n <button v-if=\"hasOverrides\" type=\"button\" class=\"xv__status-btn\" @click=\"resetUi\">\n 复位\n </button>\n </span>\n </div>\n\n <!-- Excel-style sheet tabs along the bottom. The strip scrolls; ‹ › arrows appear when it overflows (and\n a vertical wheel scrolls it horizontally) so every sheet is reachable even in a narrow side panel. -->\n <div v-if=\"sheets.length > 1\" class=\"xv__tabbar\">\n <button\n v-if=\"tabsOverflow\"\n type=\"button\"\n class=\"xv__tabscroll\"\n :disabled=\"tabAtStart\"\n title=\"向左滚动标签\"\n @click=\"scrollTabs(-1)\"\n >\n ‹\n </button>\n <div ref=\"tabsEl\" class=\"xv__tabs\" @scroll=\"updateTabOverflow\" @wheel=\"onTabsWheel\">\n <button\n v-for=\"(s, i) in sheets\"\n :key=\"i\"\n type=\"button\"\n class=\"xv__tab\"\n :class=\"{ 'xv__tab--on': i === active }\"\n :title=\"s.name\"\n @click=\"active = i\"\n >\n {{ s.name }}\n </button>\n </div>\n <button\n v-if=\"tabsOverflow\"\n type=\"button\"\n class=\"xv__tabscroll\"\n :disabled=\"tabAtEnd\"\n title=\"向右滚动标签\"\n @click=\"scrollTabs(1)\"\n >\n ›\n </button>\n </div>\n </template>\n </div>\n</template>\n\n<style scoped>\n.xv {\n flex: 1 1 auto;\n min-height: 0;\n display: flex;\n flex-direction: column;\n background: var(--surface);\n}\n.xv__msg {\n padding: 20px 16px;\n color: var(--muted);\n font-size: 13px;\n}\n.xv__msg--error {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 10px;\n color: var(--danger);\n}\n.xv__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.xv__retry:hover {\n border-color: var(--accent);\n color: var(--accent);\n}\n\n/* Formula bar above the grid: a fixed name-box (cell address) + the active cell's source (formula or text). */\n.xv__formulabar {\n display: flex;\n align-items: stretch;\n flex-shrink: 0;\n min-height: 24px;\n border-bottom: 1px solid var(--border);\n background: var(--surface);\n font-size: 12px;\n}\n.xv__fb-addr {\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n width: 72px;\n padding: 0 6px;\n border-right: 1px solid var(--border);\n background: var(--surface-2);\n color: var(--text);\n font-weight: 600;\n font-variant-numeric: tabular-nums;\n}\n.xv__fb-content {\n flex: 1 1 auto;\n min-width: 0;\n display: flex;\n align-items: center;\n padding: 0 10px;\n color: var(--text);\n font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;\n white-space: nowrap;\n overflow-x: auto;\n user-select: text;\n}\n\n/* The grid scrolls both ways inside the pane; cells keep a spreadsheet look (thin gray gridlines, white\n * sheet) regardless of theme. xv__grid is the positioned context the chart/selection overlays sit in. */\n.xv__scroll {\n flex: 1 1 auto;\n min-height: 0;\n overflow: auto;\n overscroll-behavior: contain;\n background: #fff;\n}\n/* Focusable for keyboard navigation, but no focus ring — the cell selection box is the visible cursor. */\n.xv__scroll:focus,\n.xv__scroll:focus-visible {\n outline: none;\n}\n.xv__grid {\n position: relative;\n width: max-content;\n}\n.xv__table {\n /* `separate` (not `collapse`) is deliberate and load-bearing for the frozen panes. Under\n * `border-collapse: collapse`, table cells render their backgrounds in the table's grid layer and a sticky\n * cell's z-index does NOT lift its background above other cells — so a frozen (sticky) cell with an opaque\n * white background fails to occlude the scrolling rows/columns sliding underneath it, and their text bleeds\n * straight through the frozen region. `separate` makes each cell a self-contained box that honours z-index\n * for background + content, so frozen cells cleanly cover what scrolls beneath them. `border-spacing: 0`\n * keeps the gridlines tight (table-layout:fixed pins the column widths, so this doesn't widen the table or\n * double the 1px lines perceptibly). The frozen-edge 2px rule is drawn via box-shadow (see frzedge below). */\n border-collapse: separate;\n border-spacing: 0;\n table-layout: fixed;\n /* Calibri first: Excel's default body font and the basis of the column-width→pixel formula (MDW=7px). A\n * wider fallback (Segoe UI etc.) would overflow the Excel-sized columns and clip text into \"…\"; Calibri's\n * metrics make the widths line up, so text fits as it does in Excel. */\n font-family: Calibri, 'Segoe UI', system-ui, sans-serif;\n font-size: 12.5px;\n color: #1a1a1a;\n}\n.xv__rowhead-col {\n width: 42px;\n}\n/* Row-number gutter — sticky to the left so it stays visible while scrolling horizontally. */\n.xv__rowhead {\n position: sticky;\n left: 0;\n z-index: 5;\n width: 42px;\n min-width: 42px;\n padding: 2px 6px;\n background: #f3f4f6;\n border: 1px solid #d9dce1;\n color: #6b7280;\n font-weight: 500;\n font-size: 11px;\n text-align: center;\n user-select: none;\n}\n/* A row-number gutter over a frozen row is pinned to the top too (sticky `top`, via gutterStyle's inline\n * offset) so it stays beside its frozen cells when scrolling down. It must out-rank the scrolling gutters,\n * which share `.xv__rowhead`'s z5 and — being later in the DOM (higher row numbers) — would otherwise paint\n * over it as they scroll up, so the frozen row numbers 1…N would vanish under 7,8,9…. Lift it to z7 (the\n * column-axis mirror of `.xv__colhead--frz`), still below the corner (z8). */\n.xv__rowhead--frz {\n z-index: 7;\n}\n/* Column-letter header (A, B, C…) — sticky to the top while scrolling vertically. */\n.xv__colhead {\n position: sticky;\n top: 0;\n z-index: 6;\n height: 22px;\n padding: 2px 6px;\n background: #f3f4f6;\n border: 1px solid #d9dce1;\n color: #6b7280;\n font-weight: 500;\n font-size: 11px;\n text-align: center;\n user-select: none;\n}\n/* A column header over a frozen column is pinned left by its inline `left`; lift it above the scrolling\n * headers (z6) so they pass beneath it, but keep it below the corner (z8). */\n.xv__colhead--frz {\n z-index: 7;\n}\n.xv__colhead-text,\n.xv__rowhead-text {\n pointer-events: none;\n}\n/* Collapsed (hidden) header cell: zero width, no visible content. */\n.xv__colhead--coll {\n padding: 0;\n border-left: 0;\n border-right: 0;\n overflow: hidden;\n}\n/* A header whose column / row is inside the current selection — lit up like Excel's row/column headers. */\n.xv__colhead--sel,\n.xv__rowhead--sel {\n background: #d6e4fb;\n color: #1d4ed8;\n font-weight: 600;\n}\n/* Top-left corner — pinned to both edges, above both headers. */\n.xv__corner {\n position: sticky;\n top: 0;\n left: 0;\n z-index: 8;\n width: 42px;\n min-width: 42px;\n background: #eceef1;\n border: 1px solid #d9dce1;\n}\n\n/* Frozen panes: cells in the first N rows / M columns stay pinned. The inline style supplies the offset; these\n * classes switch on sticky positioning and the stacking order. The `.xv__cell` prefix is REQUIRED, not cosmetic:\n * `.xv__cell` below sets `position: relative`, and a bare `.xv__cell--frz-*` has equal specificity but is\n * declared earlier, so `relative` would win and the frozen cell would take its inline `top`/`left` as a plain\n * relative shift — dragging the pinned row/column down/right by the offset. Chaining `.xv__cell` outranks it. */\n.xv__cell.xv__cell--frz-col {\n position: sticky;\n z-index: 2;\n}\n.xv__cell.xv__cell--frz-row {\n position: sticky;\n z-index: 3;\n}\n.xv__cell.xv__cell--frz-row.xv__cell--frz-col {\n z-index: 4;\n}\n/* The 2px rule marking the frozen/scroll boundary (Excel-style), so the pinned region reads as distinct.\n * Drawn as an INSET box-shadow, not a `border: 2px`: a box-shadow rides along with the cell's sticky offset\n * and is layout-neutral (it doesn't fatten the edge cell's box and shift the columns past it by a pixel). It\n * also sidesteps the border-vs-sticky quirk entirely — a collapsed boundary border is painted in the table's\n * grid space at the cell's NATURAL edge and slides away under the frozen block on scroll, leaving it bare\n * (\"竖向边框丢失\"); a box-shadow stays pinned to the frozen edge no matter the border model. A cell at the\n * block's corner (last frozen row AND column) needs both directions. */\n.xv__cell--frzedge-col,\n.xv__colhead--frzedge {\n box-shadow: inset -2px 0 0 0 #cccccc;\n}\n.xv__cell--frzedge-row,\n.xv__rowhead--frzedge {\n box-shadow: inset 0 -2px 0 0 #cccccc;\n}\n.xv__cell--frzedge-col.xv__cell--frzedge-row {\n box-shadow:\n inset -2px 0 0 0 #cccccc,\n inset 0 -2px 0 0 #cccccc;\n}\n\n.xv__cell {\n position: relative;\n /* Horizontal padding kept tight (4px) so the usable content width matches Excel's column pixels — the width\n * formula (lib/xlsx colWidthToPx) reserves exactly this chrome, otherwise long text clips into \"…\". */\n padding: 2px 4px;\n border: 1px solid #e3e5e9;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n vertical-align: top;\n /* Default: a drag highlights a CELL RANGE, not text. Double-click a cell (→ .xv__cell--edit) to select text. */\n user-select: none;\n}\n/* Excel-style spill: let a general/left text cell paint over the empty cells to its right. The cell stops\n * clipping; the inner span is capped (inline style) to the column + empty-run width with its own ellipsis, so\n * it shows in full until even the run is too narrow. z-index lifts the text above the neighbours' gridlines. */\n.xv__cell--spill {\n overflow: visible;\n z-index: 1;\n}\n.xv__cell--spill .xv__cellt {\n display: inline-block;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n vertical-align: bottom;\n}\n/* The spilled text paints over neighbouring cells; let clicks fall THROUGH to whatever cell is visually under\n * the pointer (like Excel/WPS — clicking a position selects that cell, not the one the text spilled out of).\n * The double-clicked edit cell keeps pointer events so its text stays selectable. */\n.xv__cell--spill:not(.xv__cell--edit) .xv__cellt {\n pointer-events: none;\n}\n/* Double-clicked cell: enable native text selection so the value can be highlighted/copied. */\n.xv__cell--edit {\n user-select: text;\n cursor: text;\n outline: 1.5px solid var(--accent, #2563eb);\n outline-offset: -1.5px;\n overflow: visible;\n z-index: 3;\n}\n/* Collapsed (hidden) column cell: contributes no width and shows nothing. */\n.xv__cell--coll {\n padding: 0;\n border-left: 0;\n border-right: 0;\n overflow: hidden;\n}\n/* Collapsed (hidden) row: zero out BOTH the data cells and the row-number gutter. Leaving the `<th>` chrome\n * intact would keep the table row ~22px tall even though every `<td>` was empty, producing a permanent blank\n * band and making the measured frozen-row offsets disagree with effRowH(…)=0. */\n.xv__row--coll .xv__rowhead,\n.xv__row--coll .xv__cell {\n height: 0;\n padding-top: 0;\n padding-bottom: 0;\n border-top: 0;\n border-bottom: 0;\n line-height: 0;\n font-size: 0 !important;\n overflow: hidden;\n}\n.xv__cell--wrap {\n white-space: normal;\n word-break: break-word;\n}\n.xv__cell--bold {\n font-weight: 700;\n}\n.xv__cell--italic {\n font-style: italic;\n}\n.xv__cell--underline {\n text-decoration: underline;\n}\n.xv__cell--strike {\n text-decoration: line-through;\n}\n.xv__cell--bt {\n border-top-color: #9aa0a6;\n}\n.xv__cell--br {\n border-right-color: #9aa0a6;\n}\n.xv__cell--bb {\n border-bottom-color: #9aa0a6;\n}\n.xv__cell--bl {\n border-left-color: #9aa0a6;\n}\n\n/* Expander chip for a collapsed run, anchored to the header cell's leading edge. */\n.xv__expander {\n position: absolute;\n z-index: 1;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: 0;\n font-size: 10px;\n line-height: 1;\n color: #fff;\n background: #9aa0a6;\n border: none;\n border-radius: 3px;\n cursor: pointer;\n}\n.xv__expander:hover {\n background: var(--accent, #2563eb);\n}\n.xv__expander--col {\n left: -7px;\n top: 50%;\n transform: translateY(-50%);\n width: 14px;\n height: 14px;\n}\n.xv__expander--row {\n top: -7px;\n left: 50%;\n transform: translateX(-50%);\n width: 14px;\n height: 12px;\n}\n\n/* Resize grips on the trailing edge of each header cell. */\n.xv__col-resize {\n position: absolute;\n top: 0;\n right: -3px;\n width: 6px;\n height: 100%;\n cursor: col-resize;\n z-index: 2;\n}\n.xv__row-resize {\n position: absolute;\n left: 0;\n bottom: -3px;\n height: 6px;\n width: 100%;\n cursor: row-resize;\n z-index: 2;\n}\n\n/* Selection outline — one per range, drawn in grid space. Border only: the translucent FILL is painted per\n * cell by .xv__cell--sel below, so it shows on frozen cells too (this grid-space box sits at z-index 1, under\n * the frozen panes' opaque sticky cells at z2-4, which would otherwise hide the fill there). */\n.xv__sel {\n position: absolute;\n z-index: 1;\n pointer-events: none;\n border: 1.5px solid rgba(37, 99, 235, 0.65);\n}\n/* Per-cell selection fill, layered as a background-image OVER the cell's own background-color (so a coloured or\n * frozen/opaque cell still shows the tint — see cellStyle, which sets backgroundColor not the `background`\n * shorthand precisely so this isn't wiped). Every selected grid column has a <td> (the parser emits empties and\n * merge masters span their covered slots), so the fill tiles the whole selection with no gaps — including the\n * frozen panes, where this per-cell layer is the ONLY visible highlight (the grid-space outline box can't\n * reach them; a lone frozen-cell click would otherwise look unselected). */\n.xv__cell--sel {\n background-image: linear-gradient(rgba(37, 99, 235, 0.16), rgba(37, 99, 235, 0.16));\n}\n.xv__charts {\n position: absolute;\n inset: 0;\n z-index: 1;\n pointer-events: none;\n}\n.xv__chart {\n position: absolute;\n box-shadow: 0 1px 4px rgba(0, 0, 0, 0.12);\n}\n\n/* Status bar (selection stats + hide/resize controls). */\n.xv__status {\n display: flex;\n align-items: center;\n gap: 10px;\n flex-shrink: 0;\n padding: 3px 10px;\n min-height: 24px;\n border-top: 1px solid var(--border);\n background: var(--surface-2);\n font-size: 11.5px;\n color: var(--muted);\n}\n.xv__status-sel {\n font-variant-numeric: tabular-nums;\n}\n.xv__status-hint {\n color: var(--faint);\n}\n.xv__status-right {\n margin-left: auto;\n display: flex;\n gap: 6px;\n}\n.xv__status-btn {\n font-family: inherit;\n font-size: 11.5px;\n color: var(--text);\n background: var(--surface);\n border: 1px solid var(--border-2);\n border-radius: 6px;\n padding: 2px 8px;\n cursor: pointer;\n}\n.xv__status-btn:hover {\n border-color: var(--accent);\n color: var(--accent);\n}\n.xv__status-btn--on {\n color: var(--accent);\n border-color: var(--accent);\n}\n\n/* Sheet tabs (only when there's more than one worksheet). The bar = optional ‹ › scroll arrows flanking a\n * horizontally-scrollable strip, so many sheets stay reachable even in a narrow side panel. */\n.xv__tabbar {\n display: flex;\n align-items: stretch;\n flex-shrink: 0;\n border-top: 1px solid var(--border);\n background: var(--surface-2);\n}\n.xv__tabs {\n flex: 1 1 auto;\n min-width: 0;\n display: flex;\n gap: 2px;\n padding: 4px 8px;\n overflow-x: auto;\n scrollbar-width: none; /* the arrows + wheel drive it; the thin native bar is awkward to grab in a side pane */\n}\n.xv__tabs::-webkit-scrollbar {\n display: none;\n}\n/* ‹ / › overflow-scroll buttons (rendered only when the strip overflows; disabled at each end). */\n.xv__tabscroll {\n flex-shrink: 0;\n width: 22px;\n border: none;\n background: var(--surface-2);\n color: var(--muted);\n font-size: 16px;\n line-height: 1;\n cursor: pointer;\n}\n.xv__tabscroll:hover:not(:disabled) {\n color: var(--accent);\n background: var(--surface);\n}\n.xv__tabscroll:disabled {\n color: var(--faint);\n opacity: 0.5;\n cursor: default;\n}\n.xv__tab {\n flex-shrink: 0;\n max-width: 160px;\n padding: 5px 12px;\n font-family: inherit;\n font-size: 12px;\n color: var(--muted);\n background: transparent;\n border: none;\n border-radius: 7px 7px 0 0;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n cursor: pointer;\n transition:\n background 0.15s,\n color 0.15s;\n}\n.xv__tab:hover {\n background: var(--surface);\n color: var(--text);\n}\n.xv__tab--on {\n color: var(--accent);\n background: var(--surface);\n font-weight: 600;\n box-shadow: var(--shadow);\n}\n</style>\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"XmindViewer.js","names":[],"sources":["../../src/components/XmindViewer.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref, watch } from 'vue'\nimport { parseXmind } from '@/lib/xmind'\nimport type { MindMap } from '@/lib/mindmap'\nimport { fileExt } from '@/lib/fileType'\nimport MindMapView from '@/components/MindMapView.vue'\n\n/**\n * Side-viewer for XMind workbooks (.xmind). Fetches the bytes, unzips + parses `content.json` / `content.xml`\n * in-browser (see lib/xmind.ts — reuses the project's ZIP reader), then hands the normalized tree to the shared\n * MindMapView so an XMind renders as the same interactive mind map as OPML / FreeMind. Nothing leaves the browser.\n */\nconst props = defineProps<{ url?: string; name: string; size?: number }>()\n\ntype Status = 'loading' | 'ready' | 'error'\nconst status = ref<Status>('loading')\nconst map = ref<MindMap | null>(null)\nconst errorMsg = ref('')\n\nconst baseName = computed(() => {\n const n = props.name || ''\n const e = fileExt(n)\n return e ? n.slice(0, n.length - e.length - 1) : n\n})\n\nasync function load(): Promise<void> {\n if (!props.url) return\n status.value = 'loading'\n errorMsg.value = ''\n map.value = null\n try {\n const res = await fetch(props.url)\n if (!res.ok) throw new Error(`HTTP ${res.status}`)\n const buf = await res.arrayBuffer()\n map.value = await parseXmind(buf, baseName.value)\n status.value = 'ready'\n } catch (e) {\n errorMsg.value = e instanceof Error ? e.message : String(e)\n status.value = 'error'\n }\n}\n\nwatch(() => props.url, load, { immediate: true })\n</script>\n\n<template>\n <div class=\"xm\">\n <p v-if=\"status === 'loading'\" class=\"xm__msg\">正在解析 XMind…</p>\n <div v-else-if=\"status === 'error'\" class=\"xm__msg xm__msg--error\">\n <p>解析失败:{{ errorMsg }}</p>\n <button type=\"button\" class=\"xm__btn\" @click=\"load\">重试</button>\n </div>\n <MindMapView v-else-if=\"map\" :map=\"map\" :name=\"name\" />\n </div>\n</template>\n\n<style scoped>\n.xm {\n flex: 1 1 auto;\n min-height: 0;\n display: flex;\n flex-direction: column;\n background: var(--surface-2);\n}\n.xm__msg {\n padding: 20px 16px;\n color: var(--muted);\n font-size: 13px;\n}\n.xm__msg--error {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 10px;\n color: var(--danger);\n}\n.xm__btn {\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.xm__btn:hover {\n border-color: var(--accent);\n color: var(--accent);\n}\n</style>\n"],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"XmindViewer.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../src/components/XmindViewer.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref, watch } from 'vue'\nimport { parseXmind } from '@/lib/xmind'\nimport type { MindMap } from '@/lib/mindmap'\nimport { fileExt } from '@/lib/fileType'\nimport MindMapView from '@/components/MindMapView.vue'\n\n/**\n * Side-viewer for XMind workbooks (.xmind). Fetches the bytes, unzips + parses `content.json` / `content.xml`\n * in-browser (see lib/xmind.ts — reuses the project's ZIP reader), then hands the normalized tree to the shared\n * MindMapView so an XMind renders as the same interactive mind map as OPML / FreeMind. Nothing leaves the browser.\n */\nconst props = defineProps<{ url?: string; name: string; size?: number }>()\n\ntype Status = 'loading' | 'ready' | 'error'\nconst status = ref<Status>('loading')\nconst map = ref<MindMap | null>(null)\nconst errorMsg = ref('')\n\nconst baseName = computed(() => {\n const n = props.name || ''\n const e = fileExt(n)\n return e ? n.slice(0, n.length - e.length - 1) : n\n})\n\nasync function load(): Promise<void> {\n if (!props.url) return\n status.value = 'loading'\n errorMsg.value = ''\n map.value = null\n try {\n const res = await fetch(props.url)\n if (!res.ok) throw new Error(`HTTP ${res.status}`)\n const buf = await res.arrayBuffer()\n map.value = await parseXmind(buf, baseName.value)\n status.value = 'ready'\n } catch (e) {\n errorMsg.value = e instanceof Error ? e.message : String(e)\n status.value = 'error'\n }\n}\n\nwatch(() => props.url, load, { immediate: true })\n</script>\n\n<template>\n <div class=\"xm\">\n <p v-if=\"status === 'loading'\" class=\"xm__msg\">正在解析 XMind…</p>\n <div v-else-if=\"status === 'error'\" class=\"xm__msg xm__msg--error\">\n <p>解析失败:{{ errorMsg }}</p>\n <button type=\"button\" class=\"xm__btn\" @click=\"load\">重试</button>\n </div>\n <MindMapView v-else-if=\"map\" :map=\"map\" :name=\"name\" />\n </div>\n</template>\n\n<style scoped>\n.xm {\n flex: 1 1 auto;\n min-height: 0;\n display: flex;\n flex-direction: column;\n background: var(--surface-2);\n}\n.xm__msg {\n padding: 20px 16px;\n color: var(--muted);\n font-size: 13px;\n}\n.xm__msg--error {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 10px;\n color: var(--danger);\n}\n.xm__btn {\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.xm__btn:hover {\n border-color: var(--accent);\n color: var(--accent);\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;EAYA,IAAM,IAAQ,GAGR,IAAS,EAAY,SAAS,GAC9B,IAAM,EAAoB,IAAI,GAC9B,IAAW,EAAI,EAAE,GAEjB,IAAW,QAAe;GAC9B,IAAM,IAAI,EAAM,QAAQ,IAClB,IAAI,EAAQ,CAAC;GACnB,OAAO,IAAI,EAAE,MAAM,GAAG,EAAE,SAAS,EAAE,SAAS,CAAC,IAAI;EACnD,CAAC;EAED,eAAe,IAAsB;GAC9B,MAAM,KAGX;IAFA,EAAO,QAAQ,WACf,EAAS,QAAQ,IACjB,EAAI,QAAQ;IACZ,IAAI;KACF,IAAM,IAAM,MAAM,MAAM,EAAM,GAAG;KACjC,IAAI,CAAC,EAAI,IAAI,MAAU,MAAM,QAAQ,EAAI,QAAQ;KAGjD,AADA,EAAI,QAAQ,MAAM,EAAW,MADX,EAAI,YAAY,GACA,EAAS,KAAK,GAChD,EAAO,QAAQ;IACjB,SAAS,GAAG;KAEV,AADA,EAAS,QAAQ,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,GAC1D,EAAO,QAAQ;IACjB;GAVY;EAWd;SAEA,QAAY,EAAM,KAAK,GAAM,EAAE,WAAW,GAAK,CAAC,mBAI9C,EAOM,OAPN,GAOM,CANK,EAAA,UAAM,aAAA,EAAA,GAAf,EAA8D,KAA9D,GAA+C,aAAW,KAC1C,EAAA,UAAM,WAAA,EAAA,GAAtB,EAGM,OAHN,GAGM,CAFJ,EAA0B,KAAA,MAAvB,UAAK,EAAG,EAAA,KAAQ,GAAA,CAAA,GACnB,EAA+D,UAAA;GAAvD,MAAK;GAAS,OAAM;GAAW,SAAO;KAAM,IAAE,CAAA,CAAA,KAEhC,EAAA,SAAA,EAAA,GAAxB,EAAuD,GAAA;;GAAzB,KAAK,EAAA;GAAM,MAAM,EAAA"}
|
|
@@ -2,6 +2,8 @@ import e from "../_virtual/_plugin-vue_export-helper.js";
|
|
|
2
2
|
import t from "./ZipBrowser.vue_vue_type_script_setup_true_lang.js";
|
|
3
3
|
/* empty css */
|
|
4
4
|
//#region src/components/ZipBrowser.vue
|
|
5
|
-
var n = /*#__PURE__*/ e(t, [["__scopeId", "data-v-
|
|
5
|
+
var n = /*#__PURE__*/ e(t, [["__scopeId", "data-v-3b2fe067"]]);
|
|
6
6
|
//#endregion
|
|
7
7
|
export { n as default };
|
|
8
|
+
|
|
9
|
+
//# sourceMappingURL=ZipBrowser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ZipBrowser.js","names":[],"sources":["../../src/components/ZipBrowser.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n computed,\n defineAsyncComponent,\n onBeforeUnmount,\n onMounted,\n reactive,\n ref,\n watch,\n} from 'vue'\nimport {\n extractZipEntry,\n readZip,\n zipSupported,\n ZipEncryptedError,\n ZipPasswordError,\n type ZipEntry,\n} from '@/lib/zip'\nimport type { DicomSlice } from '@/lib/dicom'\nimport type { ViewerFile } from '@/composables/useFileViewer'\nimport { fileExt, isDicomFile, isTextFile } from '@/lib/fileType'\nimport { canToggleView, classifyViewerFile, mediaMimeForName } from '@/lib/viewerKind'\nimport { buildArchiveHtmlPreview } from '@/lib/archiveHtml'\nimport { decodeTextSniff } from '@/lib/encoding'\nimport { formatBytes } from '@/lib/format'\nimport Icon from '@/components/icons/Icon.vue'\n\n// A folder of sibling .dcm files browses as one scrollable series — FileViewerBody has no slice model, so the\n// DICOM stack stays a direct hand-off here (a lone .dcm goes through the body like any other entry).\nconst DicomViewer = defineAsyncComponent(() => import('@/components/DicomViewer.vue'))\n\n// The shared side-viewer body renders a selected entry with the SAME viewers as a top-level file. Lazy-imported\n// to break the module cycle (the body lazy-imports this browser back, for nested archives).\nconst FileViewerBody = defineAsyncComponent(() => import('@/components/FileViewerBody.vue'))\n\n/**\n * The side viewer's ZIP mode: fetch an archive's bytes, decompress them entirely in-browser (see lib/zip.ts),\n * and browse the contents. A master→detail within the narrow pane — a collapsible file tree, and selecting an\n * entry extracts just that one, wraps it in a blob URL, and hands it to {@link FileViewerBody} (so a file\n * inside the zip previews with every viewer a top-level file gets — code/image/pdf/cert/audio/nested-zip/…).\n * HTML is the one multi-entry case: its relative JS/CSS/image/font dependencies are resolved from the same ZIP\n * and rewritten before entering the existing sandboxed iframe. Other entry bytes are not retained; selecting\n * decodes on demand, and the download button re-extracts.\n */\nconst props = defineProps<{ url: string; name: string }>()\n\ntype Status = 'loading' | 'ready' | 'error'\nconst status = ref<Status>('loading')\nconst errorMsg = ref('')\nlet buffer: ArrayBuffer | null = null\nconst entries = ref<ZipEntry[]>([])\n\n// Encrypted archives: a password bar appears once any entry is encrypted. The password is held only in memory\n// and passed to each extraction (ZipCrypto / WinZip AES decrypt happens in lib/zip.ts).\nconst password = ref('')\nconst pwError = ref('')\nconst unlocked = ref(false)\nconst anyEncrypted = computed(() => entries.value.some((e) => e.encrypted))\n\ninterface TreeNode {\n name: string\n path: string\n isDir: boolean\n size: number\n entry?: ZipEntry\n children: TreeNode[]\n}\nconst root = ref<TreeNode>({ name: '', path: '', isDir: true, size: 0, children: [] })\nconst expanded = reactive(new Set<string>())\n\nconst fileCount = computed(() => entries.value.reduce((n, e) => n + (e.isDir ? 0 : 1), 0))\nconst totalSize = computed(() => entries.value.reduce((s, e) => s + (e.isDir ? 0 : e.size), 0))\n\n// Build a folder tree from the flat entry paths (directories may be implicit — derive them either way).\nfunction buildTree(list: ZipEntry[]): TreeNode {\n const rootNode: TreeNode = { name: '', path: '', isDir: true, size: 0, children: [] }\n const dirs = new Map<string, TreeNode>([['', rootNode]])\n const ensureDir = (segs: string[]): TreeNode => {\n let cur = rootNode\n let acc = ''\n for (const seg of segs) {\n acc = acc ? `${acc}/${seg}` : seg\n let node = dirs.get(acc)\n if (!node) {\n node = { name: seg, path: acc, isDir: true, size: 0, children: [] }\n dirs.set(acc, node)\n cur.children.push(node)\n }\n cur = node\n }\n return cur\n }\n for (const e of list) {\n const segs = e.path.split('/').filter(Boolean)\n if (e.isDir) {\n ensureDir(segs)\n continue\n }\n const fname = segs.pop()\n if (!fname) continue\n ensureDir(segs).children.push({\n name: fname,\n path: e.path,\n isDir: false,\n size: e.size,\n entry: e,\n children: [],\n })\n }\n sortNode(rootNode)\n return rootNode\n}\n// Folders first, then files, each alphabetical (case-insensitive).\nfunction sortNode(n: TreeNode): void {\n n.children.sort((a, b) => (a.isDir === b.isDir ? a.name.localeCompare(b.name) : a.isDir ? -1 : 1))\n for (const c of n.children) if (c.isDir) sortNode(c)\n}\n\n// Flatten the tree to the rows currently visible (respecting which folders are expanded).\ninterface Row {\n node: TreeNode\n depth: number\n}\nconst rows = computed<Row[]>(() => {\n const out: Row[] = []\n const walk = (nodes: TreeNode[], depth: number): void => {\n for (const n of nodes) {\n out.push({ node: n, depth })\n if (n.isDir && expanded.has(n.path)) walk(n.children, depth + 1)\n }\n }\n walk(root.value.children, 0)\n return out\n})\n\nasync function load(): Promise<void> {\n selectionRun++\n status.value = 'loading'\n errorMsg.value = ''\n selected.value = null\n entryFile.value = null\n entryHtmlPreview.value = ''\n password.value = ''\n pwError.value = ''\n unlocked.value = false\n revokeBlob()\n stackSlices.value = []\n if (!zipSupported()) {\n errorMsg.value = '当前浏览器不支持在前端解压 ZIP(缺少 DecompressionStream)'\n status.value = 'error'\n return\n }\n try {\n const res = await fetch(props.url)\n if (!res.ok) throw new Error(`HTTP ${res.status}`)\n buffer = await res.arrayBuffer()\n const list = readZip(buffer)\n entries.value = list\n root.value = buildTree(list)\n expanded.clear()\n status.value = 'ready'\n } catch (e) {\n errorMsg.value = e instanceof Error ? e.message : String(e)\n status.value = 'error'\n }\n}\n\n// ── selected entry (detail view) ──────────────────────────────────────────────────────────────\nconst selected = ref<ZipEntry | null>(null)\nconst entryStatus = ref<Status>('ready') // covers extraction; the body owns loading once entryFile is set\nconst entryErr = ref('')\nconst entryText = ref('') // the body emits loaded text up so the copy button can use it\nconst entryFile = ref<ViewerFile | null>(null)\nconst entryHtmlPreview = ref('') // dependency-rewritten HTML; source/copy still use the original text\nconst entryStack = ref(false) // DICOM series (handled directly, not via the body)\nconst entryMode = ref<'render' | 'source'>('render')\nconst blobUrl = ref('') // entry blob URL fed to the body (revoked on change/unmount)\nlet selectionRun = 0 // stale guard for async HTML dependency extraction (including selecting the same row twice)\n\n// DICOM series stack: when the clicked .dcm has sibling .dcm files in the same folder, they are browsed as\n// one scrollable series. Slices extract lazily; the DICOM viewer caches/prefetches them.\nconst stackSlices = ref<DicomSlice[]>([])\nconst stackStart = ref(0)\nconst stackName = ref('')\nconst stackTotalSize = computed(() => stackSlices.value.reduce((s, sl) => s + (sl.size ?? 0), 0))\n\nconst canToggle = computed(() => canToggleView(entryFile.value, entryText.value))\n\nfunction revokeBlob(): void {\n if (blobUrl.value) {\n URL.revokeObjectURL(blobUrl.value)\n blobUrl.value = ''\n }\n}\n\nfunction dirOf(path: string): string {\n const i = path.lastIndexOf('/')\n return i < 0 ? '' : path.slice(0, i)\n}\n// Sibling .dcm files in the same folder, natural-sorted (numeric-aware) so e.g. img2 precedes img10.\nfunction dicomSiblings(e: ZipEntry): ZipEntry[] {\n const dir = dirOf(e.path)\n return entries.value\n .filter((x) => !x.isDir && isDicomFile(x.name) && dirOf(x.path) === dir)\n .sort((a, b) => a.path.localeCompare(b.path, undefined, { numeric: true, sensitivity: 'base' }))\n}\n\n// Treat as text if the name says so; for extensionless entries, sniff the first bytes for NULs. Feeds the\n// body's source fall-back so an extensionless text file still opens as source instead of a download card.\nfunction looksText(bytes: Uint8Array, name: string): boolean {\n if (isTextFile(name)) return true\n if (fileExt(name)) return false\n const n = Math.min(bytes.length, 4096)\n for (let i = 0; i < n; i++) if (bytes[i] === 0) return false\n return true\n}\n\nasync function selectEntry(e: ZipEntry): Promise<void> {\n const run = ++selectionRun\n let pendingUrl = ''\n selected.value = e\n entryStatus.value = 'loading'\n entryErr.value = ''\n entryText.value = ''\n entryFile.value = null\n entryHtmlPreview.value = ''\n entryStack.value = false\n entryMode.value = 'render'\n revokeBlob()\n stackSlices.value = []\n if (!buffer) return\n try {\n // DICOM series: a folder holding several .dcm → browse them as one scrollable series (slices on demand).\n if (isDicomFile(e.name)) {\n const siblings = dicomSiblings(e)\n if (siblings.length > 1) {\n stackSlices.value = siblings.map((se) => ({\n name: se.name,\n size: se.size,\n load: async () => {\n const u = await extractZipEntry(buffer as ArrayBuffer, se, password.value)\n const out = new ArrayBuffer(u.byteLength)\n new Uint8Array(out).set(u)\n return out\n },\n }))\n stackStart.value = Math.max(\n 0,\n siblings.findIndex((se) => se.path === e.path),\n )\n stackName.value = dirOf(e.path).split('/').pop() || e.name\n entryStack.value = true\n entryStatus.value = 'ready'\n return\n }\n }\n const archiveBuffer = buffer\n const bytes = await extractZipEntry(archiveBuffer, e, password.value)\n if (selectionRun !== run || selected.value !== e) return\n const url = URL.createObjectURL(new Blob([bytes], { type: mediaMimeForName(e.name) }))\n pendingUrl = url\n const file = classifyViewerFile({\n name: e.name,\n url,\n size: e.size,\n textHint: looksText(bytes, e.name),\n })\n let htmlPreview = ''\n if (file.html) {\n const source = decodeTextSniff(bytes)\n file.content = source\n const preview = await buildArchiveHtmlPreview({\n html: source,\n documentPath: e.path,\n entries: entries.value,\n loadEntry: (dependency) => extractZipEntry(archiveBuffer, dependency, password.value),\n })\n if (selectionRun !== run || selected.value !== e) {\n URL.revokeObjectURL(url)\n pendingUrl = ''\n return\n }\n htmlPreview = preview.html\n }\n if (selectionRun !== run || selected.value !== e) {\n URL.revokeObjectURL(url)\n pendingUrl = ''\n return\n }\n blobUrl.value = url\n pendingUrl = ''\n entryHtmlPreview.value = htmlPreview\n entryFile.value = file\n entryStatus.value = 'ready'\n } catch (err) {\n if (pendingUrl) URL.revokeObjectURL(pendingUrl)\n if (err instanceof ZipEncryptedError)\n entryErr.value = '该文件已加密:请返回列表并在顶部输入密码'\n else if (err instanceof ZipPasswordError) {\n entryErr.value = '密码错误'\n unlocked.value = false\n } else entryErr.value = err instanceof Error ? err.message : String(err)\n entryStatus.value = 'error'\n }\n}\n\n// Validate the entered password against the first encrypted entry, then unlock (and re-open any selected file).\nasync function applyPassword(): Promise<void> {\n pwError.value = ''\n const enc = entries.value.find((e) => !e.isDir && e.encrypted && e.size > 0)\n if (!enc || !buffer) {\n unlocked.value = true\n return\n }\n try {\n await extractZipEntry(buffer, enc, password.value)\n unlocked.value = true\n if (selected.value) void selectEntry(selected.value)\n } catch (e) {\n unlocked.value = false\n pwError.value =\n e instanceof ZipPasswordError ? '密码错误' : e instanceof Error ? e.message : String(e)\n }\n}\n\nfunction onRow(node: TreeNode): void {\n if (node.isDir) {\n if (expanded.has(node.path)) expanded.delete(node.path)\n else expanded.add(node.path)\n } else if (node.entry) {\n void selectEntry(node.entry)\n }\n}\n\nfunction back(): void {\n selectionRun++\n selected.value = null\n entryText.value = ''\n entryFile.value = null\n entryHtmlPreview.value = ''\n entryStack.value = false\n revokeBlob()\n stackSlices.value = []\n}\n\n// Re-extract on demand so we don't retain decompressed bytes for the whole session.\nasync function downloadEntry(e: ZipEntry): Promise<void> {\n if (!buffer) return\n try {\n const url = URL.createObjectURL(new Blob([await extractZipEntry(buffer, e, password.value)]))\n const a = document.createElement('a')\n a.href = url\n a.download = e.name\n document.body.appendChild(a)\n a.click()\n a.remove()\n setTimeout(() => URL.revokeObjectURL(url), 1000)\n } catch {\n /* extraction failed — nothing to download */\n }\n}\n\nconst copied = ref(false)\nlet copyTimer: ReturnType<typeof setTimeout> | undefined\nasync function copyEntry(): Promise<void> {\n try {\n await navigator.clipboard?.writeText(entryText.value)\n } catch {\n /* clipboard unavailable — ignore */\n }\n copied.value = true\n clearTimeout(copyTimer)\n copyTimer = setTimeout(() => (copied.value = false), 1400)\n}\n\nonMounted(load)\nwatch(() => props.url, load)\nonBeforeUnmount(() => {\n selectionRun++\n revokeBlob()\n clearTimeout(copyTimer)\n})\n</script>\n\n<template>\n <div class=\"zip\">\n <p v-if=\"status === 'loading'\" class=\"zip__msg\">正在解压…</p>\n\n <div v-else-if=\"status === 'error'\" class=\"zip__msg zip__msg--error\">\n <p>{{ errorMsg }}</p>\n <button type=\"button\" class=\"zip__retry\" @click=\"load\">重试</button>\n </div>\n\n <!-- detail: one selected entry -->\n <template v-else-if=\"selected\">\n <div class=\"zip__bar\">\n <button type=\"button\" class=\"zip__back\" title=\"返回列表\" @click=\"back\">\n <Icon name=\"arrowLeft\" :size=\"16\" />\n </button>\n <span class=\"zip__path\" :title=\"selected.path\">{{ selected.path }}</span>\n <span class=\"zip__bsize\">{{ formatBytes(selected.size) }}</span>\n <div v-if=\"canToggle\" class=\"zip__view\" role=\"group\" aria-label=\"渲染或源码\">\n <button\n type=\"button\"\n class=\"zip__vbtn\"\n :class=\"{ 'zip__vbtn--on': entryMode === 'render' }\"\n title=\"渲染\"\n @click=\"entryMode = 'render'\"\n >\n <Icon name=\"eye\" :size=\"14\" />\n </button>\n <button\n type=\"button\"\n class=\"zip__vbtn\"\n :class=\"{ 'zip__vbtn--on': entryMode === 'source' }\"\n title=\"源码\"\n @click=\"entryMode = 'source'\"\n >\n <Icon name=\"code\" :size=\"14\" />\n </button>\n </div>\n <button\n v-if=\"entryText && entryStatus === 'ready'\"\n type=\"button\"\n class=\"zip__act\"\n :title=\"copied ? '已复制' : '复制'\"\n @click=\"copyEntry\"\n >\n <Icon name=\"copy\" :size=\"14\" />\n </button>\n <button type=\"button\" class=\"zip__act\" title=\"下载\" @click=\"downloadEntry(selected)\">\n <Icon name=\"download\" :size=\"15\" />\n </button>\n </div>\n\n <p v-if=\"entryStatus === 'loading'\" class=\"zip__msg\">读取中…</p>\n <div v-else-if=\"entryStatus === 'error'\" class=\"zip__msg zip__msg--error\">\n <p>{{ entryErr }}</p>\n </div>\n\n <!-- dicom series: a whole folder of .dcm slices browsed as one scrollable series (wheel/cine翻层) -->\n <DicomViewer\n v-else-if=\"entryStack\"\n :slices=\"stackSlices\"\n :start=\"stackStart\"\n :name=\"stackName\"\n :size=\"stackTotalSize\"\n />\n\n <!-- everything else: the shared body picks the right viewer from the extracted blob -->\n <FileViewerBody\n v-else-if=\"entryFile\"\n :file=\"entryFile\"\n :mode=\"entryMode\"\n :html-preview=\"entryHtmlPreview || undefined\"\n @loaded=\"entryText = $event\"\n />\n </template>\n\n <!-- list: the file tree -->\n <template v-else>\n <div class=\"zip__summary\">{{ fileCount }} 个文件 · {{ formatBytes(totalSize) }}</div>\n\n <!-- password bar: shown when the archive has encrypted entries (ZipCrypto / WinZip AES) -->\n <div v-if=\"anyEncrypted\" class=\"zip__pw\" :class=\"{ 'zip__pw--ok': unlocked }\">\n <Icon name=\"key\" :size=\"14\" />\n <input\n v-model=\"password\"\n type=\"password\"\n class=\"zip__pwinput\"\n :placeholder=\"unlocked ? '已解锁(如需可重新输入)' : '此压缩包已加密,输入密码解锁'\"\n @keyup.enter=\"applyPassword\"\n />\n <button type=\"button\" class=\"zip__pwbtn\" @click=\"applyPassword\">\n {{ unlocked ? '已解锁' : '解锁' }}\n </button>\n <span v-if=\"pwError\" class=\"zip__pwerr\">{{ pwError }}</span>\n </div>\n\n <div class=\"zip__scroll zip__tree\">\n <p v-if=\"rows.length === 0\" class=\"zip__msg\">空压缩包</p>\n <button\n v-for=\"row in rows\"\n :key=\"row.node.path\"\n type=\"button\"\n class=\"zip__row\"\n :class=\"{ 'zip__row--dir': row.node.isDir }\"\n :style=\"{ paddingLeft: `${12 + row.depth * 14}px` }\"\n @click=\"onRow(row.node)\"\n >\n <span class=\"zip__twisty\">\n <Icon\n v-if=\"row.node.isDir\"\n name=\"chevronRight\"\n :size=\"13\"\n :class=\"{ 'zip__twisty--open': expanded.has(row.node.path) }\"\n />\n </span>\n <span class=\"zip__rowicon\">\n <Icon :name=\"row.node.isDir ? 'folder' : 'fileOutline'\" :size=\"15\" />\n </span>\n <span class=\"zip__rowname\">{{ row.node.name }}</span>\n <Icon\n v-if=\"row.node.entry?.encrypted\"\n name=\"key\"\n :size=\"12\"\n class=\"zip__rowlock\"\n title=\"已加密\"\n />\n <span v-if=\"!row.node.isDir\" class=\"zip__rowsize\">{{ formatBytes(row.node.size) }}</span>\n </button>\n </div>\n </template>\n </div>\n</template>\n\n<style scoped>\n.zip {\n flex: 1 1 auto;\n min-height: 0;\n display: flex;\n flex-direction: column;\n background: var(--surface);\n}\n\n.zip__msg {\n padding: 20px 16px;\n color: var(--muted);\n font-size: 13px;\n}\n.zip__msg--error {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 10px;\n color: var(--danger);\n}\n.zip__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.zip__retry:hover {\n border-color: var(--accent);\n color: var(--accent);\n}\n\n/* List view ------------------------------------------------------------------------------------ */\n.zip__summary {\n flex-shrink: 0;\n padding: 9px 16px;\n border-bottom: 1px solid var(--border);\n font-size: 11.5px;\n letter-spacing: 0.02em;\n color: var(--faint);\n}\n/* Password bar (encrypted archives). */\n.zip__pw {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-shrink: 0;\n padding: 8px 12px;\n border-bottom: 1px solid var(--border);\n background: var(--surface-2);\n color: var(--muted);\n}\n.zip__pw--ok {\n color: var(--accent);\n}\n.zip__pwinput {\n flex: 1 1 auto;\n min-width: 0;\n height: 28px;\n padding: 0 9px;\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: 7px;\n}\n.zip__pwinput:focus {\n outline: none;\n border-color: var(--accent);\n}\n.zip__pwbtn {\n flex-shrink: 0;\n height: 28px;\n padding: 0 12px;\n font-family: inherit;\n font-size: 12px;\n color: var(--text);\n background: var(--surface);\n border: 1px solid var(--border-2);\n border-radius: 7px;\n cursor: pointer;\n}\n.zip__pwbtn:hover {\n border-color: var(--accent);\n color: var(--accent);\n}\n.zip__pwerr {\n flex-shrink: 0;\n font-size: 11.5px;\n color: var(--danger);\n}\n.zip__rowlock {\n flex-shrink: 0;\n color: var(--faint);\n}\n\n.zip__scroll {\n flex: 1 1 auto;\n min-height: 0;\n overflow: auto;\n overscroll-behavior: contain;\n}\n.zip__tree {\n padding: 6px 0;\n}\n.zip__row {\n display: flex;\n align-items: center;\n gap: 7px;\n width: 100%;\n padding: 5px 12px 5px 12px;\n background: transparent;\n border: none;\n font-family: inherit;\n font-size: 12.5px;\n color: var(--text);\n text-align: left;\n cursor: pointer;\n}\n.zip__row:hover {\n background: var(--surface-2);\n}\n.zip__twisty {\n flex-shrink: 0;\n display: inline-flex;\n width: 13px;\n color: var(--faint);\n}\n.zip__twisty--open {\n transform: rotate(90deg);\n transition: transform 0.12s;\n}\n.zip__rowicon {\n flex-shrink: 0;\n display: inline-flex;\n color: var(--faint);\n}\n.zip__row--dir .zip__rowicon {\n color: var(--accent);\n}\n.zip__rowname {\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.zip__row--dir .zip__rowname {\n font-weight: 500;\n}\n.zip__rowsize {\n flex-shrink: 0;\n font-size: 11px;\n color: var(--faint);\n font-variant-numeric: tabular-nums;\n}\n\n/* Detail bar ----------------------------------------------------------------------------------- */\n.zip__bar {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-shrink: 0;\n padding: 7px 10px 7px 8px;\n border-bottom: 1px solid var(--border);\n background: var(--surface);\n}\n.zip__back,\n.zip__act {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n width: 30px;\n height: 28px;\n background: transparent;\n border: none;\n border-radius: 7px;\n color: var(--muted);\n cursor: pointer;\n transition:\n background 0.15s,\n color 0.15s;\n}\n.zip__back:hover,\n.zip__act:hover {\n background: var(--surface-2);\n color: var(--accent);\n}\n.zip__path {\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n direction: rtl;\n text-align: left;\n font-size: 12.5px;\n font-weight: 500;\n color: var(--text-strong);\n}\n.zip__bsize {\n flex-shrink: 0;\n font-size: 11px;\n color: var(--faint);\n font-variant-numeric: tabular-nums;\n}\n/* Render/source toggle (when the selected entry supports it) — compact segmented control. */\n.zip__view {\n display: inline-flex;\n align-items: center;\n flex-shrink: 0;\n gap: 2px;\n padding: 2px;\n border-radius: 7px;\n background: var(--surface-2);\n}\n.zip__vbtn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 26px;\n height: 24px;\n padding: 0;\n background: transparent;\n border: none;\n border-radius: 5px;\n color: var(--faint);\n cursor: pointer;\n}\n.zip__vbtn--on {\n background: var(--surface);\n color: var(--accent);\n box-shadow: var(--shadow);\n}\n</style>\n"],"mappings":""}
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
* and browse the contents. A master→detail within the narrow pane — a collapsible file tree, and selecting an
|
|
4
4
|
* entry extracts just that one, wraps it in a blob URL, and hands it to {@link FileViewerBody} (so a file
|
|
5
5
|
* inside the zip previews with every viewer a top-level file gets — code/image/pdf/cert/audio/nested-zip/…).
|
|
6
|
-
*
|
|
6
|
+
* HTML is the one multi-entry case: its relative JS/CSS/image/font dependencies are resolved from the same ZIP
|
|
7
|
+
* and rewritten before entering the existing sandboxed iframe. Other entry bytes are not retained; selecting
|
|
8
|
+
* decodes on demand, and the download button re-extracts.
|
|
7
9
|
*/
|
|
8
10
|
type __VLS_Props = {
|
|
9
11
|
url: string;
|