diff --git a/web/package-lock.json b/web/package-lock.json index 38407cfbe3..8b49b90401 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -10,6 +10,7 @@ "@ant-design/icons": "^5.2.6", "@antv/g2": "^5.2.10", "@antv/g6": "^5.1.0", + "@extend-ai/react-docx": "^0.6.7", "@floating-ui/react": "^0.27.19", "@hookform/resolvers": "^3.9.1", "@js-preview/excel": "^1.7.14", @@ -73,7 +74,6 @@ "lexical": "^0.23.1", "lodash": "^4.17.23", "lucide-react": "^1.7.0", - "mammoth": "^1.7.2", "next-themes": "^0.4.6", "openai-speech-stream-player": "^1.0.8", "papaparse": "^5.5.3", @@ -1275,6 +1275,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@chenglou/pretext": { + "version": "0.0.5", + "resolved": "https://registry.npmmirror.com/@chenglou/pretext/-/pretext-0.0.5.tgz", + "integrity": "sha512-A8GZN10REdFGsyuiUgLV8jjPDDFMg5GmgxGWV0I3igxBOnzj+jgz2VMmVD7g+SFyoctfeqHFxbNatKSzVRWtRg==", + "license": "MIT" + }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmmirror.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -2014,6 +2020,22 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@extend-ai/react-docx": { + "version": "0.6.7", + "resolved": "https://registry.npmmirror.com/@extend-ai/react-docx/-/react-docx-0.6.7.tgz", + "integrity": "sha512-4z95OFWNYKOEzIVKxoe78Eg6tS+Cu3gA3HnF7C0DEozK+ThECh8U1xSmiqZwp4bjC2OSxvMUtgNNhkEkl6YjAQ==", + "license": "MIT", + "dependencies": { + "@chenglou/pretext": "^0.0.5", + "@tanstack/react-virtual": "^3.13.12", + "fast-png": "^8.0.0", + "utif": "^3.1.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, "node_modules/@ffmpeg/ffmpeg": { "version": "0.11.6", "resolved": "https://registry.npmmirror.com/@ffmpeg/ffmpeg/-/ffmpeg-0.11.6.tgz", @@ -7685,6 +7707,23 @@ "react-dom": ">=16.8" } }, + "node_modules/@tanstack/react-virtual": { + "version": "3.14.2", + "resolved": "https://registry.npmmirror.com/@tanstack/react-virtual/-/react-virtual-3.14.2.tgz", + "integrity": "sha512-IpWnmCLvuymRfeeLNVXIzNEYBFLpd3drVIS91sqV78VTZFyldlChkOocZRCPp1B+Wnk09bcLNme8WaMU/9/9bQ==", + "license": "MIT", + "dependencies": { + "@tanstack/virtual-core": "3.17.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/@tanstack/table-core": { "version": "8.21.3", "resolved": "https://registry.npmmirror.com/@tanstack/table-core/-/table-core-8.21.3.tgz", @@ -7698,6 +7737,16 @@ "url": "https://github.com/sponsors/tannerlinsley" } }, + "node_modules/@tanstack/virtual-core": { + "version": "3.17.0", + "resolved": "https://registry.npmmirror.com/@tanstack/virtual-core/-/virtual-core-3.17.0.tgz", + "integrity": "sha512-gOxY/hFkPh/XQYhnThBHzkbkX3Ed+z/iushyz+R+JAr213aXxUDgQoTgTdrDpBSRsjFM73P/KfUyWmaF9WHMkQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, "node_modules/@testing-library/dom": { "version": "10.4.1", "resolved": "https://registry.npmmirror.com/@testing-library/dom/-/dom-10.4.1.tgz", @@ -9247,15 +9296,6 @@ "react": "^18" } }, - "node_modules/@xmldom/xmldom": { - "version": "0.8.11", - "resolved": "https://registry.npmmirror.com/@xmldom/xmldom/-/xmldom-0.8.11.tgz", - "integrity": "sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmmirror.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz", @@ -10075,26 +10115,6 @@ "node": ">= 0.6.0" } }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/baseline-browser-mapping": { "version": "2.9.11", "resolved": "https://registry.npmmirror.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.11.tgz", @@ -10140,12 +10160,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/bluebird": { - "version": "3.4.7", - "resolved": "https://registry.npmmirror.com/bluebird/-/bluebird-3.4.7.tgz", - "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==", - "license": "MIT" - }, "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmmirror.com/boolbase/-/boolbase-1.0.0.tgz", @@ -12010,12 +12024,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/dingbat-to-unicode": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/dingbat-to-unicode/-/dingbat-to-unicode-1.0.1.tgz", - "integrity": "sha512-98l0sW87ZT58pU4i61wa2OHwxbiYSbuxsCBozaVnYX2iCnr3bLM3fIes1/ej7h1YdOKuKt/MLs706TVnALA65w==", - "license": "BSD-2-Clause" - }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmmirror.com/dir-glob/-/dir-glob-3.0.1.tgz", @@ -12224,15 +12232,6 @@ "node": ">=12" } }, - "node_modules/duck": { - "version": "0.1.12", - "resolved": "https://registry.npmmirror.com/duck/-/duck-0.1.12.tgz", - "integrity": "sha512-wkctla1O6VfP89gQ+J/yDesM0S7B7XLXjKGzXxMDVFg7uEn706niAtyYovKbyq1oT9YwDcly721/iUWoc8MVRg==", - "license": "BSD", - "dependencies": { - "underscore": "^1.13.1" - } - }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -13419,6 +13418,16 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-png": { + "version": "8.0.0", + "resolved": "https://registry.npmmirror.com/fast-png/-/fast-png-8.0.0.tgz", + "integrity": "sha512-gCysNasJ8KEMgfdYIKd/wTDo6ENK1PWT0RJO7O+0pgmuHPw2O6tA1WvdxFRJoLf9V8yFYpG0FA1YgI8X97OhJA==", + "license": "MIT", + "dependencies": { + "fflate": "^0.8.2", + "iobuffer": "^6.0.1" + } + }, "node_modules/fast-uri": { "version": "3.1.0", "resolved": "https://registry.npmmirror.com/fast-uri/-/fast-uri-3.1.0.tgz", @@ -13490,6 +13499,12 @@ "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", "license": "MIT" }, + "node_modules/fflate": { + "version": "0.8.3", + "resolved": "https://registry.npmmirror.com/fflate/-/fflate-0.8.3.tgz", + "integrity": "sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==", + "license": "MIT" + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmmirror.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -15373,6 +15388,12 @@ "deprecated": "The Intersection Observer polyfill is no longer needed and can safely be removed. Intersection Observer has been Baseline since 2019.", "license": "Apache-2.0" }, + "node_modules/iobuffer": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/iobuffer/-/iobuffer-6.0.1.tgz", + "integrity": "sha512-SZWYkWNfjIXIBYSDpXDYIgshqtbOPsi4lviawAEceR1Kqk+sHDlcQjWrzNQsii80AyBY0q5c8HCTNjqo74ul+Q==", + "license": "MIT" + }, "node_modules/is-alphabetical": { "version": "2.0.1", "resolved": "https://registry.npmmirror.com/is-alphabetical/-/is-alphabetical-2.0.1.tgz", @@ -17990,17 +18011,6 @@ "loose-envify": "cli.js" } }, - "node_modules/lop": { - "version": "0.4.2", - "resolved": "https://registry.npmmirror.com/lop/-/lop-0.4.2.tgz", - "integrity": "sha512-RefILVDQ4DKoRZsJ4Pj22TxE3omDO47yFpkIBoDKzkqPRISs5U1cnAdg/5583YPkWPaLIYHOKRMQSvjFsO26cw==", - "license": "BSD-2-Clause", - "dependencies": { - "duck": "^0.1.12", - "option": "~0.2.1", - "underscore": "^1.13.1" - } - }, "node_modules/loupe": { "version": "3.2.1", "resolved": "https://registry.npmmirror.com/loupe/-/loupe-3.2.1.tgz", @@ -18114,39 +18124,6 @@ "tmpl": "1.0.5" } }, - "node_modules/mammoth": { - "version": "1.11.0", - "resolved": "https://registry.npmmirror.com/mammoth/-/mammoth-1.11.0.tgz", - "integrity": "sha512-BcEqqY/BOwIcI1iR5tqyVlqc3KIaMRa4egSoK83YAVrBf6+yqdAAbtUcFDCWX8Zef8/fgNZ6rl4VUv+vVX8ddQ==", - "license": "BSD-2-Clause", - "dependencies": { - "@xmldom/xmldom": "^0.8.6", - "argparse": "~1.0.3", - "base64-js": "^1.5.1", - "bluebird": "~3.4.0", - "dingbat-to-unicode": "^1.0.1", - "jszip": "^3.7.1", - "lop": "^0.4.2", - "path-is-absolute": "^1.0.0", - "underscore": "^1.13.1", - "xmlbuilder": "^10.0.0" - }, - "bin": { - "mammoth": "bin/mammoth" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/mammoth/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmmirror.com/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, "node_modules/markdown-extensions": { "version": "2.0.0", "resolved": "https://registry.npmmirror.com/markdown-extensions/-/markdown-extensions-2.0.0.tgz", @@ -19847,12 +19824,6 @@ "integrity": "sha512-4pYnxvvOpL1PVgyuXly2GXG7IyGZErKgoaRUZKTi84Sd2sRLYtu5YfcpVip1nbH6awvxDYaIRyQrKP4Jm1zzRA==", "license": "ISC" }, - "node_modules/option": { - "version": "0.2.4", - "resolved": "https://registry.npmmirror.com/option/-/option-0.2.4.tgz", - "integrity": "sha512-pkEqbDyl8ou5cpq+VsnQbe/WlEy5qS7xPzMS1U55OCG9KPvwFD46zDbxQIj3egJSFc3D+XhYOPUzz49zQAVy7A==", - "license": "BSD-2-Clause" - }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmmirror.com/optionator/-/optionator-0.9.4.tgz", @@ -25911,12 +25882,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/underscore": { - "version": "1.13.7", - "resolved": "https://registry.npmmirror.com/underscore/-/underscore-1.13.7.tgz", - "integrity": "sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==", - "license": "MIT" - }, "node_modules/undici-types": { "version": "7.16.0", "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-7.16.0.tgz", @@ -26220,6 +26185,15 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/utif": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/utif/-/utif-3.1.0.tgz", + "integrity": "sha512-WEo4D/xOvFW53K5f5QTaTbbiORcm2/pCL9P6qmJnup+17eYfKaEhDeX9PeQkuyEoIxlbGklDuGl8xwuXYMrrXQ==", + "license": "MIT", + "dependencies": { + "pako": "^1.0.5" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -27825,15 +27799,6 @@ "node": ">=12" } }, - "node_modules/xmlbuilder": { - "version": "10.1.1", - "resolved": "https://registry.npmmirror.com/xmlbuilder/-/xmlbuilder-10.1.1.tgz", - "integrity": "sha512-OyzrcFLL/nb6fMGHbiRDuPup9ljBycsdCypwuyg5AAHvyWzGfChJpCXMG88AGTIMFhGZ9RccFN1e6lhg3hkwKg==", - "license": "MIT", - "engines": { - "node": ">=4.0" - } - }, "node_modules/xmlchars": { "version": "2.2.0", "resolved": "https://registry.npmmirror.com/xmlchars/-/xmlchars-2.2.0.tgz", diff --git a/web/package.json b/web/package.json index 6dbed295b4..c527643032 100644 --- a/web/package.json +++ b/web/package.json @@ -32,6 +32,7 @@ "@ant-design/icons": "^5.2.6", "@antv/g2": "^5.2.10", "@antv/g6": "^5.1.0", + "@extend-ai/react-docx": "^0.6.7", "@floating-ui/react": "^0.27.19", "@hookform/resolvers": "^3.9.1", "@js-preview/excel": "^1.7.14", @@ -95,7 +96,6 @@ "lexical": "^0.23.1", "lodash": "^4.17.23", "lucide-react": "^1.7.0", - "mammoth": "^1.7.2", "next-themes": "^0.4.6", "openai-speech-stream-player": "^1.0.8", "papaparse": "^5.5.3", diff --git a/web/src/components/document-preview/doc-preview.tsx b/web/src/components/document-preview/doc-preview.tsx index 67d956d917..22af1da1c0 100644 --- a/web/src/components/document-preview/doc-preview.tsx +++ b/web/src/components/document-preview/doc-preview.tsx @@ -1,134 +1,242 @@ import message from '@/components/ui/message'; import { Spin } from '@/components/ui/spin'; import request from '@/utils/request'; +import { DocxEditorViewer, useDocxEditor } from '@extend-ai/react-docx'; import classNames from 'classnames'; -import mammoth from 'mammoth'; -import { useEffect, useState } from 'react'; +import { ZoomIn, ZoomOut } from 'lucide-react'; +import { useCallback, useEffect, useRef, useState } from 'react'; interface DocPreviewerProps { className?: string; url: string; } -// Word document preview component. Behavior: -// 1) Fetches the document as a Blob. -// 2) Detects .docx input via a ZIP header probe. -// 3) Renders .docx using Mammoth; presents a controlled "unsupported" notice for non-ZIP payloads. +// ZIP file header bytes "PK" +const ZIP_HEADER_0 = 0x50; +const ZIP_HEADER_1 = 0x4b; + +const isZipLikeBlob = async (blob: Blob): Promise => { + try { + const headerSlice = blob.slice(0, 4); + const buf = await headerSlice.arrayBuffer(); + const bytes = new Uint8Array(buf); + return ( + bytes.length >= 2 && + bytes[0] === ZIP_HEADER_0 && + bytes[1] === ZIP_HEADER_1 + ); + } catch (e) { + console.error('Failed to inspect blob header', e); + return false; + } +}; + +const ZOOM_STEPS = [25, 50, 75, 100, 125, 150, 175, 200] as const; + +const clampZoom = (scale: number, direction: 1 | -1): number => { + let idx = ZOOM_STEPS.indexOf(scale as (typeof ZOOM_STEPS)[number]); + if (idx < 0) { + if (direction > 0) { + idx = ZOOM_STEPS.findIndex((v) => v > scale); + } else { + for (let i = ZOOM_STEPS.length - 1; i >= 0; i--) { + if (ZOOM_STEPS[i] < scale) { + idx = i; + break; + } + } + } + } + idx = Math.max( + 0, + Math.min(ZOOM_STEPS.length - 1, idx < 0 ? 0 : idx + direction), + ); + return ZOOM_STEPS[idx] ?? scale; +}; + +// Word document preview component. +// Uses @extend-ai/react-docx for canvas-based page-level rendering. +// Falls back to an unsupported notice for legacy .doc (non-ZIP) payloads. export const DocPreviewer: React.FC = ({ className, url, }) => { - const [htmlContent, setHtmlContent] = useState(''); + const editor = useDocxEditor({ initialFileName: 'document.docx' }); + const { importDocxFile, status, totalPages } = editor; const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + const [zoomScale, setZoomScale] = useState(100); + const cancelledRef = useRef(false); - // Determines whether the Blob represents a .docx document by checking for the ZIP - // file signature ("PK") in the initial bytes. A valid .docx file is a ZIP container - // and always begins with: - // 50 4B 03 04 ("PK..") - // - // Legacy .doc files use the CFBF binary format, commonly starting with: - // D0 CF 11 E0 A1 B1 1A E1 - // - // Note that some files distributed with a “.doc” extension may internally be .docx - // documents (e.g., renamed files or files produced by systems that export .docx - // content under a .doc filename). These files will still present the ZIP signature - // and are therefore treated as supported .docx payloads. The header inspection - // ensures correct routing regardless of filename or reported extension. - const isZipLikeBlob = async (blob: Blob): Promise => { - try { - const headerSlice = blob.slice(0, 4); - const buf = await headerSlice.arrayBuffer(); - const bytes = new Uint8Array(buf); - - // ZIP files start with "PK" (0x50, 0x4B) - return bytes.length >= 2 && bytes[0] === 0x50 && bytes[1] === 0x4b; - } catch (e) { - console.error('Failed to inspect blob header', e); - return false; - } - }; - - const fetchDocument = async () => { + // Fetch the document blob and load it into the editor + const fetchDocument = useCallback(async () => { if (!url) return; + cancelledRef.current = false; setLoading(true); + setError(null); - const res = await request(url, { - method: 'GET', - responseType: 'blob', - onError: () => { - message.error('Document parsing failed'); - console.error('Error loading document:', url); - }, - }); + let res; + try { + res = await request(url, { + method: 'GET', + responseType: 'blob', + onError: () => { + if (!cancelledRef.current) { + message.error('Document parsing failed'); + console.error('Error loading document:', url); + } + }, + }); + } catch { + if (!cancelledRef.current) { + setError('Failed to fetch document.'); + setLoading(false); + } + return; + } + + if (cancelledRef.current) return; try { const blob: Blob = res.data; - const contentType: string = - blob.type || (res as any).headers?.['content-type'] || ''; - - // Execution path selection: ZIP-like payloads are treated as .docx and rendered via Mammoth; - // non-ZIP payloads receive an explicit unsupported notice. const looksLikeZip = await isZipLikeBlob(blob); if (!looksLikeZip) { - // Non-ZIP payload (likely legacy .doc or another format): skip Mammoth processing. - setHtmlContent(` -
-
-

