From e0a078a5ac17df2ccfda772d3734b2178f379d49 Mon Sep 17 00:00:00 2001 From: oswaldo martinez Date: Sat, 19 Jul 2025 01:34:30 -0500 Subject: [PATCH 1/5] added react pageflip and manage to use react pdf --- app/components/common/ClientOnly.tsx | 27 ++ app/routes.ts | 1 + app/routes/assets/EbookReader.tsx | 92 ++++++ app/routes/assets/EbookReaderPage.tsx | 12 + package-lock.json | 423 ++++++++++++++++++++++++++ package.json | 3 + 6 files changed, 558 insertions(+) create mode 100644 app/components/common/ClientOnly.tsx create mode 100644 app/routes/assets/EbookReader.tsx create mode 100644 app/routes/assets/EbookReaderPage.tsx diff --git a/app/components/common/ClientOnly.tsx b/app/components/common/ClientOnly.tsx new file mode 100644 index 00000000..78dc9566 --- /dev/null +++ b/app/components/common/ClientOnly.tsx @@ -0,0 +1,27 @@ +// components/ClientOnly.js +import { lazy, Suspense, useEffect, useState } from "react"; +import PropTypes from "prop-types"; + +const ClientOnly = ({ load, fallback = null, ...props }) => { + const [mounted, setMounted] = useState(false); + + useEffect(() => { + setMounted(true); + }, []); + + if (!mounted) return fallback; + + const Component = lazy(load); + return ( + + + + ); +}; + +ClientOnly.propTypes = { + load: PropTypes.func.isRequired, + fallback: PropTypes.node, +}; + +export default ClientOnly; diff --git a/app/routes.ts b/app/routes.ts index a2cf9532..5e323bc2 100644 --- a/app/routes.ts +++ b/app/routes.ts @@ -39,6 +39,7 @@ export default [ // user dash ...prefix("dash", [ route("compras/:assetId", "routes/purchase_detail.tsx"), + route("ebook/:assetId", "routes/assets/EbookReaderPage.tsx"), route("compras/:assetSlug/review", "routes/assets/ReviewAsset.tsx"), layout("components/DashLayout/DashLayout.tsx", [ index("routes/start.tsx"), diff --git a/app/routes/assets/EbookReader.tsx b/app/routes/assets/EbookReader.tsx new file mode 100644 index 00000000..d30e7313 --- /dev/null +++ b/app/routes/assets/EbookReader.tsx @@ -0,0 +1,92 @@ +"use client"; +import { pdfjs } from "react-pdf"; +import HTMLFlipBook from "react-pageflip"; + +pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`; + +import { useRef, useState } from "react"; +import { Document, Page } from "react-pdf"; + +export default function MyApp() { + const [numPages, setNumPages] = useState(null); + const [htmlContent, setHtmlContent] = useState(""); + const flipBook = useRef(null); + const [file, setFile] = useState(null); + const [fileType, setFileType] = useState(null); + + const onDocumentLoadSuccess = ({ numPages }) => { + setNumPages(numPages); + }; + + const getFileType = (fileName) => { + const extension = fileName.split(".").pop().toLowerCase(); + switch (extension) { + case "pdf": + return "pdf"; + case "epub": + return "epub"; + case "docx": + return "docx"; + default: + return null; + } + }; + + const handleFileChange = (e) => { + const selectedFile = e.target.files[0]; + if (selectedFile) { + const type = getFileType(selectedFile.name); + if (type) { + setFile(selectedFile); + setFileType(type); + } else { + alert("Unsupported file format. Please upload PDF, EPUB, or DOCX."); + } + } + }; + + return ( +
+ + {file && ( + Loading PDF...
} + > + + {Array.from({ length: numPages || 0 }, (_, index) => ( +
+ +
+ ))} +
+ + )} + + ); +} diff --git a/app/routes/assets/EbookReaderPage.tsx b/app/routes/assets/EbookReaderPage.tsx new file mode 100644 index 00000000..372eaf68 --- /dev/null +++ b/app/routes/assets/EbookReaderPage.tsx @@ -0,0 +1,12 @@ +import ClientOnly from "~/components/common/ClientOnly"; + +export default function EbookReaderPage() { + return ( +
+ import("./EbookReader")} + fallback={
Loading...
} + /> +
+ ); +} diff --git a/package-lock.json b/package-lock.json index ea0d7674..c00fce39 100644 --- a/package-lock.json +++ b/package-lock.json @@ -64,6 +64,8 @@ "react-hot-toast": "^2.5.1", "react-icons": "^5.4.0", "react-markdown": "^10.1.0", + "react-pageflip": "^2.0.3", + "react-pdf": "^10.0.1", "react-router-dom": "^7.7.0", "react-simplemde-editor": "^5.2.0", "react-syntax-highlighter": "^15.6.1", @@ -107,6 +109,7 @@ "typescript": "^5.7.3", "vite": "^6.1.1", "vite-plugin-externalize-deps": "^0.9.0", + "vite-plugin-static-copy": "^3.1.1", "vite-tsconfig-paths": "^5.1.4", "vitest": "^3.2.4", "winston": "^3.17.0" @@ -2881,6 +2884,191 @@ "sparse-bitfield": "^3.0.3" } }, + "node_modules/@napi-rs/canvas": { + "version": "0.1.74", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-0.1.74.tgz", + "integrity": "sha512-pOIyzuS+5Bz1vAhD7tdhaw5/936mMJZUn4aVajojUdjYOGSWmfpDYSgt0nQLZPZVN5GLgWgutqXPOi7Jsm3k+Q==", + "license": "MIT", + "optional": true, + "workspaces": [ + "e2e/*" + ], + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@napi-rs/canvas-android-arm64": "0.1.74", + "@napi-rs/canvas-darwin-arm64": "0.1.74", + "@napi-rs/canvas-darwin-x64": "0.1.74", + "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.74", + "@napi-rs/canvas-linux-arm64-gnu": "0.1.74", + "@napi-rs/canvas-linux-arm64-musl": "0.1.74", + "@napi-rs/canvas-linux-riscv64-gnu": "0.1.74", + "@napi-rs/canvas-linux-x64-gnu": "0.1.74", + "@napi-rs/canvas-linux-x64-musl": "0.1.74", + "@napi-rs/canvas-win32-x64-msvc": "0.1.74" + } + }, + "node_modules/@napi-rs/canvas-android-arm64": { + "version": "0.1.74", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.74.tgz", + "integrity": "sha512-aq5ode+9Z/ZR0H485dI2jdRdttg/hl9Ob+iPCt0nj+QFiirpxDrbUHKeTZWQWEtkWyC7vI5R2dMTbDINBfl9eg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-darwin-arm64": { + "version": "0.1.74", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.74.tgz", + "integrity": "sha512-eO5Miz+ef1dEQyUMWDdcbAb1Wr7yMyxD9/CL9d4frQxO4pTTaCiMBUWup8XDPLr/g7XkSkGCZLP47xiXiyXSpQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-darwin-x64": { + "version": "0.1.74", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.74.tgz", + "integrity": "sha512-0EkO0IFkps7C3JpKC7lbM3IL+QDUYeUKagHLDbUry4PeQTghxp6JcgccpmU32ZbpFZgPnm7o0tTJO0J1d8S2rA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-linux-arm-gnueabihf": { + "version": "0.1.74", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.74.tgz", + "integrity": "sha512-qAVJEN2JqGayEI1kSpJy1Xr6ZmCFV9QhRyV35yWsS7e9X1jm+T4DAlCxI4PlKIlqVSzYMYhKrxchST20XBSzHg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-linux-arm64-gnu": { + "version": "0.1.74", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.74.tgz", + "integrity": "sha512-lOnop22qy6MYxI94GGunMMjo6D80I//2W/6pqKUfwXaDQtOfvHsTcVVzDu5cFXUTNrb9ZRfMCeol5YEd+9FJvg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-linux-arm64-musl": { + "version": "0.1.74", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.74.tgz", + "integrity": "sha512-tfFqLHGtSEabBigOnPUfZviSTGmW2xHv5tYZYPBWmgGiTkoNJ7lEWFUxHjwvV5HXGqLs8ok/O7g1enSpxO6lmQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-linux-riscv64-gnu": { + "version": "0.1.74", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.74.tgz", + "integrity": "sha512-j6H9dHTMtr1y3tu/zGm1ythYIL9vTl4EEv9f6CMx0n3Zn2M+OruUUwh9ylCj4afzSNEK9T8cr6zMnmTPzkpBvQ==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-linux-x64-gnu": { + "version": "0.1.74", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.74.tgz", + "integrity": "sha512-73DIV4E7Y9CpIJuUXVl9H6+MEQXyRy4VJQoUGA1tOlcKQiStxqhq6UErL4decI28NxjyQXBhtYZKj5q8AJEuOg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-linux-x64-musl": { + "version": "0.1.74", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.74.tgz", + "integrity": "sha512-FgDMEFdGIJT3I2xejflRJ82/ZgDphyirS43RgtoLaIXI6zihLiZcQ7rczpqeWgAwlJNjR0He2EustsKe1SkUOg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-win32-x64-msvc": { + "version": "0.1.74", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.74.tgz", + "integrity": "sha512-x6bhwlhn0wU7dfiP46mt5Bi6PowSUH4CJ4PTzGj58LRQ1HVasEIJgoMx7MLC48F738eJpzbfg3WR/D8+e9CeTA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -8027,6 +8215,21 @@ "node": ">= 0.6" } }, + "node_modules/fs-extra": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", + "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -8175,6 +8378,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, "node_modules/graphql": { "version": "16.11.0", "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.11.0.tgz", @@ -9448,6 +9658,19 @@ "node": ">=6" } }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/jsonwebtoken": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", @@ -9784,6 +10007,15 @@ "source-map-js": "^1.2.0" } }, + "node_modules/make-cancellable-promise": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/make-cancellable-promise/-/make-cancellable-promise-2.0.0.tgz", + "integrity": "sha512-3SEQqTpV9oqVsIWqAcmDuaNeo7yBO3tqPtqGRcKkEo0lrzD3wqbKG9mkxO65KoOgXqj+zH2phJ2LiAsdzlogSw==", + "license": "MIT", + "funding": { + "url": "https://github.com/wojtekmaj/make-cancellable-promise?sponsor=1" + } + }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -9800,6 +10032,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/make-event-props": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/make-event-props/-/make-event-props-2.0.0.tgz", + "integrity": "sha512-G/hncXrl4Qt7mauJEXSg3AcdYzmpkIITTNl5I+rH9sog5Yw0kK6vseJjCaPfOXqOqQuPUP89Rkhfz5kPS8ijtw==", + "license": "MIT", + "funding": { + "url": "https://github.com/wojtekmaj/make-event-props?sponsor=1" + } + }, "node_modules/map-obj": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-5.0.0.tgz", @@ -10167,6 +10408,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/merge-refs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-refs/-/merge-refs-2.0.0.tgz", + "integrity": "sha512-3+B21mYK2IqUWnd2EivABLT7ueDhb0b8/dGK8LoFQPrU61YITeCMn14F7y7qZafWNZhUEKb24cJdiT5Wxs3prg==", + "license": "MIT", + "funding": { + "url": "https://github.com/wojtekmaj/merge-refs?sponsor=1" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -11499,12 +11757,31 @@ "fn.name": "1.x.x" } }, + "node_modules/p-map": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.3.tgz", + "integrity": "sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/package-json-from-dist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "license": "BlueOak-1.0.0" }, + "node_modules/page-flip": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/page-flip/-/page-flip-2.0.7.tgz", + "integrity": "sha512-96lQFUUz7r/LZzEUZJ3yBIMEKU9+m8HMFDzTvTdD6P7Ag/wXINjp9n0W7b4wanwnDbQETo4uNUoL3zMqpFxwGA==", + "license": "MIT" + }, "node_modules/pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", @@ -11641,6 +11918,18 @@ "node": ">= 14.16" } }, + "node_modules/pdfjs-dist": { + "version": "5.3.31", + "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-5.3.31.tgz", + "integrity": "sha512-EhPdIjNX0fcdwYQO+e3BAAJPXt+XI29TZWC7COhIXs/K0JHcUt1Gdz1ITpebTwVMFiLsukdUZ3u0oTO7jij+VA==", + "license": "Apache-2.0", + "engines": { + "node": ">=20.16.0 || >=22.3.0" + }, + "optionalDependencies": { + "@napi-rs/canvas": "^0.1.67" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -12319,6 +12608,44 @@ "react": ">=18" } }, + "node_modules/react-pageflip": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/react-pageflip/-/react-pageflip-2.0.3.tgz", + "integrity": "sha512-k81mHhRvUM52y8jyzTCh5t4O0lepkLhp+XGSUzq2C3uD+iW99Cv0jfRlqFCjZbD5N3jKkIFr7/3giucoXKDP3Q==", + "license": "MIT", + "dependencies": { + "page-flip": "latest" + } + }, + "node_modules/react-pdf": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/react-pdf/-/react-pdf-10.0.1.tgz", + "integrity": "sha512-dblLZ5BqubeNFZwTzHkRi7Rexev2+Xb9z7sEvNYT+IOxmih1KgrByeqD4Hm0RnwR9nTWludXbiHQMe9d47DK2w==", + "license": "MIT", + "dependencies": { + "clsx": "^2.0.0", + "dequal": "^2.0.3", + "make-cancellable-promise": "^2.0.0", + "make-event-props": "^2.0.0", + "merge-refs": "^2.0.0", + "pdfjs-dist": "5.3.31", + "tiny-invariant": "^1.0.0", + "warning": "^4.0.0" + }, + "funding": { + "url": "https://github.com/wojtekmaj/react-pdf?sponsor=1" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/react-refresh": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", @@ -14150,6 +14477,12 @@ "node": ">=0.8" } }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "license": "MIT" + }, "node_modules/tinybench": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", @@ -14572,6 +14905,16 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -14900,6 +15243,77 @@ "vite": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0" } }, + "node_modules/vite-plugin-static-copy": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/vite-plugin-static-copy/-/vite-plugin-static-copy-3.1.1.tgz", + "integrity": "sha512-oR53SkL5cX4KT1t18E/xU50vJDo0N8oaHza4EMk0Fm+2/u6nQivxavOfrDk3udWj+dizRizB/QnBvJOOQrTTAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.6.0", + "fs-extra": "^11.3.0", + "p-map": "^7.0.3", + "picocolors": "^1.1.1", + "tinyglobby": "^0.2.14" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/vite-plugin-static-copy/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/vite-plugin-static-copy/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/vite-plugin-static-copy/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/vite-tsconfig-paths": { "version": "5.1.4", "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-5.1.4.tgz", @@ -15052,6 +15466,15 @@ "node": ">=18" } }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/web-encoding": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/web-encoding/-/web-encoding-1.1.5.tgz", diff --git a/package.json b/package.json index 9e06bd5b..759b0d82 100644 --- a/package.json +++ b/package.json @@ -74,6 +74,8 @@ "react-hot-toast": "^2.5.1", "react-icons": "^5.4.0", "react-markdown": "^10.1.0", + "react-pageflip": "^2.0.3", + "react-pdf": "^10.0.1", "react-router-dom": "^7.7.0", "react-simplemde-editor": "^5.2.0", "react-syntax-highlighter": "^15.6.1", @@ -117,6 +119,7 @@ "typescript": "^5.7.3", "vite": "^6.1.1", "vite-plugin-externalize-deps": "^0.9.0", + "vite-plugin-static-copy": "^3.1.1", "vite-tsconfig-paths": "^5.1.4", "vitest": "^3.2.4", "winston": "^3.17.0" From d67928b486338f852d280d55ee971b8155fefb2a Mon Sep 17 00:00:00 2001 From: oswaldo martinez Date: Sat, 19 Jul 2025 02:13:39 -0500 Subject: [PATCH 2/5] added react pageflip and manage to use react pdf --- app/routes/assets/EbookReader.tsx | 142 +++++++++++++++++++++--------- 1 file changed, 99 insertions(+), 43 deletions(-) diff --git a/app/routes/assets/EbookReader.tsx b/app/routes/assets/EbookReader.tsx index d30e7313..f787a1d7 100644 --- a/app/routes/assets/EbookReader.tsx +++ b/app/routes/assets/EbookReader.tsx @@ -4,15 +4,15 @@ import HTMLFlipBook from "react-pageflip"; pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`; -import { useRef, useState } from "react"; +import { useEffect, useRef, useState } from "react"; import { Document, Page } from "react-pdf"; -export default function MyApp() { +export default function EbookReader() { const [numPages, setNumPages] = useState(null); - const [htmlContent, setHtmlContent] = useState(""); - const flipBook = useRef(null); + const flipBookRef = useRef(null); const [file, setFile] = useState(null); const [fileType, setFileType] = useState(null); + const [bookSize, setBookSize] = useState({ width: 600, height: 850 }); const onDocumentLoadSuccess = ({ numPages }) => { setNumPages(numPages); @@ -45,47 +45,103 @@ export default function MyApp() { } }; + // Responsive sizing + useEffect(() => { + const calculateSize = () => { + const maxWidth = Math.min(window.innerWidth - 80, 650); // 40px padding on each side + const height = Math.min(maxWidth * 1.414, window.innerHeight - 120); // A4 ratio with top/bottom margin + setBookSize({ width: maxWidth, height }); + }; + + calculateSize(); + window.addEventListener("resize", calculateSize); + return () => window.removeEventListener("resize", calculateSize); + }, []); + return (
- - {file && ( - Loading PDF...
} - > - - {Array.from({ length: numPages || 0 }, (_, index) => ( -
- -
- ))} -
- + {file ? ( + <> +
+ {/* Container with constrained width */} +
+

