mirror of
				https://github.com/Frooodle/Stirling-PDF.git
				synced 2025-10-25 11:17:28 +02:00 
			
		
		
		
	Adding LibreOffice conversion support (WIP)
This commit is contained in:
		
							parent
							
								
									c7dd18695d
								
							
						
					
					
						commit
						e625a415fd
					
				
							
								
								
									
										150
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										150
									
								
								README.md
									
									
									
									
									
								
							| @ -88,66 +88,136 @@ If you are interested in learning about this, take a look at the Example workflo | |||||||
| 
 | 
 | ||||||
| Current functions of spdf and their progress in this repo. | Current functions of spdf and their progress in this repo. | ||||||
| 
 | 
 | ||||||
|  | #### Page Operations | ||||||
| | Status | Feature                  | Description | | | Status | Feature                  | Description | | ||||||
| | ------ | ------------------------ | ----------- | | | ------ | ------------------------ | ----------- | | ||||||
| | ✔️   | Merge                    |             | | | 🚧A    | Merge                    |             | | ||||||
| | ✔️   | Split                    |             | | | 🚧A    | Split                    |             | | ||||||
| | ✔️   | Rotate                   |             | | | 🚧A    | Organize                 |             | | ||||||
| | ✔️   | Multi-Page-Layout        |             | | | 🚧S    | Rotate                   |             | | ||||||
| | ✔️   | Adjust page size/scale   |             | | | 🚧A    | Remove Pages             |             | | ||||||
| | ✔️   | Organize                 |             | | | 🚧A    | Multi-Page Layout        |             | | ||||||
| | ✔️   | Change Metadata          |             | | | ❌     | Adjust page size/scale   |             | | ||||||
| | ✔️   | Auto Rename              |             | | | 🚧A    | Auto Split Pages         |             | | ||||||
| | ❌     | Add Watermark            |             | | | ❌     | Adjust Colours/Contrast  |             | | ||||||
|  | | ❌     | Crop                     |             | | ||||||
|  | | 🚧A    | Extract Pages            |             | | ||||||
| | ❌     | PDF to Single large Page |             | | | ❌     | PDF to Single large Page |             | | ||||||
| | ❌     | Auto Redact              |             | |  | ||||||
| | ❌     | Remove Pages             |             | |  | ||||||
| 
 | 
 | ||||||
| | Status | Feature            | Description | |  | ||||||
| | ------ | ------------------ | ----------- | |  | ||||||
| | ✔️    | Remove Blank Pages |             | |  | ||||||
| | ✔️    | Auto Split Pages   |             | |  | ||||||
| 
 | 
 | ||||||
| | Status | Feature      | Description | | #### Convert | ||||||
| | ------ | ------------ | ----------- | | | Status | Feature             | Description | | ||||||
| | ❌     | Repair       |             | | | ------ | ------------------- | ----------- | | ||||||
| | ❌     | Compress     |             | | | ❌     | Image to PDF        |             | | ||||||
| | ❌     | Flatten      |             | | | 🚧S    | Convert file to PDF |             | | ||||||
| | ❌     | Compare/Diff |             | | | ❌     | URL to PDF          |             | | ||||||
| | ❌     | Sanitize     |             | | | ❌     | HTML to PDF         |             | | ||||||
| | ❌     | Get info     |             | | | ❌     | Markdown to PDF     |             | | ||||||
| | ❌     | Show JS      |             | | | ❌     | PDF to Image        |             | | ||||||
|  | | ❌     | PDF to Word         |             | | ||||||
|  | | ❌     | PDF to Presentation |             | | ||||||
|  | | ❌     | PDF to Text/RTF     |             | | ||||||
|  | | ❌     | PDF to HTML         |             | | ||||||
|  | | ❌     | PDF to PDF/A        |             | | ||||||
| 
 | 
 | ||||||
|  | #### Security | ||||||
| | Status | Feature               | Description | | | Status | Feature               | Description | | ||||||
| | ------ | --------------------- | ----------- | | | ------ | --------------------- | ----------- | | ||||||
| | ❌     | Sign                  |             | |  | ||||||
| | ❌     | Sign with Certificate |             | |  | ||||||
| | ❌     | Add Password          |             | | | ❌     | Add Password          |             | | ||||||
| | ❌     | Remove Password       |             | | | ❌     | Remove Password       |             | | ||||||
| | ❌     | Change Permissions    |             | | | ❌     | Change Permissions    |             | | ||||||
|  | | ❌     | Add Watermark         |             | | ||||||
|  | | ❌     | Sign with Certificate |             | | ||||||
|  | | ❌     | Sanitize              |             | | ||||||
|  | | ❌     | Auto Redact           |             | | ||||||
| 
 | 
 | ||||||
|  | #### Miscellaneous | ||||||
| | Status | Feature                     | Description | | | Status | Feature                     | Description | | ||||||
| | ------ | --------------------------- | ----------- | | | ------ | --------------------------- | ----------- | | ||||||
| | ❌     | Image to PDF                |             | |  | ||||||
| | ❌     | Add image                   |             | |  | ||||||
| | ❌     | Extract Images              |             | |  | ||||||
| | ❌     | PDF to Image                |             | |  | ||||||
| | ❌     | OCR                         |             | | | ❌     | OCR                         |             | | ||||||
| | ❌     | Detect/Split Scanned photos |             | | | ❌     | Add image                   |             | | ||||||
|  | | ❌     | Compress                    |             | | ||||||
|  | | ❌     | Extract Images              |             | | ||||||
|  | | 🚧S    | Change Metadata             |             | | ||||||
|  | | 🚧A    | Detect/Split Scanned photos |             | | ||||||
|  | | ❌     | Sign                        |             | | ||||||
|  | | ❌     | Flatten                     |             | | ||||||
|  | | ❌     | Repair                      |             | | ||||||
|  | | 🚧A    | Remove Blank Pages          |             | | ||||||
|  | | ❌     | Compare/Diff                |             | | ||||||
|  | | ❌     | Add Page Numbers            |             | | ||||||
|  | | ❌     | Auto Rename                 |             | | ||||||
|  | | ❌     | Get info                    |             | | ||||||
|  | | ❌     | Show JS                     |             | | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| | Status | Feature                  | Description | |  | ||||||
| | ------ | ------------------------ | ----------- | |  | ||||||
| | ❌     | Convert file to PDF      |             | |  | ||||||
| | ❌     | PDF to Text/RTF          |             | |  | ||||||
| | ❌     | PDF to HTML              |             | |  | ||||||
| | ❌     | PDF to XML               |             | |  | ||||||
| | ❌     | URL to PDF               |             | |  | ||||||
| | ❌     | HTML to PDF              |             | |  | ||||||
| | ❌     | Markdown to PDF          |             | |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| ✔️: Done, 🚧: Started Developement, ❌: Planned Feature | ✔️: Done, 🚧: Started Developement, ❌: Planned Feature | ||||||
|  | A: Available in the internal API, S: Available on the node server, C: Available in the client | ||||||
| 
 | 
 | ||||||
| ## Contribute | ## Contribute | ||||||
| 
 | 
 | ||||||