- Preview is not available for this Word document -

-

- Mammoth supports modern .docx files only.
- The file header does not indicate a .docx ZIP archive. -

-
-
- `); + setError( + 'This file header does not indicate a .docx ZIP archive. Only .docx files are supported.', + ); + setLoading(false); return; } - // ZIP-like payload: parse as .docx with Mammoth - const arrayBuffer = await blob.arrayBuffer(); - const result = await mammoth.convertToHtml( - { arrayBuffer }, - { includeDefaultStyleMap: true }, - ); + const file = new File([blob], 'document.docx', { + type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + }); - const styledContent = result.value - .replace(/

/g, '

') - .replace(//g, ''); + await importDocxFile(file); - setHtmlContent(styledContent); + if (!cancelledRef.current) { + setZoomScale(100); + setLoading(false); + } } catch (err) { - message.error('Failed to parse document.'); - console.error('Error parsing document:', err); - } finally { - setLoading(false); + if (!cancelledRef.current) { + message.error('Failed to parse document.'); + console.error('Error parsing document:', err); + setLoading(false); + } } - }; + }, [url, importDocxFile]); useEffect(() => { - if (url) { - fetchDocument(); + fetchDocument(); + return () => { + cancelledRef.current = true; + }; + }, [fetchDocument]); + + // Monitor editor status for library-level errors + useEffect(() => { + if (status === 'Only .docx files are supported') { + setError(status); + setLoading(false); } - }, [url]); + }, [status]); + + const handleZoomIn = useCallback(() => { + setZoomScale((s) => clampZoom(s, 1)); + }, []); + + const handleZoomOut = useCallback(() => { + setZoomScale((s) => clampZoom(s, -1)); + }, []); + + const showContent = !loading && !error; + const pageCount = showContent && totalPages > 0 ? totalPages : 0; return (

- {loading && ( -
- + {/* Toolbar */} +
+ + {loading ? 'Loading...' : error ? '' : `Page ${pageCount || '-'}`} + +
+ + + {zoomScale}% + +
- )} +
- {!loading &&
} + {/* Viewer / Error area */} +
+ {loading && ( +
+ +
+ )} + + {error && !loading && ( +
+
+

+ Preview is not available for this Word document +

+

+ @extend-ai/react-docx supports modern .docx files + only. +
+ {error} +

+
+
+ )} + + {showContent && ( +
+
+ + +
+ } + pageGapBackgroundColor="#f5f5f5" + /> +
+
+ )} +
); }; diff --git a/web/src/components/document-preview/hooks.ts b/web/src/components/document-preview/hooks.ts index fa17e3b66f..1fd92ae385 100644 --- a/web/src/components/document-preview/hooks.ts +++ b/web/src/components/document-preview/hooks.ts @@ -6,7 +6,6 @@ import { getAuthorization } from '@/utils/authorization-util'; import jsPreviewExcel from '@js-preview/excel'; import { useSize } from 'ahooks'; import axios from 'axios'; -import mammoth from 'mammoth'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; export const useDocumentResizeObserver = () => { @@ -127,45 +126,6 @@ export const useFetchExcel = (filePath: string) => { return { status, containerRef, error }; }; -export const useFetchDocx = (filePath: string) => { - const [succeed, setSucceed] = useState(true); - const [error, setError] = useState(); - const { fetchDocument } = useFetchDocument(); - const containerRef = useRef(null); - - const fetchDocumentAsync = useCallback(async () => { - try { - const jsonFile = await fetchDocument(filePath); - mammoth - .convertToHtml( - { arrayBuffer: jsonFile.data }, - { includeDefaultStyleMap: true }, - ) - .then((result) => { - setSucceed(true); - const docEl = document.createElement('div'); - docEl.className = 'document-container'; - docEl.innerHTML = result.value; - const container = containerRef.current; - if (container) { - container.innerHTML = docEl.outerHTML; - } - }) - .catch(() => { - setSucceed(false); - }); - } catch (error: any) { - setError(error.toString()); - } - }, [filePath, fetchDocument]); - - useEffect(() => { - fetchDocumentAsync(); - }, [fetchDocumentAsync]); - - return { succeed, containerRef, error }; -}; - export const useCatchDocumentError = (url: string) => { const httpHeaders = useMemo(() => { return {