mirror of
https://github.com/danielmiessler/fabric.git
synced 2026-09-10 07:36:44 -04:00
feat(web): replace PDF.js conversion with WASM worker
- Process PDF attachments through reusable pdf-inspector WASM worker - Transfer ArrayBuffers and initialize WASM once per worker - Reject pending conversions and recreate workers after crashes - Parse attachments without triggering duplicate chat requests - Surface OCR, encoding, and empty-content conversion errors - Remove PDF.js pipeline and obsolete transitive dependencies - Add tests for conversion results and chat boundaries
This commit is contained in:
parent
9e92bca148
commit
b90a07bc19
7
cmd/generate_changelog/incoming/2185.txt
Normal file
7
cmd/generate_changelog/incoming/2185.txt
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
### PR [#2185](https://github.com/danielmiessler/Fabric/pull/2185) by [ksylvan](https://github.com/ksylvan): feat(web): replace PDF.js pipeline with pdf-inspector WASM worker
|
||||
|
||||
- Replaced the PDF.js processing pipeline with the new `pdf-inspector.worker.ts`, backed by `@firecrawl/pdf-inspector-wasm`. The worker initializes WASM one time and processes transferred ArrayBuffers.
|
||||
- Added worker error handling. A worker crash now rejects all pending requests with a clear error and terminates the worker. The next request starts a new worker.
|
||||
- Fixed the file attachment behavior. The app now parses each file and stores its content without a chat request. One submit sends exactly one `streamChat` call, not one call for each attached file.
|
||||
- Removed the legacy PDF dependency chain: `pdf-to-markdown-core`, `pdfjs-dist`, `pdf-config.ts`, and the related build configuration. `@firecrawl/pdf-inspector-wasm` is now the only PDF dependency.
|
||||
- Simplified the worker boundary in follow-up refactors: merged redundant methods, removed dead code, and inlined a single-use interface. The full test suite continued to pass.
|
||||
353
web/package-lock.json
generated
353
web/package-lock.json
generated
|
|
@ -25,6 +25,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.39.5",
|
||||
"@firecrawl/pdf-inspector-wasm": "0.1.3",
|
||||
"@skeletonlabs/skeleton": "^5.0.0",
|
||||
"@skeletonlabs/skeleton-svelte": "^5.0.0",
|
||||
"@sveltejs/adapter-auto": "^3.3.1",
|
||||
|
|
@ -41,8 +42,6 @@
|
|||
"lucide-svelte": "^0.575.0",
|
||||
"mdsvex": "^0.12.8",
|
||||
"patch-package": "^8.0.1",
|
||||
"pdf-to-markdown-core": "github:jzillmann/pdf-to-markdown#modularize",
|
||||
"pdfjs-dist": "^6.2.108",
|
||||
"postcss": "^8.5.25",
|
||||
"prettier": "^3.9.6",
|
||||
"prettier-plugin-svelte": "^4.1.1",
|
||||
|
|
@ -266,6 +265,13 @@
|
|||
"node": "^20.19.0 || ^22.13.0 || >=24"
|
||||
}
|
||||
},
|
||||
"node_modules/@firecrawl/pdf-inspector-wasm": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@firecrawl/pdf-inspector-wasm/-/pdf-inspector-wasm-0.1.3.tgz",
|
||||
"integrity": "sha512-8gz3s3H/WABomls64YCZ8W6zd+eIxFgNAfCXI9+a7axDekZQHX21rsng1uxImoBZD3mXpN7XqLH/LS68WnGWhw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@floating-ui/core": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.8.0.tgz",
|
||||
|
|
@ -417,283 +423,6 @@
|
|||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@napi-rs/canvas": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-1.0.3.tgz",
|
||||
"integrity": "sha512-OlI657a5XXvKGFX7kNeIzJ8rO7IXt87Mqu2H8rXE46viAuOfum/JA7ysX7+eBhxNKznT+RCZh418mndlcFX3+w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"workspaces": [
|
||||
"e2e/*"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/Brooooooklyn"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@napi-rs/canvas-android-arm64": "1.0.3",
|
||||
"@napi-rs/canvas-darwin-arm64": "1.0.3",
|
||||
"@napi-rs/canvas-darwin-x64": "1.0.3",
|
||||
"@napi-rs/canvas-linux-arm-gnueabihf": "1.0.3",
|
||||
"@napi-rs/canvas-linux-arm64-gnu": "1.0.3",
|
||||
"@napi-rs/canvas-linux-arm64-musl": "1.0.3",
|
||||
"@napi-rs/canvas-linux-riscv64-gnu": "1.0.3",
|
||||
"@napi-rs/canvas-linux-x64-gnu": "1.0.3",
|
||||
"@napi-rs/canvas-linux-x64-musl": "1.0.3",
|
||||
"@napi-rs/canvas-win32-arm64-msvc": "1.0.3",
|
||||
"@napi-rs/canvas-win32-x64-msvc": "1.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@napi-rs/canvas-android-arm64": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-1.0.3.tgz",
|
||||
"integrity": "sha512-7kSCdUhoXiO+AaIMXdBGdtp6EctZNkmF62Rea/BmVQlwKaM3bBhOzyGUzxyxz9dv5vdBfpyAaxhSRSJF4kqK4A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/Brooooooklyn"
|
||||
}
|
||||
},
|
||||
"node_modules/@napi-rs/canvas-darwin-arm64": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-1.0.3.tgz",
|
||||
"integrity": "sha512-ds14V1BPagLszQyaDTeggny5fNeTCqsUQ5QhFj9VDxSEfzrVxXtdbR0LoFyKa0Siaaw8KvqSk4t7k/WoZJwvbg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/Brooooooklyn"
|
||||
}
|
||||
},
|
||||
"node_modules/@napi-rs/canvas-darwin-x64": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-1.0.3.tgz",
|
||||
"integrity": "sha512-qof3LRAAycmkV2I1izZo9RoSHF8kCQr5O05sFwv0jK8rSdYV6KHVwimo6Qb7RxZj40WHKbLHm5JDaUF0o5XUAA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/Brooooooklyn"
|
||||
}
|
||||
},
|
||||
"node_modules/@napi-rs/canvas-linux-arm-gnueabihf": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-1.0.3.tgz",
|
||||
"integrity": "sha512-FU2kKZLmolHA9+KcUA+l1+xH3WTLUUTQDU/kLv9SEUr2TrRPu94aytOeizFJDHPs/QBcw4QL1mCQhetQXYBbag==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/Brooooooklyn"
|
||||
}
|
||||
},
|
||||
"node_modules/@napi-rs/canvas-linux-arm64-gnu": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-1.0.3.tgz",
|
||||
"integrity": "sha512-GVSjntxKeA+/y/ZKf1F+cmUw1WeIkE5aMRPqnZUlBTBvBcrvgWccJAWuYCKPX4QJQwZILIIwhgdAbl51yj6fpA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/Brooooooklyn"
|
||||
}
|
||||
},
|
||||
"node_modules/@napi-rs/canvas-linux-arm64-musl": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-1.0.3.tgz",
|
||||
"integrity": "sha512-J51oK/axyZ13kxycumSMfLiDZMdWdOVvqDFI28BpuViZHE3A0bQfr8B5vg8YnPEnqLD3BSn1hkdlh2buspEcNQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/Brooooooklyn"
|
||||
}
|
||||
},
|
||||
"node_modules/@napi-rs/canvas-linux-riscv64-gnu": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-1.0.3.tgz",
|
||||
"integrity": "sha512-CtQgQjoVTX67jS9XuCTtJ40Sl7wRLMguoFnnGnfDmCWf7kzKFZVwj5ynqUOIGKFMSB61ZCuQlwPvVNxYTTseaw==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/Brooooooklyn"
|
||||
}
|
||||
},
|
||||
"node_modules/@napi-rs/canvas-linux-x64-gnu": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-1.0.3.tgz",
|
||||
"integrity": "sha512-jtfzAHFp+FRaR7zGT4jyCe6wUgAG/dVb5A4Apd8FY9jKarntDfUAlJXscugiH7ZF5kKnu7/lHFk9LaDPcrGEVQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/Brooooooklyn"
|
||||
}
|
||||
},
|
||||
"node_modules/@napi-rs/canvas-linux-x64-musl": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-1.0.3.tgz",
|
||||
"integrity": "sha512-xTzaUCKUHTY4bCGadeeRZggbRVbGUT1petg7Z8r9AJR2+D9Bqu6nQAgqBGC6D47tA70LjaaaLTrJ7wNY1T74dg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/Brooooooklyn"
|
||||
}
|
||||
},
|
||||
"node_modules/@napi-rs/canvas-win32-arm64-msvc": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-arm64-msvc/-/canvas-win32-arm64-msvc-1.0.3.tgz",
|
||||
"integrity": "sha512-ktVLuBkI6QVOm5BwO/WbdGwxgeetAMJa7TTmR8qBarXF0OU2NKjvjUtPJAl2y8t+zBRczJl/1VOl9gua6WcK2g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/Brooooooklyn"
|
||||
}
|
||||
},
|
||||
"node_modules/@napi-rs/canvas-win32-x64-msvc": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-1.0.3.tgz",
|
||||
"integrity": "sha512-SGhlQ8bDjL1Cz2KnsKMasr/5sTcwG/SZkB6WCJxLsmSm/3aS2C+3p39bA7iZ2/94+NkVDySZfbiGoaSZSFHYxA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/Brooooooklyn"
|
||||
}
|
||||
},
|
||||
"node_modules/@napi-rs/wasm-runtime": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.1.tgz",
|
||||
|
|
@ -2041,13 +1770,6 @@
|
|||
"undici-types": "~8.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/string-similarity": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/string-similarity/-/string-similarity-4.0.2.tgz",
|
||||
"integrity": "sha512-LkJQ/jsXtCVMK+sKYAmX/8zEq+/46f1PTQw7YtmQwb74jemS1SlNLmARM2Zml9DgdDTWKAtc5L13WorpHPDjDA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/trusted-types": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
|
||||
|
|
@ -5633,32 +5355,6 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pdf-to-markdown-core": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "git+ssh://git@github.com/jzillmann/pdf-to-markdown.git#5e930fafd305644cff00a3824030f0d6741acde8",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
"@types/string-similarity": "^4.0.0",
|
||||
"simple-statistics": "^7.7.0",
|
||||
"string-similarity": "^4.0.4",
|
||||
"uuid": "^9.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/pdfjs-dist": {
|
||||
"version": "6.2.108",
|
||||
"resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-6.2.108.tgz",
|
||||
"integrity": "sha512-YxFb+SQcodN2rnX9Tn3dHYlqfb7NjlzzfONPpJd+AKoKtUjEdevTfbC07d5TcczzOK6261auRkP/M8OBHs9vFQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=22.13.0 || >=24"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@napi-rs/canvas": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||
|
|
@ -6392,16 +6088,6 @@
|
|||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/simple-statistics": {
|
||||
"version": "7.9.3",
|
||||
"resolved": "https://registry.npmjs.org/simple-statistics/-/simple-statistics-7.9.3.tgz",
|
||||
"integrity": "sha512-WXpxUfo7BJCRpyl4besiuMV7wNj9xiPIq7IKmUQO4upIaF8pK2AXwhjttHN5L8KXZrLkGMCHGHq4p+pJXiIahQ==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/sirv": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz",
|
||||
|
|
@ -6461,14 +6147,6 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/string-similarity": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/string-similarity/-/string-similarity-4.0.4.tgz",
|
||||
"integrity": "sha512-/q/8Q4Bl4ZKAPjj8WerIBJWALKkaPRfrvhfF8k/B23i4nzrlRj2/go1m90In7nG/3XDSbOo0+pu6RvCTM9RGMQ==",
|
||||
"deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/stringify-entities": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz",
|
||||
|
|
@ -7016,21 +6694,6 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "9.0.1",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
|
||||
"integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
|
||||
"deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
"https://github.com/sponsors/broofa",
|
||||
"https://github.com/sponsors/ctavan"
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/vfile": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz",
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.39.5",
|
||||
"@firecrawl/pdf-inspector-wasm": "0.1.3",
|
||||
"@skeletonlabs/skeleton": "^5.0.0",
|
||||
"@skeletonlabs/skeleton-svelte": "^5.0.0",
|
||||
"@sveltejs/adapter-auto": "^3.3.1",
|
||||
|
|
@ -33,8 +34,6 @@
|
|||
"lucide-svelte": "^0.575.0",
|
||||
"mdsvex": "^0.12.8",
|
||||
"patch-package": "^8.0.1",
|
||||
"pdf-to-markdown-core": "github:jzillmann/pdf-to-markdown#modularize",
|
||||
"pdfjs-dist": "^6.2.108",
|
||||
"postcss": "^8.5.25",
|
||||
"prettier": "^3.9.6",
|
||||
"prettier-plugin-svelte": "^4.1.1",
|
||||
|
|
|
|||
|
|
@ -71,6 +71,9 @@ importers:
|
|||
'@eslint/js':
|
||||
specifier: ^9.39.5
|
||||
version: 9.39.5
|
||||
'@firecrawl/pdf-inspector-wasm':
|
||||
specifier: 0.1.3
|
||||
version: 0.1.3
|
||||
'@skeletonlabs/skeleton':
|
||||
specifier: ^5.0.0
|
||||
version: 5.0.0(tailwindcss@4.3.3)
|
||||
|
|
@ -119,12 +122,6 @@ importers:
|
|||
patch-package:
|
||||
specifier: ^8.0.1
|
||||
version: 8.0.1
|
||||
pdf-to-markdown-core:
|
||||
specifier: github:jzillmann/pdf-to-markdown#modularize
|
||||
version: https://codeload.github.com/jzillmann/pdf-to-markdown/tar.gz/5e930fafd305644cff00a3824030f0d6741acde8
|
||||
pdfjs-dist:
|
||||
specifier: ^6.2.108
|
||||
version: 6.2.108
|
||||
postcss:
|
||||
specifier: ^8.5.25
|
||||
version: 8.5.25
|
||||
|
|
@ -382,6 +379,9 @@ packages:
|
|||
resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@firecrawl/pdf-inspector-wasm@0.1.3':
|
||||
resolution: {integrity: sha512-8gz3s3H/WABomls64YCZ8W6zd+eIxFgNAfCXI9+a7axDekZQHX21rsng1uxImoBZD3mXpN7XqLH/LS68WnGWhw==}
|
||||
|
||||
'@floating-ui/core@1.8.0':
|
||||
resolution: {integrity: sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==}
|
||||
|
||||
|
|
@ -430,81 +430,6 @@ packages:
|
|||
'@jridgewell/trace-mapping@0.3.31':
|
||||
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
|
||||
|
||||
'@napi-rs/canvas-android-arm64@1.0.3':
|
||||
resolution: {integrity: sha512-7kSCdUhoXiO+AaIMXdBGdtp6EctZNkmF62Rea/BmVQlwKaM3bBhOzyGUzxyxz9dv5vdBfpyAaxhSRSJF4kqK4A==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
'@napi-rs/canvas-darwin-arm64@1.0.3':
|
||||
resolution: {integrity: sha512-ds14V1BPagLszQyaDTeggny5fNeTCqsUQ5QhFj9VDxSEfzrVxXtdbR0LoFyKa0Siaaw8KvqSk4t7k/WoZJwvbg==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@napi-rs/canvas-darwin-x64@1.0.3':
|
||||
resolution: {integrity: sha512-qof3LRAAycmkV2I1izZo9RoSHF8kCQr5O05sFwv0jK8rSdYV6KHVwimo6Qb7RxZj40WHKbLHm5JDaUF0o5XUAA==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@napi-rs/canvas-linux-arm-gnueabihf@1.0.3':
|
||||
resolution: {integrity: sha512-FU2kKZLmolHA9+KcUA+l1+xH3WTLUUTQDU/kLv9SEUr2TrRPu94aytOeizFJDHPs/QBcw4QL1mCQhetQXYBbag==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@napi-rs/canvas-linux-arm64-gnu@1.0.3':
|
||||
resolution: {integrity: sha512-GVSjntxKeA+/y/ZKf1F+cmUw1WeIkE5aMRPqnZUlBTBvBcrvgWccJAWuYCKPX4QJQwZILIIwhgdAbl51yj6fpA==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@napi-rs/canvas-linux-arm64-musl@1.0.3':
|
||||
resolution: {integrity: sha512-J51oK/axyZ13kxycumSMfLiDZMdWdOVvqDFI28BpuViZHE3A0bQfr8B5vg8YnPEnqLD3BSn1hkdlh2buspEcNQ==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@napi-rs/canvas-linux-riscv64-gnu@1.0.3':
|
||||
resolution: {integrity: sha512-CtQgQjoVTX67jS9XuCTtJ40Sl7wRLMguoFnnGnfDmCWf7kzKFZVwj5ynqUOIGKFMSB61ZCuQlwPvVNxYTTseaw==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@napi-rs/canvas-linux-x64-gnu@1.0.3':
|
||||
resolution: {integrity: sha512-jtfzAHFp+FRaR7zGT4jyCe6wUgAG/dVb5A4Apd8FY9jKarntDfUAlJXscugiH7ZF5kKnu7/lHFk9LaDPcrGEVQ==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@napi-rs/canvas-linux-x64-musl@1.0.3':
|
||||
resolution: {integrity: sha512-xTzaUCKUHTY4bCGadeeRZggbRVbGUT1petg7Z8r9AJR2+D9Bqu6nQAgqBGC6D47tA70LjaaaLTrJ7wNY1T74dg==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@napi-rs/canvas-win32-arm64-msvc@1.0.3':
|
||||
resolution: {integrity: sha512-ktVLuBkI6QVOm5BwO/WbdGwxgeetAMJa7TTmR8qBarXF0OU2NKjvjUtPJAl2y8t+zBRczJl/1VOl9gua6WcK2g==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@napi-rs/canvas-win32-x64-msvc@1.0.3':
|
||||
resolution: {integrity: sha512-SGhlQ8bDjL1Cz2KnsKMasr/5sTcwG/SZkB6WCJxLsmSm/3aS2C+3p39bA7iZ2/94+NkVDySZfbiGoaSZSFHYxA==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@napi-rs/canvas@1.0.3':
|
||||
resolution: {integrity: sha512-OlI657a5XXvKGFX7kNeIzJ8rO7IXt87Mqu2H8rXE46viAuOfum/JA7ysX7+eBhxNKznT+RCZh418mndlcFX3+w==}
|
||||
engines: {node: '>= 10'}
|
||||
|
||||
'@napi-rs/wasm-runtime@1.2.0':
|
||||
resolution: {integrity: sha512-kDoONqMa+VnZ4vvvu/ZUurpJ4gkZU57e7g69qpNgWhYcZFPUHZM2CEMKm+cG6ufDVALbjMvfmMjFVqaK7uEMnA==}
|
||||
engines: {node: ^20.19.0 || ^22.13.0 || >=23.5.0}
|
||||
|
|
@ -834,9 +759,6 @@ packages:
|
|||
'@types/node@26.1.2':
|
||||
resolution: {integrity: sha512-Vu4a5UFA9rIIFJ7rB/Vaafh9lrCQszopTCx6KjFboXTGQbPNasehVR5TEiithSDGyd1DEiUByggTZsg8jukeIg==}
|
||||
|
||||
'@types/string-similarity@4.0.2':
|
||||
resolution: {integrity: sha512-LkJQ/jsXtCVMK+sKYAmX/8zEq+/46f1PTQw7YtmQwb74jemS1SlNLmARM2Zml9DgdDTWKAtc5L13WorpHPDjDA==}
|
||||
|
||||
'@types/trusted-types@2.0.7':
|
||||
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
|
||||
|
||||
|
|
@ -1919,14 +1841,6 @@ packages:
|
|||
pathe@2.0.3:
|
||||
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
|
||||
|
||||
pdf-to-markdown-core@https://codeload.github.com/jzillmann/pdf-to-markdown/tar.gz/5e930fafd305644cff00a3824030f0d6741acde8:
|
||||
resolution: {gitHosted: true, tarball: https://codeload.github.com/jzillmann/pdf-to-markdown/tar.gz/5e930fafd305644cff00a3824030f0d6741acde8}
|
||||
version: 0.5.0
|
||||
|
||||
pdfjs-dist@6.2.108:
|
||||
resolution: {integrity: sha512-YxFb+SQcodN2rnX9Tn3dHYlqfb7NjlzzfONPpJd+AKoKtUjEdevTfbC07d5TcczzOK6261auRkP/M8OBHs9vFQ==}
|
||||
engines: {node: '>=22.13.0 || >=24'}
|
||||
|
||||
picocolors@1.1.1:
|
||||
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
||||
|
||||
|
|
@ -2084,9 +1998,6 @@ packages:
|
|||
siginfo@2.0.0:
|
||||
resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
|
||||
|
||||
simple-statistics@7.9.3:
|
||||
resolution: {integrity: sha512-WXpxUfo7BJCRpyl4besiuMV7wNj9xiPIq7IKmUQO4upIaF8pK2AXwhjttHN5L8KXZrLkGMCHGHq4p+pJXiIahQ==}
|
||||
|
||||
sirv@3.0.2:
|
||||
resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==}
|
||||
engines: {node: '>=18'}
|
||||
|
|
@ -2108,10 +2019,6 @@ packages:
|
|||
std-env@4.2.0:
|
||||
resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==}
|
||||
|
||||
string-similarity@4.0.4:
|
||||
resolution: {integrity: sha512-/q/8Q4Bl4ZKAPjj8WerIBJWALKkaPRfrvhfF8k/B23i4nzrlRj2/go1m90In7nG/3XDSbOo0+pu6RvCTM9RGMQ==}
|
||||
deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.
|
||||
|
||||
stringify-entities@4.0.4:
|
||||
resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==}
|
||||
|
||||
|
|
@ -2277,11 +2184,6 @@ packages:
|
|||
util-deprecate@1.0.2:
|
||||
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
||||
|
||||
uuid@9.0.1:
|
||||
resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
|
||||
deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).
|
||||
hasBin: true
|
||||
|
||||
vfile-location@5.0.3:
|
||||
resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==}
|
||||
|
||||
|
|
@ -2568,6 +2470,8 @@ snapshots:
|
|||
'@eslint/core': 0.17.0
|
||||
levn: 0.4.1
|
||||
|
||||
'@firecrawl/pdf-inspector-wasm@0.1.3': {}
|
||||
|
||||
'@floating-ui/core@1.8.0':
|
||||
dependencies:
|
||||
'@floating-ui/utils': 0.2.12
|
||||
|
|
@ -2618,54 +2522,6 @@ snapshots:
|
|||
'@jridgewell/resolve-uri': 3.1.2
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
|
||||
'@napi-rs/canvas-android-arm64@1.0.3':
|
||||
optional: true
|
||||
|
||||
'@napi-rs/canvas-darwin-arm64@1.0.3':
|
||||
optional: true
|
||||
|
||||
'@napi-rs/canvas-darwin-x64@1.0.3':
|
||||
optional: true
|
||||
|
||||
'@napi-rs/canvas-linux-arm-gnueabihf@1.0.3':
|
||||
optional: true
|
||||
|
||||
'@napi-rs/canvas-linux-arm64-gnu@1.0.3':
|
||||
optional: true
|
||||
|
||||
'@napi-rs/canvas-linux-arm64-musl@1.0.3':
|
||||
optional: true
|
||||
|
||||
'@napi-rs/canvas-linux-riscv64-gnu@1.0.3':
|
||||
optional: true
|
||||
|
||||
'@napi-rs/canvas-linux-x64-gnu@1.0.3':
|
||||
optional: true
|
||||
|
||||
'@napi-rs/canvas-linux-x64-musl@1.0.3':
|
||||
optional: true
|
||||
|
||||
'@napi-rs/canvas-win32-arm64-msvc@1.0.3':
|
||||
optional: true
|
||||
|
||||
'@napi-rs/canvas-win32-x64-msvc@1.0.3':
|
||||
optional: true
|
||||
|
||||
'@napi-rs/canvas@1.0.3':
|
||||
optionalDependencies:
|
||||
'@napi-rs/canvas-android-arm64': 1.0.3
|
||||
'@napi-rs/canvas-darwin-arm64': 1.0.3
|
||||
'@napi-rs/canvas-darwin-x64': 1.0.3
|
||||
'@napi-rs/canvas-linux-arm-gnueabihf': 1.0.3
|
||||
'@napi-rs/canvas-linux-arm64-gnu': 1.0.3
|
||||
'@napi-rs/canvas-linux-arm64-musl': 1.0.3
|
||||
'@napi-rs/canvas-linux-riscv64-gnu': 1.0.3
|
||||
'@napi-rs/canvas-linux-x64-gnu': 1.0.3
|
||||
'@napi-rs/canvas-linux-x64-musl': 1.0.3
|
||||
'@napi-rs/canvas-win32-arm64-msvc': 1.0.3
|
||||
'@napi-rs/canvas-win32-x64-msvc': 1.0.3
|
||||
optional: true
|
||||
|
||||
'@napi-rs/wasm-runtime@1.2.0(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)':
|
||||
dependencies:
|
||||
'@emnapi/core': 1.11.1
|
||||
|
|
@ -2964,8 +2820,6 @@ snapshots:
|
|||
dependencies:
|
||||
undici-types: 8.3.0
|
||||
|
||||
'@types/string-similarity@4.0.2': {}
|
||||
|
||||
'@types/trusted-types@2.0.7': {}
|
||||
|
||||
'@types/unist@2.0.11': {}
|
||||
|
|
@ -4257,17 +4111,6 @@ snapshots:
|
|||
|
||||
pathe@2.0.3: {}
|
||||
|
||||
pdf-to-markdown-core@https://codeload.github.com/jzillmann/pdf-to-markdown/tar.gz/5e930fafd305644cff00a3824030f0d6741acde8:
|
||||
dependencies:
|
||||
'@types/string-similarity': 4.0.2
|
||||
simple-statistics: 7.9.3
|
||||
string-similarity: 4.0.4
|
||||
uuid: 9.0.1
|
||||
|
||||
pdfjs-dist@6.2.108:
|
||||
optionalDependencies:
|
||||
'@napi-rs/canvas': 1.0.3
|
||||
|
||||
picocolors@1.1.1: {}
|
||||
|
||||
picomatch@2.3.2: {}
|
||||
|
|
@ -4451,8 +4294,6 @@ snapshots:
|
|||
|
||||
siginfo@2.0.0: {}
|
||||
|
||||
simple-statistics@7.9.3: {}
|
||||
|
||||
sirv@3.0.2:
|
||||
dependencies:
|
||||
'@polka/url': 1.0.0-next.29
|
||||
|
|
@ -4469,8 +4310,6 @@ snapshots:
|
|||
|
||||
std-env@4.2.0: {}
|
||||
|
||||
string-similarity@4.0.4: {}
|
||||
|
||||
stringify-entities@4.0.4:
|
||||
dependencies:
|
||||
character-entities-html4: 2.1.0
|
||||
|
|
@ -4660,8 +4499,6 @@ snapshots:
|
|||
|
||||
util-deprecate@1.0.2: {}
|
||||
|
||||
uuid@9.0.1: {}
|
||||
|
||||
vfile-location@5.0.3:
|
||||
dependencies:
|
||||
'@types/unist': 3.0.3
|
||||
|
|
|
|||
|
|
@ -46,8 +46,3 @@ peerDependencyRules:
|
|||
allowBuilds:
|
||||
esbuild: true
|
||||
svelte-preprocess: true
|
||||
# A git-hosted package needs the full dependency path as the key, because
|
||||
# pnpm does not trust the identity of a package that has no semver version.
|
||||
# The commit hash must agree with the version in pnpm-lock.yaml. If you move
|
||||
# the "modularize" branch, update this key and the lockfile together.
|
||||
'pdf-to-markdown-core@https://codeload.github.com/jzillmann/pdf-to-markdown/tar.gz/5e930fafd305644cff00a3824030f0d6741acde8': true
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { Button } from "$lib/components/ui/button";
|
||||
import { Textarea } from "$lib/components/ui/textarea";
|
||||
import { sendMessage, messageStore } from '$lib/store/chat-store';
|
||||
import { messageStore } from '$lib/store/chat-store';
|
||||
import { systemPrompt, selectedPatternName } from '$lib/store/pattern-store';
|
||||
import { toastStore } from '$lib/store/toast-store';
|
||||
import { Paperclip, Send, FileCheck } from 'lucide-svelte';
|
||||
|
|
@ -14,6 +14,7 @@
|
|||
import { obsidianSettings, updateObsidianSettings } from '$lib/store/obsidian-store';
|
||||
import { PdfConversionService } from '$lib/services/PdfConversionService';
|
||||
import { formatErrorMessage } from '$lib/utils/error-message';
|
||||
import { readFileContent } from './read-file-content';
|
||||
|
||||
const pdfService = new PdfConversionService();
|
||||
|
||||
|
|
@ -101,7 +102,7 @@
|
|||
|
||||
for (let i = 0; i < files.length && uploadedFiles.length < 5; i++) {
|
||||
const file = files[i];
|
||||
const content = await readFileContent(file);
|
||||
const content = await readFileContent(file, pdfService, toastStore.warning);
|
||||
fileContents.push(content);
|
||||
uploadedFiles = [...uploadedFiles, file.name];
|
||||
|
||||
|
|
@ -134,104 +135,6 @@
|
|||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
async function readFileContent(file: File): Promise<string> {
|
||||
// Log initial file metadata
|
||||
console.log('Reading file:', {
|
||||
name: file.name,
|
||||
type: file.type,
|
||||
size: file.size,
|
||||
lastModified: new Date(file.lastModified).toISOString()
|
||||
});
|
||||
|
||||
// Handle PDF files
|
||||
if (file.type === 'application/pdf') {
|
||||
try {
|
||||
// Start PDF processing
|
||||
console.log('Starting PDF conversion process');
|
||||
const markdown = await pdfService.convertToMarkdown(file);
|
||||
|
||||
// Validate conversion result
|
||||
console.log('PDF conversion completed:', {
|
||||
resultLength: markdown.length,
|
||||
preview: markdown.substring(0, 100)
|
||||
});
|
||||
|
||||
// Ensure we have valid content
|
||||
if (!markdown || markdown.trim().length === 0) {
|
||||
throw new Error('PDF conversion returned empty content');
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Add to fileContents for pattern processing
|
||||
fileContents.push(markdown);
|
||||
|
||||
// Prepare enhanced prompt with system instructions
|
||||
const enhancedPrompt = `${$systemPrompt}\nAnalyze and process the provided content according to these instructions.`;
|
||||
|
||||
// Format final content with proper labeling
|
||||
const finalContent = `${userInput}\n\nFile Contents (PDF):\n${markdown}`;
|
||||
|
||||
// Process through pattern system
|
||||
await sendMessage(finalContent, enhancedPrompt);
|
||||
|
||||
return markdown;
|
||||
|
||||
} catch (error) {
|
||||
console.error('PDF Conversion error:', {
|
||||
error,
|
||||
fileName: file.name,
|
||||
fileSize: file.size
|
||||
});
|
||||
|
||||
const errorMessage = error instanceof Error
|
||||
? error.message
|
||||
: 'Unknown error during PDF conversion';
|
||||
|
||||
throw new Error(`Failed to convert PDF ${file.name}: ${errorMessage}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle text files
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onload = async (e) => {
|
||||
const content = e.target?.result as string;
|
||||
console.log('Text file processed:', {
|
||||
fileName: file.name,
|
||||
contentLength: content.length,
|
||||
preview: content.substring(0, 100)
|
||||
});
|
||||
// resolve(content);
|
||||
const enhancedPrompt = `${$systemPrompt}\nAnalyze and process the provided content according to these instructions.`;
|
||||
const finalContent = `${userInput}\n\nFile Contents (Text):\n${content}`;
|
||||
await sendMessage(finalContent, enhancedPrompt);
|
||||
resolve(content);
|
||||
};
|
||||
|
||||
reader.onerror = (e) => {
|
||||
console.error('FileReader error:', {
|
||||
error: reader.error,
|
||||
fileName: file.name
|
||||
});
|
||||
reject(new Error(`Failed to read ${file.name}: ${reader.error?.message}`));
|
||||
};
|
||||
|
||||
// Start reading the file
|
||||
reader.readAsText(file);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
async function saveToObsidian(content: string) {
|
||||
if (!$obsidianSettings.saveToObsidian) {
|
||||
console.log('Obsidian saving is disabled');
|
||||
|
|
@ -440,54 +343,6 @@ async function readFileContent(file: File): Promise<string> {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* async function handleSubmit() {
|
||||
if (!userInput.trim()) return;
|
||||
|
||||
try {
|
||||
console.log('\n=== Submit Handler Start ===');
|
||||
|
||||
if (isYouTubeURL) {
|
||||
console.log('2a. Starting YouTube flow');
|
||||
await processYouTubeURL(userInput);
|
||||
return;
|
||||
}
|
||||
|
||||
const enhancedPrompt = fileContents.length > 0
|
||||
? `${$systemPrompt}\nAnalyze and process the provided content according to these instructions.`
|
||||
: $systemPrompt;
|
||||
|
||||
// Hide raw content from display but keep it for processing
|
||||
messageStore.update(messages => [...messages, {
|
||||
role: 'system',
|
||||
content: 'Processing content...',
|
||||
format: 'loading'
|
||||
}]);
|
||||
|
||||
// Store the user input before clearing it
|
||||
const inputText = userInput;
|
||||
|
||||
// Construct finalContent BEFORE clearing userInput
|
||||
const finalContent = fileContents.length > 0
|
||||
? `${inputText}\n\nFile Contents (${uploadedFiles.map(f => f.endsWith('.pdf') ? 'PDF' : 'Text').join(', ')}):\n${fileContents.join('\n\n---\n\n')}`
|
||||
: inputText;
|
||||
|
||||
// Now clear the input fields
|
||||
userInput = "";
|
||||
uploadedFiles = [];
|
||||
fileContents = [];
|
||||
fileButtonKey = !fileButtonKey;
|
||||
|
||||
await sendMessage(finalContent, enhancedPrompt);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Chat submission error:', error);
|
||||
}
|
||||
} */
|
||||
|
||||
|
||||
|
||||
|
||||
function handleKeydown(event: KeyboardEvent) {
|
||||
if (event.key === 'Enter' && !event.shiftKey) {
|
||||
event.preventDefault();
|
||||
|
|
|
|||
56
web/src/lib/components/chat/read-file-content.test.ts
Normal file
56
web/src/lib/components/chat/read-file-content.test.ts
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
import { readFileSync } from 'node:fs';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { readFileContent } from './read-file-content';
|
||||
|
||||
const pdfBytes = new Uint8Array([0x25, 0x50, 0x44, 0x46]);
|
||||
|
||||
describe('readFileContent', () => {
|
||||
const noPdf = {
|
||||
convertToMarkdown: () => Promise.reject(new Error('unexpected PDF conversion'))
|
||||
};
|
||||
|
||||
it('resolves with the text of a text file', async () => {
|
||||
const file = new File(['# Notes\n\nPlain text.\n'], 'notes.md', { type: 'text/markdown' });
|
||||
|
||||
await expect(readFileContent(file, noPdf, () => {})).resolves.toBe('# Notes\n\nPlain text.\n');
|
||||
});
|
||||
|
||||
it('resolves with the converted Markdown of a PDF file', async () => {
|
||||
const pdf = {
|
||||
convertToMarkdown: vi.fn().mockResolvedValue({ markdown: '# Title\n' })
|
||||
};
|
||||
const file = new File([pdfBytes], 'report.pdf', { type: 'application/pdf' });
|
||||
|
||||
await expect(readFileContent(file, pdf, () => {})).resolves.toBe('# Title\n');
|
||||
expect(pdf.convertToMarkdown).toHaveBeenCalledWith(file);
|
||||
});
|
||||
|
||||
it('forwards the conversion warning of a PDF file', async () => {
|
||||
const pdf = {
|
||||
convertToMarkdown: () => Promise.resolve({ markdown: '# Title\n', warning: 'partial text' })
|
||||
};
|
||||
const warn = vi.fn();
|
||||
const file = new File([pdfBytes], 'report.pdf', { type: 'application/pdf' });
|
||||
|
||||
await readFileContent(file, pdf, warn);
|
||||
|
||||
expect(warn).toHaveBeenCalledWith('partial text');
|
||||
});
|
||||
});
|
||||
|
||||
// The repository has no browser test runner, so these tests pin the chat
|
||||
// boundary at the source level: the component has no sendMessage path, and
|
||||
// handleSubmit holds the one streamChat call. Together with the seam tests
|
||||
// above, this proves that attachment parses without a chat request and that
|
||||
// one submit sends one request.
|
||||
describe('ChatInput chat boundary', () => {
|
||||
const source = readFileSync(new URL('./ChatInput.svelte', import.meta.url), 'utf8');
|
||||
|
||||
it('has no sendMessage path, so a file attachment cannot send a chat request', () => {
|
||||
expect(source.includes('sendMessage')).toBe(false);
|
||||
});
|
||||
|
||||
it('sends through exactly one streamChat call, in handleSubmit', () => {
|
||||
expect(source.match(/\.streamChat\(/g) ?? []).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
17
web/src/lib/components/chat/read-file-content.ts
Normal file
17
web/src/lib/components/chat/read-file-content.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import type { PdfConversion } from '../../services/PdfConversionService';
|
||||
|
||||
// The parser seam for ChatInput. It converts one attached file to text.
|
||||
// Keep this module free of chat and store imports: that constraint is what
|
||||
// makes a file attachment unable to send a chat request.
|
||||
export async function readFileContent(
|
||||
file: File,
|
||||
pdf: { convertToMarkdown(file: File): Promise<PdfConversion> },
|
||||
warn: (message: string) => void
|
||||
): Promise<string> {
|
||||
if (file.type === 'application/pdf') {
|
||||
const { markdown, warning } = await pdf.convertToMarkdown(file);
|
||||
if (warning) warn(warning);
|
||||
return markdown;
|
||||
}
|
||||
return file.text();
|
||||
}
|
||||
199
web/src/lib/services/PdfConversionService.test.ts
Normal file
199
web/src/lib/services/PdfConversionService.test.ts
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
import type { PdfProcessResult } from '@firecrawl/pdf-inspector-wasm';
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
import { interpretPdfResult, PdfConversionService } from './PdfConversionService';
|
||||
|
||||
// Plain result objects replace the worker and the WASM module in these tests.
|
||||
// The shape copies the PdfProcessResult fields that the spike measured.
|
||||
function pdfResult(overrides: Partial<PdfProcessResult> = {}): PdfProcessResult {
|
||||
return {
|
||||
pdfType: 'TextBased',
|
||||
markdown: '# Title\n\nBody text.\n',
|
||||
pageCount: 1,
|
||||
processingTimeMs: 5,
|
||||
pagesNeedingOcr: [],
|
||||
ocrReasonsByPage: [],
|
||||
confidence: 0.5,
|
||||
layout: { isComplex: false, pagesWithTables: [], pagesWithColumns: [] },
|
||||
hasEncodingIssues: false,
|
||||
...overrides
|
||||
};
|
||||
}
|
||||
|
||||
describe('interpretPdfResult', () => {
|
||||
it('returns the Markdown with no warning for a TextBased result', () => {
|
||||
const conversion = interpretPdfResult(pdfResult(), 'report.pdf');
|
||||
|
||||
expect(conversion).toEqual({ markdown: '# Title\n\nBody text.\n' });
|
||||
});
|
||||
|
||||
it('throws the OCR message for a Scanned result', () => {
|
||||
const scanned = pdfResult({ pdfType: 'Scanned', markdown: undefined });
|
||||
|
||||
expect(() => interpretPdfResult(scanned, 'report.pdf')).toThrowError(
|
||||
'report.pdf contains no machine-readable text. OCR is necessary, and the Fabric web interface does not include OCR.'
|
||||
);
|
||||
});
|
||||
|
||||
it('throws the OCR message for an ImageBased result', () => {
|
||||
const imageBased = pdfResult({ pdfType: 'ImageBased', markdown: undefined });
|
||||
|
||||
expect(() => interpretPdfResult(imageBased, 'report.pdf')).toThrowError(
|
||||
'report.pdf contains no machine-readable text. OCR is necessary, and the Fabric web interface does not include OCR.'
|
||||
);
|
||||
});
|
||||
|
||||
it('keeps the Markdown and warns with the 1-indexed page list for a Mixed result', () => {
|
||||
const mixed = pdfResult({ pdfType: 'Mixed', pagesNeedingOcr: [2, 5] });
|
||||
|
||||
const conversion = interpretPdfResult(mixed, 'report.pdf');
|
||||
|
||||
expect(conversion.markdown).toBe('# Title\n\nBody text.\n');
|
||||
expect(conversion.warning).toBe(
|
||||
'report.pdf: pages 2, 5 contain no machine-readable text. OCR is necessary for those pages, and their content is not included.'
|
||||
);
|
||||
});
|
||||
|
||||
it('adds the decode warning when hasEncodingIssues is true', () => {
|
||||
const conversion = interpretPdfResult(pdfResult({ hasEncodingIssues: true }), 'report.pdf');
|
||||
|
||||
expect(conversion.markdown).toBe('# Title\n\nBody text.\n');
|
||||
expect(conversion.warning).toBe('Some text in report.pdf did not decode correctly.');
|
||||
});
|
||||
|
||||
it('joins the Mixed warning and the decode warning with one space', () => {
|
||||
const mixed = pdfResult({
|
||||
pdfType: 'Mixed',
|
||||
pagesNeedingOcr: [3],
|
||||
hasEncodingIssues: true
|
||||
});
|
||||
|
||||
const conversion = interpretPdfResult(mixed, 'report.pdf');
|
||||
|
||||
expect(conversion.warning).toBe(
|
||||
'report.pdf: pages 3 contain no machine-readable text. OCR is necessary for those pages, and their content is not included. ' +
|
||||
'Some text in report.pdf did not decode correctly.'
|
||||
);
|
||||
});
|
||||
|
||||
it('throws when the markdown is absent', () => {
|
||||
const noMarkdown = pdfResult({ markdown: undefined });
|
||||
|
||||
expect(() => interpretPdfResult(noMarkdown, 'report.pdf')).toThrowError(
|
||||
'report.pdf: the conversion returned no text.'
|
||||
);
|
||||
});
|
||||
|
||||
it('throws when the markdown is blank', () => {
|
||||
const blank = pdfResult({ markdown: ' \n' });
|
||||
|
||||
expect(() => interpretPdfResult(blank, 'report.pdf')).toThrowError(
|
||||
'report.pdf: the conversion returned no text.'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// The fake worker records each posted request and lets a test send a response.
|
||||
// This specifies the message contract without the WASM module.
|
||||
class FakeWorker {
|
||||
static instances: FakeWorker[] = [];
|
||||
onmessage: ((event: { data: unknown }) => void) | null = null;
|
||||
onerror: ((event: { message?: string }) => void) | null = null;
|
||||
terminated = false;
|
||||
posted: Array<{ data: { id: number; buffer: ArrayBuffer }; transfer: Transferable[] }> = [];
|
||||
|
||||
constructor() {
|
||||
FakeWorker.instances.push(this);
|
||||
}
|
||||
|
||||
postMessage(data: { id: number; buffer: ArrayBuffer }, transfer: Transferable[]) {
|
||||
this.posted.push({ data, transfer });
|
||||
}
|
||||
|
||||
terminate() {
|
||||
this.terminated = true;
|
||||
}
|
||||
}
|
||||
|
||||
describe('PdfConversionService worker messages', () => {
|
||||
afterEach(() => {
|
||||
vi.unstubAllGlobals();
|
||||
FakeWorker.instances.length = 0;
|
||||
});
|
||||
|
||||
function setup() {
|
||||
vi.stubGlobal('Worker', FakeWorker);
|
||||
const service = new PdfConversionService();
|
||||
const file = new File([new Uint8Array([0x25, 0x50, 0x44, 0x46])], 'report.pdf', {
|
||||
type: 'application/pdf'
|
||||
});
|
||||
return { service, file };
|
||||
}
|
||||
|
||||
it('posts the buffer in the transfer list and resolves on a success response', async () => {
|
||||
const { service, file } = setup();
|
||||
|
||||
const promise = service.convertToMarkdown(file);
|
||||
await vi.waitFor(() => expect(FakeWorker.instances[0]?.posted).toHaveLength(1));
|
||||
|
||||
const worker = FakeWorker.instances[0];
|
||||
const { data, transfer } = worker.posted[0];
|
||||
expect(transfer).toEqual([data.buffer]);
|
||||
|
||||
worker.onmessage?.({ data: { id: data.id, ok: true, result: pdfResult() } });
|
||||
|
||||
await expect(promise).resolves.toEqual({ markdown: '# Title\n\nBody text.\n' });
|
||||
});
|
||||
|
||||
it('rejects with the worker error text on a parser error response', async () => {
|
||||
const { service, file } = setup();
|
||||
|
||||
const promise = service.convertToMarkdown(file);
|
||||
await vi.waitFor(() => expect(FakeWorker.instances[0]?.posted).toHaveLength(1));
|
||||
|
||||
const worker = FakeWorker.instances[0];
|
||||
const { data } = worker.posted[0];
|
||||
worker.onmessage?.({
|
||||
data: {
|
||||
id: data.id,
|
||||
ok: false,
|
||||
error: 'Error: process PDF: Not a PDF: file appears to be plain text'
|
||||
}
|
||||
});
|
||||
|
||||
await expect(promise).rejects.toThrowError('Not a PDF: file appears to be plain text');
|
||||
});
|
||||
|
||||
it('rejects all pending requests and terminates the worker on a worker error', async () => {
|
||||
const { service, file } = setup();
|
||||
|
||||
const first = service.convertToMarkdown(file);
|
||||
const second = service.convertToMarkdown(file);
|
||||
await vi.waitFor(() => expect(FakeWorker.instances[0]?.posted).toHaveLength(2));
|
||||
|
||||
const worker = FakeWorker.instances[0];
|
||||
worker.onerror?.({ message: 'worker script did not load' });
|
||||
|
||||
await expect(first).rejects.toThrowError('The PDF worker failed.');
|
||||
await expect(second).rejects.toThrowError('The PDF worker failed.');
|
||||
expect(worker.terminated).toBe(true);
|
||||
});
|
||||
|
||||
it('creates a new worker for the request that follows a worker error', async () => {
|
||||
const { service, file } = setup();
|
||||
|
||||
const failed = service.convertToMarkdown(file);
|
||||
await vi.waitFor(() => expect(FakeWorker.instances[0]?.posted).toHaveLength(1));
|
||||
FakeWorker.instances[0].onerror?.({ message: 'worker script did not load' });
|
||||
await expect(failed).rejects.toThrowError();
|
||||
|
||||
const retry = service.convertToMarkdown(file);
|
||||
await vi.waitFor(() => expect(FakeWorker.instances).toHaveLength(2));
|
||||
|
||||
const worker = FakeWorker.instances[1];
|
||||
await vi.waitFor(() => expect(worker.posted).toHaveLength(1));
|
||||
const { data } = worker.posted[0];
|
||||
worker.onmessage?.({ data: { id: data.id, ok: true, result: pdfResult() } });
|
||||
|
||||
await expect(retry).resolves.toEqual({ markdown: '# Title\n\nBody text.\n' });
|
||||
});
|
||||
});
|
||||
|
|
@ -1,74 +1,81 @@
|
|||
import { createPipeline, transformers } from "pdf-to-markdown-core/lib/src";
|
||||
import { PARSE_SCHEMA } from "pdf-to-markdown-core/lib/src/PdfParser";
|
||||
import type { PdfProcessResult } from '@firecrawl/pdf-inspector-wasm';
|
||||
import type { PdfRequest, PdfResponse } from '../workers/pdf-inspector.worker';
|
||||
|
||||
// pdfjs-dist v5+ requires browser APIs at import time, so we use dynamic imports
|
||||
let pdfjs: typeof import("pdfjs-dist") | null = null;
|
||||
export interface PdfConversion {
|
||||
markdown: string;
|
||||
warning?: string;
|
||||
}
|
||||
|
||||
// Maps the raw worker result to UI behavior. Throws when there is no usable text.
|
||||
export function interpretPdfResult(result: PdfProcessResult, fileName: string): PdfConversion {
|
||||
if (result.pdfType === 'Scanned' || result.pdfType === 'ImageBased') {
|
||||
throw new Error(
|
||||
`${fileName} contains no machine-readable text. OCR is necessary, and the Fabric web interface does not include OCR.`
|
||||
);
|
||||
}
|
||||
const markdown = result.markdown;
|
||||
if (!markdown || markdown.trim().length === 0) {
|
||||
throw new Error(`${fileName}: the conversion returned no text.`);
|
||||
}
|
||||
const warnings: string[] = [];
|
||||
if (result.pdfType === 'Mixed') {
|
||||
warnings.push(
|
||||
`${fileName}: pages ${result.pagesNeedingOcr.join(', ')} contain no machine-readable text. OCR is necessary for those pages, and their content is not included.`
|
||||
);
|
||||
}
|
||||
if (result.hasEncodingIssues) {
|
||||
warnings.push(`Some text in ${fileName} did not decode correctly.`);
|
||||
}
|
||||
return warnings.length > 0 ? { markdown, warning: warnings.join(' ') } : { markdown };
|
||||
}
|
||||
|
||||
export class PdfConversionService {
|
||||
private async ensureInitialized(): Promise<typeof import("pdfjs-dist")> {
|
||||
if (!pdfjs) {
|
||||
// Dynamic import to avoid SSR issues with pdfjs-dist v5+
|
||||
pdfjs = await import("pdfjs-dist");
|
||||
const pdfConfig = (await import("./pdf-config")).default;
|
||||
console.log("PDF.js version:", pdfjs.version);
|
||||
await pdfConfig.initialize();
|
||||
console.log("Worker configuration complete");
|
||||
}
|
||||
return pdfjs;
|
||||
private worker: Worker | null = null;
|
||||
private nextId = 1;
|
||||
private pending = new Map<
|
||||
number,
|
||||
{ resolve: (result: PdfProcessResult) => void; reject: (error: Error) => void }
|
||||
>();
|
||||
|
||||
async convertToMarkdown(file: File): Promise<PdfConversion> {
|
||||
const buffer = await file.arrayBuffer();
|
||||
const worker = this.getWorker();
|
||||
const id = this.nextId++;
|
||||
const result = await new Promise<PdfProcessResult>((resolve, reject) => {
|
||||
this.pending.set(id, { resolve, reject });
|
||||
worker.postMessage({ id, buffer } satisfies PdfRequest, [buffer]);
|
||||
});
|
||||
return interpretPdfResult(result, file.name);
|
||||
}
|
||||
|
||||
async convertToMarkdown(file: File): Promise<string> {
|
||||
console.log("Starting PDF conversion:", {
|
||||
fileName: file.name,
|
||||
fileSize: file.size,
|
||||
// Lazy creation delays the WASM download until the first PDF attachment.
|
||||
private getWorker(): Worker {
|
||||
if (this.worker) return this.worker;
|
||||
const worker = new Worker(new URL('../workers/pdf-inspector.worker.ts', import.meta.url), {
|
||||
type: 'module'
|
||||
});
|
||||
|
||||
const pdfjsLib = await this.ensureInitialized();
|
||||
|
||||
const buffer = await file.arrayBuffer();
|
||||
console.log("Buffer created:", buffer.byteLength);
|
||||
|
||||
const pipeline = createPipeline(pdfjsLib, {
|
||||
transformConfig: {
|
||||
transformers,
|
||||
},
|
||||
});
|
||||
console.log("Pipeline created");
|
||||
|
||||
const result = await pipeline.parse(buffer, (progress) =>
|
||||
console.log("Processing:", {
|
||||
stage: progress.stages,
|
||||
details: progress.stageDetails,
|
||||
progress: progress.stageProgress,
|
||||
}),
|
||||
);
|
||||
console.log("Parse complete, validating result");
|
||||
|
||||
const transformed = result.transform();
|
||||
console.log("Transform applied:", transformed);
|
||||
|
||||
const markdown = transformed.convert({
|
||||
convert: (items) => {
|
||||
console.log("PDF Structure:", {
|
||||
itemCount: items.length,
|
||||
firstItem: items[0],
|
||||
schema: PARSE_SCHEMA, // ['transform', 'width', 'height', 'str', 'fontName', 'dir']
|
||||
});
|
||||
|
||||
const text = items
|
||||
.map((item) => item.value("str")) // Using 'str' instead of 'text' based on PARSE_SCHEMA
|
||||
.filter(Boolean)
|
||||
.join("\n");
|
||||
|
||||
console.log("Converted text:", {
|
||||
length: text.length,
|
||||
preview: text.substring(0, 100),
|
||||
});
|
||||
|
||||
return text;
|
||||
},
|
||||
});
|
||||
|
||||
return markdown;
|
||||
worker.onmessage = (event: MessageEvent<PdfResponse>) => {
|
||||
const response = event.data;
|
||||
const entry = this.pending.get(response.id);
|
||||
if (!entry) return;
|
||||
this.pending.delete(response.id);
|
||||
if (response.ok) {
|
||||
entry.resolve(response.result);
|
||||
} else {
|
||||
entry.reject(new Error(response.error));
|
||||
}
|
||||
};
|
||||
// Fires only when the worker script itself does not load. The handler
|
||||
// terminates its own worker, so a later replacement worker is safe.
|
||||
worker.onerror = () => {
|
||||
for (const { reject } of this.pending.values()) {
|
||||
reject(new Error('The PDF worker failed.'));
|
||||
}
|
||||
this.pending.clear();
|
||||
worker.terminate();
|
||||
this.worker = null;
|
||||
};
|
||||
this.worker = worker;
|
||||
return worker;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
import { browser } from "$app/environment";
|
||||
|
||||
// Export the configuration - accepts pdfjs module to avoid top-level import
|
||||
// This is necessary because pdfjs-dist v5+ uses browser APIs at import time
|
||||
export default {
|
||||
initialize: async () => {
|
||||
if (browser) {
|
||||
// Dynamic import to avoid SSR issues
|
||||
const pdfjs = await import("pdfjs-dist");
|
||||
const { GlobalWorkerOptions, version } = pdfjs;
|
||||
|
||||
// Use CDN-hosted worker to avoid bundling third-party minified code in the repo
|
||||
const workerSrc = `https://unpkg.com/pdfjs-dist@${version}/build/pdf.worker.min.mjs`;
|
||||
GlobalWorkerOptions.workerSrc = workerSrc;
|
||||
|
||||
console.log(`PDF.js worker v${version} initialized from CDN`);
|
||||
}
|
||||
},
|
||||
};
|
||||
29
web/src/lib/workers/pdf-inspector.worker.ts
Normal file
29
web/src/lib/workers/pdf-inspector.worker.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import init, { processPdf, type PdfProcessResult } from '@firecrawl/pdf-inspector-wasm';
|
||||
|
||||
// Request, service → worker. The buffer moves through the transfer list.
|
||||
export interface PdfRequest {
|
||||
id: number;
|
||||
buffer: ArrayBuffer;
|
||||
}
|
||||
|
||||
// Response, worker → service.
|
||||
export type PdfResponse =
|
||||
{ id: number; ok: true; result: PdfProcessResult } | { id: number; ok: false; error: string };
|
||||
|
||||
// init() runs one time, at module evaluation. Each message handler awaits
|
||||
// the same promise. An initialization failure becomes an error response.
|
||||
const ready = init();
|
||||
|
||||
self.onmessage = async (event: MessageEvent<PdfRequest>) => {
|
||||
const { id, buffer } = event.data;
|
||||
try {
|
||||
await ready;
|
||||
const result = processPdf(new Uint8Array(buffer), {
|
||||
profile: 'compact',
|
||||
includePageMarkers: true
|
||||
});
|
||||
self.postMessage({ id, ok: true, result } satisfies PdfResponse);
|
||||
} catch (err) {
|
||||
self.postMessage({ id, ok: false, error: String(err) } satisfies PdfResponse);
|
||||
}
|
||||
};
|
||||
|
|
@ -17,13 +17,6 @@ export default defineConfig({
|
|||
// Tailwind 4 gives a Vite plugin, which replaces the PostCSS setup. It must
|
||||
// come before sveltekit() so that it can process the styles in components.
|
||||
plugins: [tailwindcss(), sveltekit()],
|
||||
optimizeDeps: {
|
||||
// Vite 8 optimizes dependencies with Rolldown, which supports top-level
|
||||
// await and modern syntax. The `esbuildOptions` block that was here set
|
||||
// `target` and `supported`, and Vite 8 reads neither key. Rolldown needs no
|
||||
// replacement option. `build.target` below keeps the setting for the build.
|
||||
include: ['pdfjs-dist']
|
||||
},
|
||||
define: {
|
||||
'process.env': {
|
||||
NODE_ENV: JSON.stringify(process.env.NODE_ENV)
|
||||
|
|
|
|||
Loading…
Reference in a new issue