| For initial instructions look at [CONTRIBUTE.md](./CONTRIBUTE.md) | For initial instructions look at [CONTRIBUTE.md](./CONTRIBUTE.md) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /* | ||||||
|  | ///// CONVERT 2 pdf | ||||||
|  | file2pdf | ||||||
|  | url2pdf | ||||||
|  | html2pdf | ||||||
|  | md2pdf | ||||||
|  | image2pdf | ||||||
|  | 
 | ||||||
|  | ///// CONVERT from pdf | ||||||
|  | pdf2image | ||||||
|  | flatten | ||||||
|  | pdf2pdf/a | ||||||
|  | pdf2word | ||||||
|  | pdf2presentation | ||||||
|  | pdf2rtf | ||||||
|  | pdf2html | ||||||
|  | pdf2xml | ||||||
|  | 
 | ||||||
|  | ///// SINGLE | ||||||
|  | merge | ||||||
|  | rotate | ||||||
|  | crop | ||||||
|  | pageNumbers | ||||||
|  | colours/contrast | ||||||
|  | addPassword | ||||||
|  | removePassword | ||||||
|  | compress | ||||||
|  | changeMetadata | ||||||
|  | change Permissions | ||||||
|  | OCR | ||||||
|  | sanitise | ||||||
|  | repair | ||||||
|  | compare | ||||||
|  | extract images | ||||||
|  | signWith certificate | ||||||
|  | impose | ||||||
|  | adjust page size/scale | ||||||
|  | auto rename | ||||||
|  | getAllInfo | ||||||
|  | showJS | ||||||
|  | redact | ||||||
|  | pdf2singleLargePage | ||||||
|  | 
 | ||||||
|  | ///// SPLITTING | ||||||
|  | split | ||||||
|  | auto split | ||||||
|  | detect/split scanned | ||||||
|  | 
 | ||||||
|  | ///// REARRANGE | ||||||
|  | - organise pages (remove/re-arrange) | ||||||
|  | - removePages | ||||||
|  | - removeBlank | ||||||
|  | - extractPages | ||||||
|  | 
 | ||||||
|  | ///// ADD OBJECTS | ||||||
|  | add image | ||||||
|  | add watermark | ||||||
|  | sign | ||||||
|  | */ | ||||||
| @ -17,13 +17,15 @@ | |||||||
|     "i18next": "^23.6.0", |     "i18next": "^23.6.0", | ||||||
|     "i18next-browser-languagedetector": "^7.1.0", |     "i18next-browser-languagedetector": "^7.1.0", | ||||||
|     "path-browserify": "^1.0.1", |     "path-browserify": "^1.0.1", | ||||||
|  |     "pdfjs-dist": "^4.0.189", | ||||||
|     "react": "^18.2.0", |     "react": "^18.2.0", | ||||||
|     "react-bootstrap": "^2.9.1", |     "react-bootstrap": "^2.9.1", | ||||||
|     "react-dom": "^18.2.0", |     "react-dom": "^18.2.0", | ||||||
|     "react-i18next": "^13.3.1", |     "react-i18next": "^13.3.1", | ||||||
|     "react-icons": "^4.11.0", |     "react-icons": "^4.11.0", | ||||||
|     "react-router-bootstrap": "^0.26.2", |     "react-router-bootstrap": "^0.26.2", | ||||||
|     "react-router-dom": "^6.18.0" |     "react-router-dom": "^6.18.0", | ||||||
|  |     "vite-plugin-top-level-await": "^1.3.1" | ||||||
|   }, |   }, | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|     "@tauri-apps/cli": "^1.5.0", |     "@tauri-apps/cli": "^1.5.0", | ||||||
|  | |||||||
							
								
								
									
										22
									
								
								client-tauri/src-tauri/Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										22
									
								
								client-tauri/src-tauri/Cargo.lock
									
									
									
										generated
									
									
									
								
							| @ -1711,6 +1711,16 @@ dependencies = [ | |||||||
|  "windows-sys 0.42.0", |  "windows-sys 0.42.0", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "os_pipe" | ||||||
|  | version = "1.1.4" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "0ae859aa07428ca9a929b936690f8b12dc5f11dd8c6992a18ca93919f28bc177" | ||||||
|  | dependencies = [ | ||||||
|  |  "libc", | ||||||
|  |  "windows-sys 0.48.0", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "overload" | name = "overload" | ||||||
| version = "0.1.1" | version = "0.1.1" | ||||||
| @ -2436,6 +2446,16 @@ dependencies = [ | |||||||
|  "lazy_static", |  "lazy_static", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "shared_child" | ||||||
|  | version = "1.0.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "b0d94659ad3c2137fef23ae75b03d5241d633f8acded53d672decfa0e6e0caef" | ||||||
|  | dependencies = [ | ||||||
|  |  "libc", | ||||||
|  |  "winapi", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "simd-adler32" | name = "simd-adler32" | ||||||
| version = "0.3.7" | version = "0.3.7" | ||||||
| @ -2693,6 +2713,7 @@ dependencies = [ | |||||||
|  "objc", |  "objc", | ||||||
|  "once_cell", |  "once_cell", | ||||||
|  "open", |  "open", | ||||||
|  |  "os_pipe", | ||||||
|  "percent-encoding", |  "percent-encoding", | ||||||
|  "rand 0.8.5", |  "rand 0.8.5", | ||||||
|  "raw-window-handle", |  "raw-window-handle", | ||||||
| @ -2703,6 +2724,7 @@ dependencies = [ | |||||||
|  "serde_json", |  "serde_json", | ||||||
|  "serde_repr", |  "serde_repr", | ||||||
|  "serialize-to-javascript", |  "serialize-to-javascript", | ||||||
|  |  "shared_child", | ||||||
|  "state", |  "state", | ||||||
|  "tar", |  "tar", | ||||||
|  "tauri-macros", |  "tauri-macros", | ||||||
|  | |||||||
| @ -13,7 +13,7 @@ edition = "2021" | |||||||
| tauri-build = { version = "1.5", features = [] } | tauri-build = { version = "1.5", features = [] } | ||||||
| 
 | 
 | ||||||
| [dependencies] | [dependencies] | ||||||
| tauri = { version = "1.5", features = [ "fs-write-file", "fs-read-file", "dialog-save", "dialog-open", "shell-open"] } | tauri = { version = "1.5", features = [ "fs-remove-dir", "fs-create-dir", "shell-all", "fs-write-file", "fs-read-file", "dialog-save", "dialog-open"] } | ||||||
| serde = { version = "1.0", features = ["derive"] } | serde = { version = "1.0", features = ["derive"] } | ||||||
| serde_json = "1.0" | serde_json = "1.0" | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -14,8 +14,19 @@ | |||||||
|     "allowlist": { |     "allowlist": { | ||||||
|       "all": false, |       "all": false, | ||||||
|       "shell": { |       "shell": { | ||||||
|         "all": false, |         "all": true, | ||||||
|         "open": true |         "open": true, | ||||||
|  |         "scope": [ | ||||||
|  |           { | ||||||
|  |             "name": "libreoffice-version", | ||||||
|  |             "cmd": "libreoffice", | ||||||
|  |             "args": ["--version"] | ||||||
|  |           },{ | ||||||
|  |             "name": "libreoffice-convert", | ||||||
|  |             "cmd": "libreoffice", | ||||||
|  |             "args": ["--headless","--convert-to",{ "validator": "\\S+" },{ "validator": "\\S+" },"--outdir",{ "validator": "\\S+" }] | ||||||
|  |           } | ||||||
|  |         ] | ||||||
|       }, |       }, | ||||||
|       "dialog": { |       "dialog": { | ||||||
|         "all": false, |         "all": false, | ||||||
| @ -31,8 +42,8 @@ | |||||||
|         "writeFile": true, |         "writeFile": true, | ||||||
|         "readDir": false, |         "readDir": false, | ||||||
|         "copyFile": false, |         "copyFile": false, | ||||||
|         "createDir": false, |         "createDir": true, | ||||||
|         "removeDir": false, |         "removeDir": true, | ||||||
|         "removeFile": false, |         "removeFile": false, | ||||||
|         "renameFile": false, |         "renameFile": false, | ||||||
|         "exists": false |         "exists": false | ||||||
|  | |||||||
| @ -4,6 +4,7 @@ import { Routes, Route, Outlet } from "react-router-dom"; | |||||||
| import Home from "./pages/Home"; | import Home from "./pages/Home"; | ||||||
| import About from "./pages/About"; | import About from "./pages/About"; | ||||||
| import Dashboard from "./pages/Dashboard"; | import Dashboard from "./pages/Dashboard"; | ||||||
|  | import ToPdf from "./pages/convert/ToPdf" | ||||||
| import NoMatch from "./pages/NoMatch"; | import NoMatch from "./pages/NoMatch"; | ||||||
| import NavBar from "./components/NavBar"; | import NavBar from "./components/NavBar"; | ||||||
| 
 | 
 | ||||||
| @ -38,6 +39,7 @@ export default function App() { | |||||||
|           <Route index element={<Home />} /> |           <Route index element={<Home />} /> | ||||||
|           <Route path="about" element={<About />} /> |           <Route path="about" element={<About />} /> | ||||||
|           <Route path="dashboard" element={<Dashboard />} /> |           <Route path="dashboard" element={<Dashboard />} /> | ||||||
|  |           <Route path="to-pdf" element={<ToPdf />} /> | ||||||
| 
 | 
 | ||||||
|           {/* Using path="*"" means "match anything", so this route |           {/* Using path="*"" means "match anything", so this route | ||||||
|                 acts like a catch-all for URLs that we don't have explicit |                 acts like a catch-all for URLs that we don't have explicit | ||||||
|  | |||||||
| @ -86,7 +86,7 @@ function NavBar() { | |||||||
|     ]}, |     ]}, | ||||||
|     {displayText: t('navbar.convert'), icon: BsArrowLeftRight, sublist: [ |     {displayText: t('navbar.convert'), icon: BsArrowLeftRight, sublist: [ | ||||||
|       { displayText: t('home.imageToPdf.title'), icon: BsFileEarmarkImage, dest: "/dashboard", tooltip: t('home.imageToPdf.desc') }, |       { displayText: t('home.imageToPdf.title'), icon: BsFileEarmarkImage, dest: "/dashboard", tooltip: t('home.imageToPdf.desc') }, | ||||||
|       { displayText: t('home.fileToPDF.title'), icon: BsFileEarmark, dest: "/nothing-here", tooltip: t('home.fileToPDF.desc') }, |       { displayText: t('home.fileToPDF.title'), icon: BsFileEarmark, dest: "/to-pdf", tooltip: t('home.fileToPDF.desc') }, | ||||||
|       { displayText: t('home.HTMLToPDF.title'), icon: BsFiletypeHtml, dest: "/nothing-here", tooltip: t('home.HTMLToPDF.desc') }, |       { displayText: t('home.HTMLToPDF.title'), icon: BsFiletypeHtml, dest: "/nothing-here", tooltip: t('home.HTMLToPDF.desc') }, | ||||||
|       { displayText: t('home.URLToPDF.title'), icon: BsLink, dest: "/nothing-here", tooltip: t('home.URLToPDF.desc') }, |       { displayText: t('home.URLToPDF.title'), icon: BsLink, dest: "/nothing-here", tooltip: t('home.URLToPDF.desc') }, | ||||||
|       { displayText: t('home.MarkdownToPDF.title'), icon: BsFiletypeMd, dest: "/nothing-here", tooltip: t('home.MarkdownToPDF.desc') }, |       { displayText: t('home.MarkdownToPDF.title'), icon: BsFiletypeMd, dest: "/nothing-here", tooltip: t('home.MarkdownToPDF.desc') }, | ||||||
|  | |||||||
							
								
								
									
										16
									
								
								client-tauri/src/pages/convert/ToPdf.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								client-tauri/src/pages/convert/ToPdf.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | |||||||
|  | 
 | ||||||
|  | import { isLibreOfficeInstalled } from "../../utils/libre-office-utils"; | ||||||
|  | 
 | ||||||
|  | const hasLibreOffice = await isLibreOfficeInstalled(); | ||||||
|  | console.log(hasLibreOffice) | ||||||
|  | 
 | ||||||
|  | function About() { | ||||||
|  |     return ( | ||||||
|  |         <div> | ||||||
|  |             <h2>Convert to PDF</h2> | ||||||
|  |             {"hasLibreOffice: "+hasLibreOffice} | ||||||
|  |         </div> | ||||||
|  |     ); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export default About; | ||||||
							
								
								
									
										46
									
								
								client-tauri/src/utils/libre-office-utils.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								client-tauri/src/utils/libre-office-utils.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,46 @@ | |||||||
|  | 
 | ||||||
|  | import { readBinaryFile, writeBinaryFile, removeDir, BaseDirectory } from '@tauri-apps/api/fs'; | ||||||
|  | import { PdfFile, fromUint8Array } from '@stirling-pdf/shared-operations/wrappers/PdfFile' | ||||||
|  | import { runShell } from './tauri-wrapper'; | ||||||
|  | 
 | ||||||
|  | export async function fileToPdf(byteArray: Uint8Array, filename: string): Promise<PdfFile> { | ||||||
|  |     const randUuid = crypto.randomUUID(); | ||||||
|  |     const tempDir = "StirlingPDF/"+randUuid; | ||||||
|  |     const srcFile = tempDir+"/"+filename; | ||||||
|  | 
 | ||||||
|  |     await writeBinaryFile(srcFile, byteArray); | ||||||
|  |     await writeBinaryFile(srcFile, new Uint8Array([]), { dir: BaseDirectory.Temp }); | ||||||
|  | 
 | ||||||
|  |     const messageList: string[] = []; | ||||||
|  |     await runShell("libreoffice-convert", ["--headless","--convert-to","pdf",srcFile,"--outdir",tempDir], (message, stream) => { | ||||||
|  |         if (stream === "stdout") { | ||||||
|  |             messageList.push(message); | ||||||
|  |         } | ||||||
|  |         console.debug(`${stream}, ${randUuid}: ${message}`); | ||||||
|  |     }); | ||||||
|  |     const lastMessage = messageList[messageList.length-1] | ||||||
|  |     const outputFilePath = lastMessage.split(" -> ")[1].split(".pdf")[0]+".pdf"; | ||||||
|  |     const outputFilePathSplit = outputFilePath.toString().split("[\\/]") | ||||||
|  |     const outputFileName = outputFilePathSplit[outputFilePathSplit.length-1]; | ||||||
|  |     const outputBytes = await readBinaryFile(outputFilePath); | ||||||
|  | 
 | ||||||
|  |     await removeDir(tempDir); | ||||||
|  | 
 | ||||||
|  |     return fromUint8Array(outputBytes, outputFileName); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export async function isLibreOfficeInstalled() { | ||||||
|  |     const messageList: string[] = []; | ||||||
|  |     try { | ||||||
|  |         await runShell("libreoffice-version", ["--version"], (message, stream) => { | ||||||
|  |             if (stream === "stdout") { | ||||||
|  |                 messageList.push(message); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     } catch (error) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |     console.log("messageList", messageList) | ||||||
|  |     const result = messageList[0].match("LibreOffice ([0-9]+\.){4}.*"); | ||||||
|  |     return result ? true : false; | ||||||
|  | } | ||||||
| @ -1,6 +1,7 @@ | |||||||
| 
 | 
 | ||||||
| import { open, save } from '@tauri-apps/api/dialog'; | import { open, save } from '@tauri-apps/api/dialog'; | ||||||
| import { readBinaryFile, writeBinaryFile } from '@tauri-apps/api/fs'; | import { readBinaryFile, writeBinaryFile } from '@tauri-apps/api/fs'; | ||||||
|  | import { Command } from '@tauri-apps/api/shell' | ||||||
| 
 | 
 | ||||||
| export type TauriBrowserFile = { | export type TauriBrowserFile = { | ||||||
|     name: string, |     name: string, | ||||||
| @ -52,7 +53,7 @@ export function openFiles(options: SelectFilesDialogOptions): Promise<TauriBrows | |||||||
|                 selected = [selected]; |                 selected = [selected]; | ||||||
|             } |             } | ||||||
|          |          | ||||||
|             const files:TauriBrowserFile[] = []; |             const files: TauriBrowserFile[] = []; | ||||||
|             for (const s of selected) { |             for (const s of selected) { | ||||||
|                 const contents = await readBinaryFile(s); |                 const contents = await readBinaryFile(s); | ||||||
|                 const res = byteArrayToFile(contents, s); |                 const res = byteArrayToFile(contents, s); | ||||||
| @ -73,7 +74,7 @@ export function openFiles(options: SelectFilesDialogOptions): Promise<TauriBrows | |||||||
|             input.onchange = async () => { |             input.onchange = async () => { | ||||||
|                 if (input.files && input.files.length) {  |                 if (input.files && input.files.length) {  | ||||||
|                     console.log("input.files", input.files) |                     console.log("input.files", input.files) | ||||||
|                     const files:TauriBrowserFile[] = []; |                     const files: TauriBrowserFile[] = []; | ||||||
|                     for (const f of input.files) { |                     for (const f of input.files) { | ||||||
|                         const contents = new Uint8Array(await f.arrayBuffer()); |                         const contents = new Uint8Array(await f.arrayBuffer()); | ||||||
|                         const res = byteArrayToFile(contents, f.name); |                         const res = byteArrayToFile(contents, f.name); | ||||||
| @ -139,3 +140,30 @@ export async function downloadFile(fileData: Uint8Array, options: DownloadFilesD | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Dont forget to whitelist the Command in src-tauri/tauri.conf.json! (tauri.allowlist.shell.scope) | ||||||
|  |  * @param commandName The name of the command to run. Must be defined in tauri.allowlist.shell.scope[].name | ||||||
|  |  * @param args The args to pass into the command | ||||||
|  |  * @param callback A callback function that is called when output is logged | ||||||
|  |  * @returns A log of all the outputs logged | ||||||
|  |  */ | ||||||
|  | export function runShell(commandName: string, args: string[], callback: (message: any, stream:"stdout"|"stderr"|"error") => void): Promise<void> { | ||||||
|  |     return new Promise(async (resolve, reject) => { | ||||||
|  | 
 | ||||||
|  |         const comm = new Command(commandName, args); | ||||||
|  |         comm.on('close', data => { | ||||||
|  |             if (data.code === 0) { | ||||||
|  |                 resolve(); | ||||||
|  |             } else { | ||||||
|  |                 reject(new Error(`Command failed with exit code ${data.code} and signal ${data.signal}`)); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |         comm.on('error', error => callback(error, "error")); | ||||||
|  |         comm.stdout.on('data', line => callback(line, "stdout")); | ||||||
|  |         comm.stderr.on('data', line => callback(line, "stderr")); | ||||||
|  | 
 | ||||||
|  |         const child = await comm.spawn(); | ||||||
|  |         console.debug(`Started child process with pid: ${child.pid}`) | ||||||
|  |     }); | ||||||
|  | } | ||||||
|  | |||||||
| @ -1,9 +1,18 @@ | |||||||
| import { defineConfig } from "vite"; | import { defineConfig } from "vite"; | ||||||
| import react from "@vitejs/plugin-react"; | import react from "@vitejs/plugin-react"; | ||||||
|  | import topLevelAwait from "vite-plugin-top-level-await"; | ||||||
| 
 | 
 | ||||||
| // https://vitejs.dev/config/
 | // https://vitejs.dev/config/
 | ||||||
| export default defineConfig(async () => ({ | export default defineConfig(async () => ({ | ||||||
|   plugins: [react()], |   plugins: [ | ||||||
|  |     react(), | ||||||
|  |     topLevelAwait({ | ||||||
|  |       // The export name of top-level await promise for each chunk module
 | ||||||
|  |       promiseExportName: "__tla", | ||||||
|  |       // The function to generate import names of top-level await promise in each chunk module
 | ||||||
|  |       promiseImportName: i => `__tla_${i}` | ||||||
|  |     }), | ||||||
|  |   ], | ||||||
| 
 | 
 | ||||||
|   // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
 |   // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
 | ||||||
|   //
 |   //
 | ||||||
|  | |||||||
							
								
								
									
										299
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										299
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -74,13 +74,15 @@ | |||||||
|         "i18next": "^23.6.0", |         "i18next": "^23.6.0", | ||||||
|         "i18next-browser-languagedetector": "^7.1.0", |         "i18next-browser-languagedetector": "^7.1.0", | ||||||
|         "path-browserify": "^1.0.1", |         "path-browserify": "^1.0.1", | ||||||
|  |         "pdfjs-dist": "^4.0.189", | ||||||
|         "react": "^18.2.0", |         "react": "^18.2.0", | ||||||
|         "react-bootstrap": "^2.9.1", |         "react-bootstrap": "^2.9.1", | ||||||
|         "react-dom": "^18.2.0", |         "react-dom": "^18.2.0", | ||||||
|         "react-i18next": "^13.3.1", |         "react-i18next": "^13.3.1", | ||||||
|         "react-icons": "^4.11.0", |         "react-icons": "^4.11.0", | ||||||
|         "react-router-bootstrap": "^0.26.2", |         "react-router-bootstrap": "^0.26.2", | ||||||
|         "react-router-dom": "^6.18.0" |         "react-router-dom": "^6.18.0", | ||||||
|  |         "vite-plugin-top-level-await": "^1.3.1" | ||||||
|       }, |       }, | ||||||
|       "devDependencies": { |       "devDependencies": { | ||||||
|         "@tauri-apps/cli": "^1.5.0", |         "@tauri-apps/cli": "^1.5.0", | ||||||
| @ -2579,7 +2581,6 @@ | |||||||
|       "cpu": [ |       "cpu": [ | ||||||
|         "arm" |         "arm" | ||||||
|       ], |       ], | ||||||
|       "dev": true, |  | ||||||
|       "optional": true, |       "optional": true, | ||||||
|       "os": [ |       "os": [ | ||||||
|         "android" |         "android" | ||||||
| @ -2595,7 +2596,6 @@ | |||||||
|       "cpu": [ |       "cpu": [ | ||||||
|         "arm64" |         "arm64" | ||||||
|       ], |       ], | ||||||
|       "dev": true, |  | ||||||
|       "optional": true, |       "optional": true, | ||||||
|       "os": [ |       "os": [ | ||||||
|         "android" |         "android" | ||||||
| @ -2611,7 +2611,6 @@ | |||||||
|       "cpu": [ |       "cpu": [ | ||||||
|         "x64" |         "x64" | ||||||
|       ], |       ], | ||||||
|       "dev": true, |  | ||||||
|       "optional": true, |       "optional": true, | ||||||
|       "os": [ |       "os": [ | ||||||
|         "android" |         "android" | ||||||
| @ -2627,7 +2626,6 @@ | |||||||
|       "cpu": [ |       "cpu": [ | ||||||
|         "arm64" |         "arm64" | ||||||
|       ], |       ], | ||||||
|       "dev": true, |  | ||||||
|       "optional": true, |       "optional": true, | ||||||
|       "os": [ |       "os": [ | ||||||
|         "darwin" |         "darwin" | ||||||
| @ -2643,7 +2641,6 @@ | |||||||
|       "cpu": [ |       "cpu": [ | ||||||
|         "x64" |         "x64" | ||||||
|       ], |       ], | ||||||
|       "dev": true, |  | ||||||
|       "optional": true, |       "optional": true, | ||||||
|       "os": [ |       "os": [ | ||||||
|         "darwin" |         "darwin" | ||||||
| @ -2659,7 +2656,6 @@ | |||||||
|       "cpu": [ |       "cpu": [ | ||||||
|         "arm64" |         "arm64" | ||||||
|       ], |       ], | ||||||
|       "dev": true, |  | ||||||
|       "optional": true, |       "optional": true, | ||||||
|       "os": [ |       "os": [ | ||||||
|         "freebsd" |         "freebsd" | ||||||
| @ -2675,7 +2671,6 @@ | |||||||
|       "cpu": [ |       "cpu": [ | ||||||
|         "x64" |         "x64" | ||||||
|       ], |       ], | ||||||
|       "dev": true, |  | ||||||
|       "optional": true, |       "optional": true, | ||||||
|       "os": [ |       "os": [ | ||||||
|         "freebsd" |         "freebsd" | ||||||
| @ -2691,7 +2686,6 @@ | |||||||
|       "cpu": [ |       "cpu": [ | ||||||
|         "arm" |         "arm" | ||||||
|       ], |       ], | ||||||
|       "dev": true, |  | ||||||
|       "optional": true, |       "optional": true, | ||||||
|       "os": [ |       "os": [ | ||||||
|         "linux" |         "linux" | ||||||
| @ -2707,7 +2701,6 @@ | |||||||
|       "cpu": [ |       "cpu": [ | ||||||
|         "arm64" |         "arm64" | ||||||
|       ], |       ], | ||||||
|       "dev": true, |  | ||||||
|       "optional": true, |       "optional": true, | ||||||
|       "os": [ |       "os": [ | ||||||
|         "linux" |         "linux" | ||||||
| @ -2723,7 +2716,6 @@ | |||||||
|       "cpu": [ |       "cpu": [ | ||||||
|         "ia32" |         "ia32" | ||||||
|       ], |       ], | ||||||
|       "dev": true, |  | ||||||
|       "optional": true, |       "optional": true, | ||||||
|       "os": [ |       "os": [ | ||||||
|         "linux" |         "linux" | ||||||
| @ -2739,7 +2731,6 @@ | |||||||
|       "cpu": [ |       "cpu": [ | ||||||
|         "loong64" |         "loong64" | ||||||
|       ], |       ], | ||||||
|       "dev": true, |  | ||||||
|       "optional": true, |       "optional": true, | ||||||
|       "os": [ |       "os": [ | ||||||
|         "linux" |         "linux" | ||||||
| @ -2755,7 +2746,6 @@ | |||||||
|       "cpu": [ |       "cpu": [ | ||||||
|         "mips64el" |         "mips64el" | ||||||
|       ], |       ], | ||||||
|       "dev": true, |  | ||||||
|       "optional": true, |       "optional": true, | ||||||
|       "os": [ |       "os": [ | ||||||
|         "linux" |         "linux" | ||||||
| @ -2771,7 +2761,6 @@ | |||||||
|       "cpu": [ |       "cpu": [ | ||||||
|         "ppc64" |         "ppc64" | ||||||
|       ], |       ], | ||||||
|       "dev": true, |  | ||||||
|       "optional": true, |       "optional": true, | ||||||
|       "os": [ |       "os": [ | ||||||
|         "linux" |         "linux" | ||||||
| @ -2787,7 +2776,6 @@ | |||||||
|       "cpu": [ |       "cpu": [ | ||||||
|         "riscv64" |         "riscv64" | ||||||
|       ], |       ], | ||||||
|       "dev": true, |  | ||||||
|       "optional": true, |       "optional": true, | ||||||
|       "os": [ |       "os": [ | ||||||
|         "linux" |         "linux" | ||||||
| @ -2803,7 +2791,6 @@ | |||||||
|       "cpu": [ |       "cpu": [ | ||||||
|         "s390x" |         "s390x" | ||||||
|       ], |       ], | ||||||
|       "dev": true, |  | ||||||
|       "optional": true, |       "optional": true, | ||||||
|       "os": [ |       "os": [ | ||||||
|         "linux" |         "linux" | ||||||
| @ -2819,7 +2806,6 @@ | |||||||
|       "cpu": [ |       "cpu": [ | ||||||
|         "x64" |         "x64" | ||||||
|       ], |       ], | ||||||
|       "dev": true, |  | ||||||
|       "optional": true, |       "optional": true, | ||||||
|       "os": [ |       "os": [ | ||||||
|         "linux" |         "linux" | ||||||
| @ -2835,7 +2821,6 @@ | |||||||
|       "cpu": [ |       "cpu": [ | ||||||
|         "x64" |         "x64" | ||||||
|       ], |       ], | ||||||
|       "dev": true, |  | ||||||
|       "optional": true, |       "optional": true, | ||||||
|       "os": [ |       "os": [ | ||||||
|         "netbsd" |         "netbsd" | ||||||
| @ -2851,7 +2836,6 @@ | |||||||
|       "cpu": [ |       "cpu": [ | ||||||
|         "x64" |         "x64" | ||||||
|       ], |       ], | ||||||
|       "dev": true, |  | ||||||
|       "optional": true, |       "optional": true, | ||||||
|       "os": [ |       "os": [ | ||||||
|         "openbsd" |         "openbsd" | ||||||
| @ -2867,7 +2851,6 @@ | |||||||
|       "cpu": [ |       "cpu": [ | ||||||
|         "x64" |         "x64" | ||||||
|       ], |       ], | ||||||
|       "dev": true, |  | ||||||
|       "optional": true, |       "optional": true, | ||||||
|       "os": [ |       "os": [ | ||||||
|         "sunos" |         "sunos" | ||||||
| @ -2883,7 +2866,6 @@ | |||||||
|       "cpu": [ |       "cpu": [ | ||||||
|         "arm64" |         "arm64" | ||||||
|       ], |       ], | ||||||
|       "dev": true, |  | ||||||
|       "optional": true, |       "optional": true, | ||||||
|       "os": [ |       "os": [ | ||||||
|         "win32" |         "win32" | ||||||
| @ -2899,7 +2881,6 @@ | |||||||
|       "cpu": [ |       "cpu": [ | ||||||
|         "ia32" |         "ia32" | ||||||
|       ], |       ], | ||||||
|       "dev": true, |  | ||||||
|       "optional": true, |       "optional": true, | ||||||
|       "os": [ |       "os": [ | ||||||
|         "win32" |         "win32" | ||||||
| @ -2915,7 +2896,6 @@ | |||||||
|       "cpu": [ |       "cpu": [ | ||||||
|         "x64" |         "x64" | ||||||
|       ], |       ], | ||||||
|       "dev": true, |  | ||||||
|       "optional": true, |       "optional": true, | ||||||
|       "os": [ |       "os": [ | ||||||
|         "win32" |         "win32" | ||||||
| @ -3605,7 +3585,7 @@ | |||||||
|       "version": "0.3.3", |       "version": "0.3.3", | ||||||
|       "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", |       "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", | ||||||
|       "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", |       "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", | ||||||
|       "dev": true, |       "devOptional": true, | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@jridgewell/set-array": "^1.0.1", |         "@jridgewell/set-array": "^1.0.1", | ||||||
|         "@jridgewell/sourcemap-codec": "^1.4.10", |         "@jridgewell/sourcemap-codec": "^1.4.10", | ||||||
| @ -3619,7 +3599,7 @@ | |||||||
|       "version": "3.1.1", |       "version": "3.1.1", | ||||||
|       "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", |       "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", | ||||||
|       "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", |       "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", | ||||||
|       "dev": true, |       "devOptional": true, | ||||||
|       "engines": { |       "engines": { | ||||||
|         "node": ">=6.0.0" |         "node": ">=6.0.0" | ||||||
|       } |       } | ||||||
| @ -3628,7 +3608,7 @@ | |||||||
|       "version": "1.1.2", |       "version": "1.1.2", | ||||||
|       "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", |       "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", | ||||||
|       "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", |       "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", | ||||||
|       "dev": true, |       "devOptional": true, | ||||||
|       "engines": { |       "engines": { | ||||||
|         "node": ">=6.0.0" |         "node": ">=6.0.0" | ||||||
|       } |       } | ||||||
| @ -3637,7 +3617,7 @@ | |||||||
|       "version": "0.3.5", |       "version": "0.3.5", | ||||||
|       "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", |       "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", | ||||||
|       "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", |       "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", | ||||||
|       "dev": true, |       "devOptional": true, | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@jridgewell/gen-mapping": "^0.3.0", |         "@jridgewell/gen-mapping": "^0.3.0", | ||||||
|         "@jridgewell/trace-mapping": "^0.3.9" |         "@jridgewell/trace-mapping": "^0.3.9" | ||||||
| @ -3647,13 +3627,13 @@ | |||||||
|       "version": "1.4.15", |       "version": "1.4.15", | ||||||
|       "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", |       "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", | ||||||
|       "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", |       "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", | ||||||
|       "dev": true |       "devOptional": true | ||||||
|     }, |     }, | ||||||
|     "node_modules/@jridgewell/trace-mapping": { |     "node_modules/@jridgewell/trace-mapping": { | ||||||
|       "version": "0.3.20", |       "version": "0.3.20", | ||||||
|       "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", |       "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", | ||||||
|       "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", |       "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", | ||||||
|       "dev": true, |       "devOptional": true, | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@jridgewell/resolve-uri": "^3.1.0", |         "@jridgewell/resolve-uri": "^3.1.0", | ||||||
|         "@jridgewell/sourcemap-codec": "^1.4.14" |         "@jridgewell/sourcemap-codec": "^1.4.14" | ||||||
| @ -3833,6 +3813,22 @@ | |||||||
|         "react": ">=16.14.0" |         "react": ">=16.14.0" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/@rollup/plugin-virtual": { | ||||||
|  |       "version": "3.0.2", | ||||||
|  |       "resolved": "https://registry.npmjs.org/@rollup/plugin-virtual/-/plugin-virtual-3.0.2.tgz", | ||||||
|  |       "integrity": "sha512-10monEYsBp3scM4/ND4LNH5Rxvh3e/cVeL3jWTgZ2SrQ+BmUoQcopVQvnaMcOnykb1VkxUFuDAN+0FnpTFRy2A==", | ||||||
|  |       "engines": { | ||||||
|  |         "node": ">=14.0.0" | ||||||
|  |       }, | ||||||
|  |       "peerDependencies": { | ||||||
|  |         "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" | ||||||
|  |       }, | ||||||
|  |       "peerDependenciesMeta": { | ||||||
|  |         "rollup": { | ||||||
|  |           "optional": true | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "node_modules/@sideway/address": { |     "node_modules/@sideway/address": { | ||||||
|       "version": "4.1.4", |       "version": "4.1.4", | ||||||
|       "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz", |       "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz", | ||||||
| @ -3906,6 +3902,198 @@ | |||||||
|         "sourcemap-codec": "^1.4.8" |         "sourcemap-codec": "^1.4.8" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/@swc/core": { | ||||||
|  |       "version": "1.3.96", | ||||||
|  |       "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.96.tgz", | ||||||
|  |       "integrity": "sha512-zwE3TLgoZwJfQygdv2SdCK9mRLYluwDOM53I+dT6Z5ZvrgVENmY3txvWDvduzkV+/8IuvrRbVezMpxcojadRdQ==", | ||||||
|  |       "hasInstallScript": true, | ||||||
|  |       "dependencies": { | ||||||
|  |         "@swc/counter": "^0.1.1", | ||||||
|  |         "@swc/types": "^0.1.5" | ||||||
|  |       }, | ||||||
|  |       "engines": { | ||||||
|  |         "node": ">=10" | ||||||
|  |       }, | ||||||
|  |       "funding": { | ||||||
|  |         "type": "opencollective", | ||||||
|  |         "url": "https://opencollective.com/swc" | ||||||
|  |       }, | ||||||
|  |       "optionalDependencies": { | ||||||
|  |         "@swc/core-darwin-arm64": "1.3.96", | ||||||
|  |         "@swc/core-darwin-x64": "1.3.96", | ||||||
|  |         "@swc/core-linux-arm-gnueabihf": "1.3.96", | ||||||
|  |         "@swc/core-linux-arm64-gnu": "1.3.96", | ||||||
|  |         "@swc/core-linux-arm64-musl": "1.3.96", | ||||||
|  |         "@swc/core-linux-x64-gnu": "1.3.96", | ||||||
|  |         "@swc/core-linux-x64-musl": "1.3.96", | ||||||
|  |         "@swc/core-win32-arm64-msvc": "1.3.96", | ||||||
|  |         "@swc/core-win32-ia32-msvc": "1.3.96", | ||||||
|  |         "@swc/core-win32-x64-msvc": "1.3.96" | ||||||
|  |       }, | ||||||
|  |       "peerDependencies": { | ||||||
|  |         "@swc/helpers": "^0.5.0" | ||||||
|  |       }, | ||||||
|  |       "peerDependenciesMeta": { | ||||||
|  |         "@swc/helpers": { | ||||||
|  |           "optional": true | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "node_modules/@swc/core-darwin-arm64": { | ||||||
|  |       "version": "1.3.96", | ||||||
|  |       "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.96.tgz", | ||||||
|  |       "integrity": "sha512-8hzgXYVd85hfPh6mJ9yrG26rhgzCmcLO0h1TIl8U31hwmTbfZLzRitFQ/kqMJNbIBCwmNH1RU2QcJnL3d7f69A==", | ||||||
|  |       "cpu": [ | ||||||
|  |         "arm64" | ||||||
|  |       ], | ||||||
|  |       "optional": true, | ||||||
|  |       "os": [ | ||||||
|  |         "darwin" | ||||||
|  |       ], | ||||||
|  |       "engines": { | ||||||
|  |         "node": ">=10" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "node_modules/@swc/core-darwin-x64": { | ||||||
|  |       "version": "1.3.96", | ||||||
|  |       "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.96.tgz", | ||||||
|  |       "integrity": "sha512-mFp9GFfuPg+43vlAdQZl0WZpZSE8sEzqL7sr/7Reul5McUHP0BaLsEzwjvD035ESfkY8GBZdLpMinblIbFNljQ==", | ||||||
|  |       "cpu": [ | ||||||
|  |         "x64" | ||||||
|  |       ], | ||||||
|  |       "optional": true, | ||||||
|  |       "os": [ | ||||||
|  |         "darwin" | ||||||
|  |       ], | ||||||
|  |       "engines": { | ||||||
|  |         "node": ">=10" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "node_modules/@swc/core-linux-arm-gnueabihf": { | ||||||
|  |       "version": "1.3.96", | ||||||
|  |       "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.96.tgz", | ||||||
|  |       "integrity": "sha512-8UEKkYJP4c8YzYIY/LlbSo8z5Obj4hqcv/fUTHiEePiGsOddgGf7AWjh56u7IoN/0uEmEro59nc1ChFXqXSGyg==", | ||||||
|  |       "cpu": [ | ||||||
|  |         "arm" | ||||||
|  |       ], | ||||||
|  |       "optional": true, | ||||||
|  |       "os": [ | ||||||
|  |         "linux" | ||||||
|  |       ], | ||||||
|  |       "engines": { | ||||||
|  |         "node": ">=10" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "node_modules/@swc/core-linux-arm64-gnu": { | ||||||
|  |       "version": "1.3.96", | ||||||
|  |       "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.96.tgz", | ||||||
|  |       "integrity": "sha512-c/IiJ0s1y3Ymm2BTpyC/xr6gOvoqAVETrivVXHq68xgNms95luSpbYQ28rqaZC8bQC8M5zdXpSc0T8DJu8RJGw==", | ||||||
|  |       "cpu": [ | ||||||
|  |         "arm64" | ||||||
|  |       ], | ||||||
|  |       "optional": true, | ||||||
|  |       "os": [ | ||||||
|  |         "linux" | ||||||
|  |       ], | ||||||
|  |       "engines": { | ||||||
|  |         "node": ">=10" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "node_modules/@swc/core-linux-arm64-musl": { | ||||||
|  |       "version": "1.3.96", | ||||||
|  |       "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.96.tgz", | ||||||
|  |       "integrity": "sha512-i5/UTUwmJLri7zhtF6SAo/4QDQJDH2fhYJaBIUhrICmIkRO/ltURmpejqxsM/ye9Jqv5zG7VszMC0v/GYn/7BQ==", | ||||||
|  |       "cpu": [ | ||||||
|  |         "arm64" | ||||||
|  |       ], | ||||||
|  |       "optional": true, | ||||||
|  |       "os": [ | ||||||
|  |         "linux" | ||||||
|  |       ], | ||||||
|  |       "engines": { | ||||||
|  |         "node": ">=10" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "node_modules/@swc/core-linux-x64-gnu": { | ||||||
|  |       "version": "1.3.96", | ||||||
|  |       "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.96.tgz", | ||||||
|  |       "integrity": "sha512-USdaZu8lTIkm4Yf9cogct/j5eqtdZqTgcTib4I+NloUW0E/hySou3eSyp3V2UAA1qyuC72ld1otXuyKBna0YKQ==", | ||||||
|  |       "cpu": [ | ||||||
|  |         "x64" | ||||||
|  |       ], | ||||||
|  |       "optional": true, | ||||||
|  |       "os": [ | ||||||
|  |         "linux" | ||||||
|  |       ], | ||||||
|  |       "engines": { | ||||||
|  |         "node": ">=10" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "node_modules/@swc/core-linux-x64-musl": { | ||||||
|  |       "version": "1.3.96", | ||||||
|  |       "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.96.tgz", | ||||||
|  |       "integrity": "sha512-QYErutd+G2SNaCinUVobfL7jWWjGTI0QEoQ6hqTp7PxCJS/dmKmj3C5ZkvxRYcq7XcZt7ovrYCTwPTHzt6lZBg==", | ||||||
|  |       "cpu": [ | ||||||
|  |         "x64" | ||||||
|  |       ], | ||||||
|  |       "optional": true, | ||||||
|  |       "os": [ | ||||||
|  |         "linux" | ||||||
|  |       ], | ||||||
|  |       "engines": { | ||||||
|  |         "node": ">=10" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "node_modules/@swc/core-win32-arm64-msvc": { | ||||||
|  |       "version": "1.3.96", | ||||||
|  |       "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.96.tgz", | ||||||
|  |       "integrity": "sha512-hjGvvAduA3Un2cZ9iNP4xvTXOO4jL3G9iakhFsgVhpkU73SGmK7+LN8ZVBEu4oq2SUcHO6caWvnZ881cxGuSpg==", | ||||||
|  |       "cpu": [ | ||||||
|  |         "arm64" | ||||||
|  |       ], | ||||||
|  |       "optional": true, | ||||||
|  |       "os": [ | ||||||
|  |         "win32" | ||||||
|  |       ], | ||||||
|  |       "engines": { | ||||||
|  |         "node": ">=10" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "node_modules/@swc/core-win32-ia32-msvc": { | ||||||
|  |       "version": "1.3.96", | ||||||
|  |       "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.96.tgz", | ||||||
|  |       "integrity": "sha512-Far2hVFiwr+7VPCM2GxSmbh3ikTpM3pDombE+d69hkedvYHYZxtTF+2LTKl/sXtpbUnsoq7yV/32c9R/xaaWfw==", | ||||||
|  |       "cpu": [ | ||||||
|  |         "ia32" | ||||||
|  |       ], | ||||||
|  |       "optional": true, | ||||||
|  |       "os": [ | ||||||
|  |         "win32" | ||||||
|  |       ], | ||||||
|  |       "engines": { | ||||||
|  |         "node": ">=10" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "node_modules/@swc/core-win32-x64-msvc": { | ||||||
|  |       "version": "1.3.96", | ||||||
|  |       "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.96.tgz", | ||||||
|  |       "integrity": "sha512-4VbSAniIu0ikLf5mBX81FsljnfqjoVGleEkCQv4+zRlyZtO3FHoDPkeLVoy6WRlj7tyrRcfUJ4mDdPkbfTO14g==", | ||||||
|  |       "cpu": [ | ||||||
|  |         "x64" | ||||||
|  |       ], | ||||||
|  |       "optional": true, | ||||||
|  |       "os": [ | ||||||
|  |         "win32" | ||||||
|  |       ], | ||||||
|  |       "engines": { | ||||||
|  |         "node": ">=10" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "node_modules/@swc/counter": { | ||||||
|  |       "version": "0.1.2", | ||||||
|  |       "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.2.tgz", | ||||||
|  |       "integrity": "sha512-9F4ys4C74eSTEUNndnER3VJ15oru2NumfQxS8geE+f3eB5xvfxpWyqE5XlVnxb/R14uoXi6SLbBwwiDSkv+XEw==" | ||||||
|  |     }, | ||||||
|     "node_modules/@swc/helpers": { |     "node_modules/@swc/helpers": { | ||||||
|       "version": "0.5.3", |       "version": "0.5.3", | ||||||
|       "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.3.tgz", |       "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.3.tgz", | ||||||
| @ -3919,6 +4107,11 @@ | |||||||
|       "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", |       "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", | ||||||
|       "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" |       "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/@swc/types": { | ||||||
|  |       "version": "0.1.5", | ||||||
|  |       "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.5.tgz", | ||||||
|  |       "integrity": "sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==" | ||||||
|  |     }, | ||||||
|     "node_modules/@swiftcarrot/color-fns": { |     "node_modules/@swiftcarrot/color-fns": { | ||||||
|       "version": "3.2.0", |       "version": "3.2.0", | ||||||
|       "resolved": "https://registry.npmjs.org/@swiftcarrot/color-fns/-/color-fns-3.2.0.tgz", |       "resolved": "https://registry.npmjs.org/@swiftcarrot/color-fns/-/color-fns-3.2.0.tgz", | ||||||
| @ -4281,7 +4474,7 @@ | |||||||
|       "version": "18.18.7", |       "version": "18.18.7", | ||||||
|       "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.7.tgz", |       "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.7.tgz", | ||||||
|       "integrity": "sha512-bw+lEsxis6eqJYW8Ql6+yTqkE6RuFtsQPSe5JxXbqYRFQEER5aJA9a5UH9igqDWm3X4iLHIKOHlnAXLM4mi7uQ==", |       "integrity": "sha512-bw+lEsxis6eqJYW8Ql6+yTqkE6RuFtsQPSe5JxXbqYRFQEER5aJA9a5UH9igqDWm3X4iLHIKOHlnAXLM4mi7uQ==", | ||||||
|       "dev": true, |       "devOptional": true, | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "undici-types": "~5.26.4" |         "undici-types": "~5.26.4" | ||||||
|       } |       } | ||||||
| @ -4749,7 +4942,7 @@ | |||||||
|       "version": "8.10.0", |       "version": "8.10.0", | ||||||
|       "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", |       "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", | ||||||
|       "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", |       "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", | ||||||
|       "dev": true, |       "devOptional": true, | ||||||
|       "bin": { |       "bin": { | ||||||
|         "acorn": "bin/acorn" |         "acorn": "bin/acorn" | ||||||
|       }, |       }, | ||||||
| @ -6751,7 +6944,6 @@ | |||||||
|       "version": "0.18.20", |       "version": "0.18.20", | ||||||
|       "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", |       "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", | ||||||
|       "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", |       "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", | ||||||
|       "dev": true, |  | ||||||
|       "hasInstallScript": true, |       "hasInstallScript": true, | ||||||
|       "bin": { |       "bin": { | ||||||
|         "esbuild": "bin/esbuild" |         "esbuild": "bin/esbuild" | ||||||
| @ -10093,7 +10285,6 @@ | |||||||
|       "version": "3.3.6", |       "version": "3.3.6", | ||||||
|       "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", |       "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", | ||||||
|       "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", |       "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", | ||||||
|       "dev": true, |  | ||||||
|       "funding": [ |       "funding": [ | ||||||
|         { |         { | ||||||
|           "type": "github", |           "type": "github", | ||||||
| @ -10810,8 +11001,7 @@ | |||||||
|     "node_modules/picocolors": { |     "node_modules/picocolors": { | ||||||
|       "version": "1.0.0", |       "version": "1.0.0", | ||||||
|       "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", |       "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", | ||||||
|       "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", |       "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" | ||||||
|       "dev": true |  | ||||||
|     }, |     }, | ||||||
|     "node_modules/picomatch": { |     "node_modules/picomatch": { | ||||||
|       "version": "2.3.1", |       "version": "2.3.1", | ||||||
| @ -10862,7 +11052,6 @@ | |||||||
|       "version": "8.4.31", |       "version": "8.4.31", | ||||||
|       "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", |       "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", | ||||||
|       "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", |       "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", | ||||||
|       "dev": true, |  | ||||||
|       "funding": [ |       "funding": [ | ||||||
|         { |         { | ||||||
|           "type": "opencollective", |           "type": "opencollective", | ||||||
| @ -11639,7 +11828,6 @@ | |||||||
|       "version": "3.29.4", |       "version": "3.29.4", | ||||||
|       "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", |       "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", | ||||||
|       "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", |       "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", | ||||||
|       "dev": true, |  | ||||||
|       "bin": { |       "bin": { | ||||||
|         "rollup": "dist/bin/rollup" |         "rollup": "dist/bin/rollup" | ||||||
|       }, |       }, | ||||||
| @ -12042,7 +12230,7 @@ | |||||||
|       "version": "0.6.1", |       "version": "0.6.1", | ||||||
|       "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", |       "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", | ||||||
|       "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", |       "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", | ||||||
|       "dev": true, |       "devOptional": true, | ||||||
|       "engines": { |       "engines": { | ||||||
|         "node": ">=0.10.0" |         "node": ">=0.10.0" | ||||||
|       } |       } | ||||||
| @ -12051,7 +12239,6 @@ | |||||||
|       "version": "1.0.2", |       "version": "1.0.2", | ||||||
|       "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", |       "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", | ||||||
|       "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", |       "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", | ||||||
|       "dev": true, |  | ||||||
|       "engines": { |       "engines": { | ||||||
|         "node": ">=0.10.0" |         "node": ">=0.10.0" | ||||||
|       } |       } | ||||||
| @ -12060,7 +12247,7 @@ | |||||||
|       "version": "0.5.21", |       "version": "0.5.21", | ||||||
|       "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", |       "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", | ||||||
|       "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", |       "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", | ||||||
|       "dev": true, |       "devOptional": true, | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "buffer-from": "^1.0.0", |         "buffer-from": "^1.0.0", | ||||||
|         "source-map": "^0.6.0" |         "source-map": "^0.6.0" | ||||||
| @ -12476,7 +12663,7 @@ | |||||||
|       "version": "5.22.0", |       "version": "5.22.0", | ||||||
|       "resolved": "https://registry.npmjs.org/terser/-/terser-5.22.0.tgz", |       "resolved": "https://registry.npmjs.org/terser/-/terser-5.22.0.tgz", | ||||||
|       "integrity": "sha512-hHZVLgRA2z4NWcN6aS5rQDc+7Dcy58HOf2zbYwmFcQ+ua3h6eEFf5lIDKTzbWwlazPyOZsFQO8V80/IjVNExEw==", |       "integrity": "sha512-hHZVLgRA2z4NWcN6aS5rQDc+7Dcy58HOf2zbYwmFcQ+ua3h6eEFf5lIDKTzbWwlazPyOZsFQO8V80/IjVNExEw==", | ||||||
|       "dev": true, |       "devOptional": true, | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@jridgewell/source-map": "^0.3.3", |         "@jridgewell/source-map": "^0.3.3", | ||||||
|         "acorn": "^8.8.2", |         "acorn": "^8.8.2", | ||||||
| @ -12494,7 +12681,7 @@ | |||||||
|       "version": "2.20.3", |       "version": "2.20.3", | ||||||
|       "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", |       "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", | ||||||
|       "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", |       "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", | ||||||
|       "dev": true |       "devOptional": true | ||||||
|     }, |     }, | ||||||
|     "node_modules/text-table": { |     "node_modules/text-table": { | ||||||
|       "version": "0.2.0", |       "version": "0.2.0", | ||||||
| @ -13038,7 +13225,7 @@ | |||||||
|       "version": "5.26.5", |       "version": "5.26.5", | ||||||
|       "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", |       "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", | ||||||
|       "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", |       "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", | ||||||
|       "dev": true |       "devOptional": true | ||||||
|     }, |     }, | ||||||
|     "node_modules/unicode-canonical-property-names-ecmascript": { |     "node_modules/unicode-canonical-property-names-ecmascript": { | ||||||
|       "version": "2.0.0", |       "version": "2.0.0", | ||||||
| @ -13242,7 +13429,6 @@ | |||||||
|       "version": "4.5.0", |       "version": "4.5.0", | ||||||
|       "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.0.tgz", |       "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.0.tgz", | ||||||
|       "integrity": "sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==", |       "integrity": "sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==", | ||||||
|       "dev": true, |  | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "esbuild": "^0.18.10", |         "esbuild": "^0.18.10", | ||||||
|         "postcss": "^8.4.27", |         "postcss": "^8.4.27", | ||||||
| @ -13398,6 +13584,31 @@ | |||||||
|         "url": "https://github.com/sponsors/sindresorhus" |         "url": "https://github.com/sponsors/sindresorhus" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/vite-plugin-top-level-await": { | ||||||
|  |       "version": "1.3.1", | ||||||
|  |       "resolved": "https://registry.npmjs.org/vite-plugin-top-level-await/-/vite-plugin-top-level-await-1.3.1.tgz", | ||||||
|  |       "integrity": "sha512-55M1h4NAwkrpxPNOJIBzKZFihqLUzIgnElLSmPNPMR2Fn9+JHKaNg3sVX1Fq+VgvuBksQYxiD3OnwQAUu7kaPQ==", | ||||||
|  |       "dependencies": { | ||||||
|  |         "@rollup/plugin-virtual": "^3.0.1", | ||||||
|  |         "@swc/core": "^1.3.10", | ||||||
|  |         "uuid": "^9.0.0" | ||||||
|  |       }, | ||||||
|  |       "peerDependencies": { | ||||||
|  |         "vite": ">=2.8" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "node_modules/vite-plugin-top-level-await/node_modules/uuid": { | ||||||
|  |       "version": "9.0.1", | ||||||
|  |       "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", | ||||||
|  |       "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", | ||||||
|  |       "funding": [ | ||||||
|  |         "https://github.com/sponsors/broofa", | ||||||
|  |         "https://github.com/sponsors/ctavan" | ||||||
|  |       ], | ||||||
|  |       "bin": { | ||||||
|  |         "uuid": "dist/bin/uuid" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "node_modules/vitest": { |     "node_modules/vitest": { | ||||||
|       "version": "0.32.4", |       "version": "0.32.4", | ||||||
|       "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.32.4.tgz", |       "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.32.4.tgz", | ||||||
|  | |||||||
| @ -2,6 +2,7 @@ import express, { Request, Response } from 'express'; | |||||||
| 
 | 
 | ||||||
| //import workflow from './workflow-controller';
 | //import workflow from './workflow-controller';
 | ||||||
| import operations from './operations-controller'; | import operations from './operations-controller'; | ||||||
|  | import conversions from './conversions-controller'; | ||||||
| 
 | 
 | ||||||
| const router = express.Router(); | const router = express.Router(); | ||||||
| 
 | 
 | ||||||
| @ -11,6 +12,7 @@ router.get("/", (req: Request, res: Response) => { | |||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| router.use("/operations", operations); | router.use("/operations", operations); | ||||||
|  | router.use("/conversions", conversions); | ||||||
| //router.use("/workflow", workflow);
 | //router.use("/workflow", workflow);
 | ||||||
| 
 | 
 | ||||||
| export default router; | export default router; | ||||||
							
								
								
									
										27
									
								
								server-node/src/routes/api/conversions-controller.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								server-node/src/routes/api/conversions-controller.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,27 @@ | |||||||
|  | 
 | ||||||
|  | import { respondWithPdfFile, response_mustHaveExactlyOneFile, response_dependencyNotConfigured } from '../../utils/endpoint-utils'; | ||||||
|  | import { fileToPdf, isLibreOfficeInstalled } from '../../utils/libre-office-utils'; | ||||||
|  | 
 | ||||||
|  | import express, { Request, Response } from 'express'; | ||||||
|  | const router = express.Router(); | ||||||
|  | import multer from 'multer'; | ||||||
|  | const upload = multer(); | ||||||
|  | import Joi from 'joi'; | ||||||
|  | 
 | ||||||
|  | router.post('/file-to-pdf', upload.single("file"), async function(req: Request, res: Response) { | ||||||
|  |     if (!req.file) { | ||||||
|  |         response_mustHaveExactlyOneFile(res); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const isInstalled = await isLibreOfficeInstalled(); | ||||||
|  |     if (isInstalled) { | ||||||
|  |         const outputFile = await fileToPdf(req.file.buffer, req.file.originalname); | ||||||
|  |         respondWithPdfFile(res, outputFile); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     response_dependencyNotConfigured(res, "LibreOffice"); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | export default router; | ||||||
| @ -2,14 +2,18 @@ | |||||||
| import { Response } from 'express'; | import { Response } from 'express'; | ||||||
| import { PdfFile } from '@stirling-pdf/shared-operations/wrappers/PdfFile' | import { PdfFile } from '@stirling-pdf/shared-operations/wrappers/PdfFile' | ||||||
| 
 | 
 | ||||||
|  | export async function respondWithFile(res: Response, bytes: Uint8Array, name: string, mimeType: string): Promise<void> { | ||||||
|  |     res.writeHead(200, { | ||||||
|  |         'Content-Type': mimeType, | ||||||
|  |         'Content-disposition': 'attachment;filename=' + name, | ||||||
|  |         'Content-Length': bytes.length | ||||||
|  |     }); | ||||||
|  |     res.end(bytes); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| export async function respondWithPdfFile(res: Response, file: PdfFile): Promise<void> { | export async function respondWithPdfFile(res: Response, file: PdfFile): Promise<void> { | ||||||
|     const byteFile = await file.convertToByteArrayFile(); |     const byteFile = await file.convertToByteArrayFile(); | ||||||
|     res.writeHead(200, { |     respondWithFile(res, byteFile.byteArray!, byteFile.filename, "application/pdf"); | ||||||
|         'Content-Type': "application/pdf", |  | ||||||
|         'Content-disposition': 'attachment;filename=' + byteFile.filename, |  | ||||||
|         'Content-Length': byteFile.byteArray?.length |  | ||||||
|     }); |  | ||||||
|     res.end(byteFile.byteArray) |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function response_mustHaveExactlyOneFile(res: Response): void { | export function response_mustHaveExactlyOneFile(res: Response): void { | ||||||
| @ -27,3 +31,12 @@ export function response_mustHaveExactlyOneFile(res: Response): void { | |||||||
|         } |         } | ||||||
|     ]); |     ]); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | export function response_dependencyNotConfigured(res: Response, dependencyName: string): void { | ||||||
|  |     res.status(400).send([ | ||||||
|  |         { | ||||||
|  |             "message": `${dependencyName} is not configured correctly on the server.`, | ||||||
|  |             "type": "dependency_error", | ||||||
|  |         } | ||||||
|  |     ]); | ||||||
|  | } | ||||||
|  | |||||||
							
								
								
									
										99
									
								
								server-node/src/utils/libre-office-utils.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								server-node/src/utils/libre-office-utils.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,99 @@ | |||||||
|  | 
 | ||||||
|  | import fs from 'fs'; | ||||||
|  | import os from 'os'; | ||||||
|  | import path from 'path'; | ||||||
|  | import { exec, spawn } from 'child_process' | ||||||
|  | import { PdfFile, fromUint8Array } from '@stirling-pdf/shared-operations/wrappers/PdfFile' | ||||||
|  | 
 | ||||||
|  | export async function fileToPdf(byteArray: Uint8Array, filename: string): Promise<PdfFile> { | ||||||
|  |     const parentDir = path.join(os.tmpdir(), "StirlingPDF"); | ||||||
|  |     fs.mkdirSync(parentDir, {recursive: true}); | ||||||
|  |     const tempDir = fs.mkdtempSync(parentDir+"/"); | ||||||
|  |     const srcFile = path.join(tempDir, filename); | ||||||
|  |     const randFolderName = path.parse(tempDir).base; | ||||||
|  | 
 | ||||||
|  |     await writeBytesToFile(srcFile, byteArray); | ||||||
|  | 
 | ||||||
|  |     const messages = await runLibreOfficeCommand(randFolderName, ["--headless","--convert-to","pdf",srcFile,"--outdir",tempDir]); | ||||||
|  |     const lastMessage = messages[messages.length-1] | ||||||
|  |     const outputFilePath = lastMessage.split(" -> ")[1].split(".pdf")[0]+".pdf"; | ||||||
|  |     const outputFileName = path.parse(outputFilePath).base; | ||||||
|  |     const outputBytes = await readBytesFromFile(outputFilePath); | ||||||
|  | 
 | ||||||
|  |     fs.rmdirSync(tempDir); | ||||||
|  | 
 | ||||||
|  |     return fromUint8Array(outputBytes, outputFileName); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function isLibreOfficeInstalled() { | ||||||
|  |     return new Promise((resolve, reject) => { | ||||||
|  |         exec("libreoffice --version", (error, stdout, stderr) => { | ||||||
|  |             if (error) { | ||||||
|  |                 resolve(false); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |             if (stderr) { | ||||||
|  |                 resolve(false); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |             const result = stdout.match("LibreOffice ([0-9]+\.){4}.*"); | ||||||
|  |             resolve(result ? true : false); | ||||||
|  |         }); | ||||||
|  |     }) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function writeBytesToFile(filePath: string, bytes: Uint8Array): Promise<void> { | ||||||
|  |     return new Promise((resolve, reject) => { | ||||||
|  |         fs.writeFile(filePath, bytes, function(err) { | ||||||
|  |             if(err) { | ||||||
|  |                 reject(err) | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |             resolve(); | ||||||
|  |         }); | ||||||
|  |     }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function readBytesFromFile(filePath: string): Promise<Uint8Array> { | ||||||
|  |     return new Promise((resolve, reject) => { | ||||||
|  |         fs.readFile(filePath, (err, data) => { | ||||||
|  |             if (err) { | ||||||
|  |                 reject(new Error(`Error reading file: ${err.message}`)); | ||||||
|  |             } else { | ||||||
|  |                 const uint8Array = new Uint8Array(data); | ||||||
|  |                 resolve(uint8Array); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     }); | ||||||
|  | } | ||||||
|  |    | ||||||
|  | function runLibreOfficeCommand(idKey: string, args: string[]): Promise<string[]> { | ||||||
|  |     return new Promise(async (resolve, reject) => { | ||||||
|  |         const messageList: string[] = []; | ||||||
|  | 
 | ||||||
|  |         const process = spawn("libreoffice", args); | ||||||
|  | 
 | ||||||
|  |         process.stdout.on('data', (data) => { | ||||||
|  |             const dataStr = data.toString(); | ||||||
|  |             console.log(`Progress ${idKey}:`, dataStr); | ||||||
|  |             messageList.push(dataStr); | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         process.stderr.on('data', (data) => { | ||||||
|  |             console.error(`stderr ${idKey}:`, data.toString()); | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         process.on('exit', (code) => { | ||||||
|  |             if (code === 0) { | ||||||
|  |                 resolve(messageList); | ||||||
|  |             } else { | ||||||
|  |                 reject(new Error(`Command failed with exit code ${code}`)); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         process.on('error', (err) => { | ||||||
|  |             reject(err); | ||||||
|  |         }); | ||||||
|  |          | ||||||
|  |     }); | ||||||
|  | } | ||||||
| @ -1,6 +1,4 @@ | |||||||
| 
 | 
 | ||||||
| import { PDFDocument } from 'pdf-lib'; |  | ||||||
| 
 |  | ||||||
| import { selectPages } from "./subDocumentFunctions"; | import { selectPages } from "./subDocumentFunctions"; | ||||||
| import { PdfFile } from '../wrappers/PdfFile'; | import { PdfFile } from '../wrappers/PdfFile'; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,5 +1,4 @@ | |||||||
| 
 | 
 | ||||||
| import { PDFDocument, ParseSpeeds } from 'pdf-lib'; |  | ||||||
| import { PdfFile, fromPdfLib } from '../wrappers/PdfFile'; | import { PdfFile, fromPdfLib } from '../wrappers/PdfFile'; | ||||||
| 
 | 
 | ||||||
| export type Metadata = { | export type Metadata = { | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user