Titulo del librin

+ + {/* Flipbook wrapper */} + + setNumPages(numPages)} + loading={ +
+
+ Loading PDF... +
+
+ } + > + + {Array.from({ length: numPages || 0 }, (_, index) => ( +
+ +
+ Loading page... +
+
+ } + /> +
+ ))} + + +
+ + + {/* Navigation controls (optional) */} +
+ + +
+ + ) : ( + )} ); From 7a0e4b988140ccbdf19d2d25778b8209544b1972 Mon Sep 17 00:00:00 2001 From: oswaldo martinez Date: Sat, 19 Jul 2025 02:39:52 -0500 Subject: [PATCH 3/5] added react pageflip and manage to use react pdf --- app/routes/assets/EbookReader.tsx | 82 +++++++++++-------------------- 1 file changed, 30 insertions(+), 52 deletions(-) diff --git a/app/routes/assets/EbookReader.tsx b/app/routes/assets/EbookReader.tsx index f787a1d7..d09986a1 100644 --- a/app/routes/assets/EbookReader.tsx +++ b/app/routes/assets/EbookReader.tsx @@ -63,62 +63,40 @@ export default function EbookReader() { {file ? ( <>
- {/* Container with constrained width */} -
+

Titulo del librin

- - {/* Flipbook wrapper */} - - setNumPages(numPages)} - loading={ -
-
- Loading PDF... -
-
- } - > - + setNumPages(numPages)} > - {Array.from({ length: numPages || 0 }, (_, index) => ( -
- -
- Loading page... -
-
- } - /> -
- ))} - - + + {Array.from({ length: numPages || 0 }, (_, index) => ( +
+ +
+ ))} +
+ +
- {/* Navigation controls (optional) */} + {/* Navigation controls (optional)
-
+ */} ) : ( Date: Mon, 21 Jul 2025 23:43:35 -0500 Subject: [PATCH 4/5] sha se ve facherito, pero le falta --- app/hooks/useScreenDimensions.tsx | 54 +++++++++++ app/routes/assets/EbookReader.tsx | 105 +++++++-------------- app/routes/assets/EbookReader/FlipBook.tsx | 66 +++++++++++++ app/routes/assets/EbookReaderPage.tsx | 7 +- 4 files changed, 162 insertions(+), 70 deletions(-) create mode 100644 app/hooks/useScreenDimensions.tsx create mode 100644 app/routes/assets/EbookReader/FlipBook.tsx diff --git a/app/hooks/useScreenDimensions.tsx b/app/hooks/useScreenDimensions.tsx new file mode 100644 index 00000000..64626d52 --- /dev/null +++ b/app/hooks/useScreenDimensions.tsx @@ -0,0 +1,54 @@ +// src/hooks/useBookDimensions.js +import { useState, useEffect, useCallback } from "react"; + +const DESKTOP_BREAKPOINT = 768; // Adjust this as needed + +const useScreenDimensions = (aspectRatio = 1.414) => { + const [isMobileView, setIsMobileView] = useState(false); + const [screenWidth, setscreenWidth] = useState(600); // Initialize to 0 + const [screenHeight, setscreenHeight] = useState(800); // Initialize to 0 + + const calculateDimensions = useCallback(() => { + const windowWidth = window.innerWidth; + const windowHeight = window.innerHeight; + + const newIsMobileView = windowWidth < DESKTOP_BREAKPOINT; + setIsMobileView(newIsMobileView); + + let calculatedscreenWidth; + let calculatedscreenHeight; + + if (newIsMobileView) { + // For mobile, make the page fill a good portion of the screen + // Subtract some padding/margin for better fit + calculatedscreenWidth = Math.min(450, windowWidth * 0.9); // Max 450px, or 90% of screen + calculatedscreenHeight = calculatedscreenWidth * aspectRatio; + } else { + // For desktop, target a comfortable reading size for a single page + // Subtracting some space for margin/buttons, and leave room for two pages + // Ensure book height doesn't exceed screen height, leaving space for UI + calculatedscreenHeight = Math.min(600, windowHeight * 0.8); // Max 600px, or 80% of screen height + calculatedscreenWidth = calculatedscreenHeight / aspectRatio; + } + + setscreenWidth(calculatedscreenWidth); + setscreenHeight(calculatedscreenHeight); + }, [aspectRatio]); // useCallback dependency + + useEffect(() => { + // Set initial dimensions when the component mounts + calculateDimensions(); + + // Add event listener for window resize + window.addEventListener("resize", calculateDimensions); + + // Clean up event listener on component unmount + return () => { + window.removeEventListener("resize", calculateDimensions); + }; + }, [calculateDimensions]); // useEffect dependency + + return { isMobileView, screenWidth, screenHeight }; +}; + +export default useScreenDimensions; diff --git a/app/routes/assets/EbookReader.tsx b/app/routes/assets/EbookReader.tsx index d09986a1..703f3055 100644 --- a/app/routes/assets/EbookReader.tsx +++ b/app/routes/assets/EbookReader.tsx @@ -1,18 +1,19 @@ -"use client"; -import { pdfjs } from "react-pdf"; -import HTMLFlipBook from "react-pageflip"; +import { useEffect, useRef, useState } from "react"; +import { Document, Page, pdfjs } from "react-pdf"; +import FlipBook from "./EbookReader/FlipBook"; +import "react-pdf/dist/Page/AnnotationLayer.css"; +import "react-pdf/dist/Page/TextLayer.css"; +import useScreenDimensions from "~/hooks/useScreenDimensions"; pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`; -import { useEffect, useRef, useState } from "react"; -import { Document, Page } from "react-pdf"; - export default function EbookReader() { const [numPages, setNumPages] = useState(null); const flipBookRef = useRef(null); const [file, setFile] = useState(null); const [fileType, setFileType] = useState(null); - const [bookSize, setBookSize] = useState({ width: 600, height: 850 }); + // Responsive sizing + const { isMobileView, screenWidth, screenHeight } = useScreenDimensions(); const onDocumentLoadSuccess = ({ numPages }) => { setNumPages(numPages); @@ -45,72 +46,39 @@ export default function EbookReader() { } }; - // Responsive sizing - useEffect(() => { - const calculateSize = () => { - const maxWidth = Math.min(window.innerWidth - 80, 650); // 40px padding on each side - const height = Math.min(maxWidth * 1.414, window.innerHeight - 120); // A4 ratio with top/bottom margin - setBookSize({ width: maxWidth, height }); - }; - - calculateSize(); - window.addEventListener("resize", calculateSize); - return () => window.removeEventListener("resize", calculateSize); - }, []); - return ( -
+
{file ? ( <> -
-
-

Titulo del librin

-
- setNumPages(numPages)} +
+ + {numPages && ( + - - {Array.from({ length: numPages || 0 }, (_, index) => ( -
- -
- ))} -
-
-
-
+ {Array.from(new Array(numPages), (el, index) => ( +
+ +
+ ))} + + )} +
- - {/* Navigation controls (optional) -
- - -
*/} ) : ( )}
diff --git a/app/routes/assets/EbookReader/FlipBook.tsx b/app/routes/assets/EbookReader/FlipBook.tsx new file mode 100644 index 00000000..961d0a0b --- /dev/null +++ b/app/routes/assets/EbookReader/FlipBook.tsx @@ -0,0 +1,66 @@ +import { useCallback, useEffect, useRef, useState } from "react"; +import HTMLFlipBook from "react-pageflip"; +import useScreenDimensions from "~/hooks/useScreenDimensions"; + +export default function FlipBook({ numPages, children, title }) { + const flipBookRef = useRef(null); + + // Optional: Add navigation handlers if you want external buttons + const goToPrevPage = useCallback(() => { + if (flipBookRef.current) { + flipBookRef.current.pageFlip().flipPrev(); + } + }, []); + + const goToNextPage = useCallback(() => { + if (flipBookRef.current) { + flipBookRef.current.pageFlip().flipNext(); + } + }, []); + + const onFlip = useCallback((e) => { + console.log("Current page:", e.data); + }, []); + + const { isMobileView, screenWidth, screenHeight } = useScreenDimensions(); + + return ( +
+

{title}

+ + {children} + + {/* add buttons */} + {/* current page and store progress */} + {/* */} +
+ ); +} diff --git a/app/routes/assets/EbookReaderPage.tsx b/app/routes/assets/EbookReaderPage.tsx index 372eaf68..0a64f4bb 100644 --- a/app/routes/assets/EbookReaderPage.tsx +++ b/app/routes/assets/EbookReaderPage.tsx @@ -1,11 +1,16 @@ import ClientOnly from "~/components/common/ClientOnly"; +import Spinner from "~/components/common/Spinner"; export default function EbookReaderPage() { return (
import("./EbookReader")} - fallback={
Loading...
} + fallback={ +
+ +
+ } />
); From 52616d163a7052900f1eba2a1015280ae5351ba7 Mon Sep 17 00:00:00 2001 From: oswaldo martinez Date: Thu, 31 Jul 2025 09:25:38 -0500 Subject: [PATCH 5/5] testing epubs --- app/hooks/useScreenDimensions.tsx | 2 +- app/routes/assets/EbookReader.tsx | 84 +++++---- app/routes/assets/EbookReader/FlipBook.tsx | 9 +- package-lock.json | 197 +++++++++++++++++++++ package.json | 1 + 5 files changed, 252 insertions(+), 41 deletions(-) diff --git a/app/hooks/useScreenDimensions.tsx b/app/hooks/useScreenDimensions.tsx index 64626d52..3609cca0 100644 --- a/app/hooks/useScreenDimensions.tsx +++ b/app/hooks/useScreenDimensions.tsx @@ -3,7 +3,7 @@ import { useState, useEffect, useCallback } from "react"; const DESKTOP_BREAKPOINT = 768; // Adjust this as needed -const useScreenDimensions = (aspectRatio = 1.414) => { +const useScreenDimensions = (aspectRatio = 1.3) => { const [isMobileView, setIsMobileView] = useState(false); const [screenWidth, setscreenWidth] = useState(600); // Initialize to 0 const [screenHeight, setscreenHeight] = useState(800); // Initialize to 0 diff --git a/app/routes/assets/EbookReader.tsx b/app/routes/assets/EbookReader.tsx index 703f3055..c5b31297 100644 --- a/app/routes/assets/EbookReader.tsx +++ b/app/routes/assets/EbookReader.tsx @@ -4,6 +4,7 @@ import FlipBook from "./EbookReader/FlipBook"; import "react-pdf/dist/Page/AnnotationLayer.css"; import "react-pdf/dist/Page/TextLayer.css"; import useScreenDimensions from "~/hooks/useScreenDimensions"; +import { ReactReader } from "react-reader"; pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`; @@ -12,45 +13,54 @@ export default function EbookReader() { const flipBookRef = useRef(null); const [file, setFile] = useState(null); const [fileType, setFileType] = useState(null); + const [fileUrl, setFileUrl] = useState(null); // Responsive sizing const { isMobileView, screenWidth, screenHeight } = useScreenDimensions(); + const [location, setLocation] = useState(0); + const onDocumentLoadSuccess = ({ numPages }) => { setNumPages(numPages); }; - const getFileType = (fileName) => { - const extension = fileName.split(".").pop().toLowerCase(); - switch (extension) { + const processFile = async (e) => { + const file = e.target.files[0]; + const type = file.name.split(".").pop().toLowerCase(); + console.log(file); + setFileType(type); + switch (type) { case "pdf": - return "pdf"; + setFile(file); + break; case "epub": - return "epub"; - case "docx": - return "docx"; + const url = URL.createObjectURL(file); + setFileUrl(url); + break; + case "mobi": + case "azw3": default: - return null; + throw new Error("Unsupported file type"); } }; - const handleFileChange = (e) => { - const selectedFile = e.target.files[0]; - if (selectedFile) { - const type = getFileType(selectedFile.name); - if (type) { - setFile(selectedFile); - setFileType(type); - } else { - alert("Unsupported file format. Please upload PDF, EPUB, or DOCX."); - } - } + const asset = { + title: "Book sample", }; - + console.log({ fileType, fileUrl }); return ( -
- {file ? ( - <> -
+
+ +
+

{asset.title}

+ {file && fileType === "pdf" && ( + <> {numPages && ( )} -
- - ) : ( - - )} + + )} + + {fileUrl && fileType === "epub" && ( + <> + setLocation(loc)} + /> + + )} +
); } diff --git a/app/routes/assets/EbookReader/FlipBook.tsx b/app/routes/assets/EbookReader/FlipBook.tsx index 961d0a0b..18398b99 100644 --- a/app/routes/assets/EbookReader/FlipBook.tsx +++ b/app/routes/assets/EbookReader/FlipBook.tsx @@ -26,7 +26,6 @@ export default function FlipBook({ numPages, children, title }) { return (
-

{title}

=10.0.0" + } + }, "node_modules/@zxing/text-encoding": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/@zxing/text-encoding/-/text-encoding-0.9.0.tgz", @@ -6852,6 +6873,17 @@ "node": ">=6.6.0" } }, + "node_modules/core-js": { + "version": "3.44.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.44.0.tgz", + "integrity": "sha512-aFCtd4l6GvAXwVEh3XbbVqJGHDJt0OZRa+5ePGx3LLwi12WfexqQxcsohb2wgsa/92xtl19Hd66G/L+TaAxDMw==", + "hasInstallScript": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -6999,6 +7031,19 @@ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "license": "MIT" }, + "node_modules/d": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", + "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==", + "license": "ISC", + "dependencies": { + "es5-ext": "^0.10.64", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.12" + } + }, "node_modules/data-uri-to-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz", @@ -7456,6 +7501,23 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/epubjs": { + "version": "0.3.93", + "resolved": "https://registry.npmjs.org/epubjs/-/epubjs-0.3.93.tgz", + "integrity": "sha512-c06pNSdBxcXv3dZSbXAVLE1/pmleRhOT6mXNZo6INKmvuKpYB65MwU/lO7830czCtjIiK9i+KR+3S+p0wtljrw==", + "license": "BSD-2-Clause", + "dependencies": { + "@types/localforage": "0.0.34", + "@xmldom/xmldom": "^0.7.5", + "core-js": "^3.18.3", + "event-emitter": "^0.3.5", + "jszip": "^3.7.1", + "localforage": "^1.10.0", + "lodash": "^4.17.21", + "marks-pane": "^1.0.9", + "path-webpack": "0.0.3" + } + }, "node_modules/err-code": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", @@ -7498,6 +7560,46 @@ "node": ">= 0.4" } }, + "node_modules/es5-ext": { + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", + "hasInstallScript": true, + "license": "ISC", + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "license": "MIT", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-symbol": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", + "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==", + "license": "ISC", + "dependencies": { + "d": "^1.0.2", + "ext": "^1.7.0" + }, + "engines": { + "node": ">=0.12" + } + }, "node_modules/esast-util-from-estree": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/esast-util-from-estree/-/esast-util-from-estree-2.0.0.tgz", @@ -7598,6 +7700,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "license": "ISC", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", @@ -7711,6 +7828,16 @@ "node": ">= 0.6" } }, + "node_modules/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "license": "MIT", + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, "node_modules/event-target-shim": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", @@ -7864,6 +7991,15 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "license": "ISC", + "dependencies": { + "type": "^2.7.2" + } + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -9828,6 +9964,24 @@ "node": ">=18" } }, + "node_modules/localforage": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz", + "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==", + "license": "Apache-2.0", + "dependencies": { + "lie": "3.1.1" + } + }, + "node_modules/localforage/node_modules/lie": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", + "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", @@ -10087,6 +10241,12 @@ "node": ">= 18" } }, + "node_modules/marks-pane": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/marks-pane/-/marks-pane-1.0.9.tgz", + "integrity": "sha512-Ahs4oeG90tbdPWwAJkAAoHg2lRR8lAs9mZXETNPO9hYg3AkjUJBKi1NQ4aaIQZVGrig7c/3NUV1jANl8rFTeMg==", + "license": "MIT" + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -11480,6 +11640,12 @@ "node": ">= 0.6" } }, + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", + "license": "ISC" + }, "node_modules/node": { "version": "20.19.4", "resolved": "https://registry.npmjs.org/node/-/node-20.19.4.tgz", @@ -11902,6 +12068,12 @@ "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", "license": "MIT" }, + "node_modules/path-webpack": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/path-webpack/-/path-webpack-0.0.3.tgz", + "integrity": "sha512-AmeDxedoo5svf7aB3FYqSAKqMxys014lVKBzy1o/5vv9CtU7U4wgGWL1dA2o6MOzcD53ScN4Jmiq6VbtLz1vIQ==", + "license": "MIT" + }, "node_modules/pathe": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", @@ -12646,6 +12818,16 @@ } } }, + "node_modules/react-reader": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/react-reader/-/react-reader-2.0.13.tgz", + "integrity": "sha512-g8Y4Wz0LAEYR+JHRdYuR0dxHK+qmHLmJr4yrS820WNzB3oJGFGKk6EEOj2bnE6P0fo642M6QluPLaPL1sh8T8Q==", + "license": "ISC", + "dependencies": { + "epubjs": "^0.3.93", + "react-swipeable": "^7.0.2" + } + }, "node_modules/react-refresh": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", @@ -12716,6 +12898,15 @@ "react-dom": ">=16.8.2" } }, + "node_modules/react-swipeable": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/react-swipeable/-/react-swipeable-7.0.2.tgz", + "integrity": "sha512-v1Qx1l+aC2fdxKa9aKJiaU/ZxmJ5o98RMoFwUqAAzVWUcxgfHFXDDruCKXhw6zIYXm6V64JiHgP9f6mlME5l8w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.3 || ^17 || ^18 || ^19.0.0 || ^19.0.0-rc" + } + }, "node_modules/react-syntax-highlighter": { "version": "15.6.1", "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.6.1.tgz", @@ -14724,6 +14915,12 @@ "dev": true, "license": "ISC" }, + "node_modules/type": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", + "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==", + "license": "ISC" + }, "node_modules/type-fest": { "version": "4.41.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", diff --git a/package.json b/package.json index 759b0d82..d0ee5974 100644 --- a/package.json +++ b/package.json @@ -76,6 +76,7 @@ "react-markdown": "^10.1.0", "react-pageflip": "^2.0.3", "react-pdf": "^10.0.1", + "react-reader": "^2.0.13", "react-router-dom": "^7.7.0", "react-simplemde-editor": "^5.2.0", "react-syntax-highlighter": "^15.6.1",