From cfc35f45b11185755c077c8d007686979a4915de Mon Sep 17 00:00:00 2001 From: Nicolas Bonamy Date: Thu, 9 May 2024 13:39:28 -0500 Subject: [PATCH] more text formats --- README.md | 2 +- .../xl/sharedStrings.xml | 2 + .../xl/worksheets/sheet1.xml | 2 + package-lock.json | 460 ++++++++++++++++-- package.json | 3 +- src/components/Attachment.vue | 3 + src/main/text.ts | 11 + src/services/llm.ts | 2 +- src/services/openai.ts | 4 +- tests/fixtures/sample.docx | Bin 0 -> 13019 bytes tests/fixtures/sample.pdf | Bin 0 -> 8953 bytes tests/fixtures/sample.pptx | Bin 0 -> 32657 bytes tests/fixtures/sample.txt | 1 + tests/fixtures/sample.xlsx | Bin 0 -> 8838 bytes tests/unit/engine_openai.test.ts | 2 +- tests/unit/text.test.ts | 34 ++ 16 files changed, 477 insertions(+), 49 deletions(-) create mode 100644 officeParserTemp/171527995559100002.xlsx/xl/sharedStrings.xml create mode 100644 officeParserTemp/171527995559100002.xlsx/xl/worksheets/sheet1.xml create mode 100644 tests/fixtures/sample.docx create mode 100644 tests/fixtures/sample.pdf create mode 100644 tests/fixtures/sample.pptx create mode 100644 tests/fixtures/sample.txt create mode 100644 tests/fixtures/sample.xlsx create mode 100644 tests/unit/text.test.ts diff --git a/README.md b/README.md index 72f5e71..245601b 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ To use Internet search you need a [Tavily API key](https://app.tavily.com/home). ## DONE -- [x] Text attachments +- [x] Text attachments (TXT, PDF, DOCX, PPTX, XLSX) - [x] MistralAI function calling - [x] Auto-update - [x] History date sections diff --git a/officeParserTemp/171527995559100002.xlsx/xl/sharedStrings.xml b/officeParserTemp/171527995559100002.xlsx/xl/sharedStrings.xml new file mode 100644 index 0000000..7a22ef9 --- /dev/null +++ b/officeParserTemp/171527995559100002.xlsx/xl/sharedStrings.xml @@ -0,0 +1,2 @@ + +Hello from Excel \ No newline at end of file diff --git a/officeParserTemp/171527995559100002.xlsx/xl/worksheets/sheet1.xml b/officeParserTemp/171527995559100002.xlsx/xl/worksheets/sheet1.xml new file mode 100644 index 0000000..7dec635 --- /dev/null +++ b/officeParserTemp/171527995559100002.xlsx/xl/worksheets/sheet1.xml @@ -0,0 +1,2 @@ + +0 \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index ebb1673..e41c81b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "witsy", - "version": "1.4.3", + "version": "1.5.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "witsy", - "version": "1.4.3", + "version": "1.5.0", "license": "MIT", "dependencies": { "@anthropic-ai/sdk": "^0.20.4", @@ -27,6 +27,7 @@ "markdown-it": "^14.1.0", "markdown-it-mark": "^4.0.0", "mitt": "^3.0.1", + "officeparser": "^4.1.1", "ollama": "^0.5.0", "openai": "^4.32.1", "openai-speech-stream-player": "^1.0.8", @@ -3158,6 +3159,11 @@ "node": ">=10" } }, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==" + }, "node_modules/@tootallnate/once": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", @@ -4688,8 +4694,7 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/base": { "version": "0.11.2", @@ -4742,7 +4747,6 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, "funding": [ { "type": "github", @@ -4864,7 +4868,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -4936,7 +4939,6 @@ "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, "funding": [ { "type": "github", @@ -4956,15 +4958,33 @@ "ieee754": "^1.1.13" } }, + "node_modules/buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "dependencies": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "node_modules/buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==" + }, "node_modules/buffer-crc32": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", - "dev": true, "engines": { "node": "*" } }, + "node_modules/buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==" + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -5544,8 +5564,7 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "node_modules/concat-stream": { "version": "1.6.2", @@ -5723,8 +5742,7 @@ "node_modules/core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "dev": true + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" }, "node_modules/create-require": { "version": "1.1.1", @@ -6019,6 +6037,24 @@ "node": ">=0.10" } }, + "node_modules/decompress": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress/-/decompress-4.2.1.tgz", + "integrity": "sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ==", + "dependencies": { + "decompress-tar": "^4.0.0", + "decompress-tarbz2": "^4.0.0", + "decompress-targz": "^4.0.0", + "decompress-unzip": "^4.0.1", + "graceful-fs": "^4.1.10", + "make-dir": "^1.0.0", + "pify": "^2.3.0", + "strip-dirs": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/decompress-response": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", @@ -6046,6 +6082,124 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/decompress-tar": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz", + "integrity": "sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==", + "dependencies": { + "file-type": "^5.2.0", + "is-stream": "^1.1.0", + "tar-stream": "^1.5.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress-tar/node_modules/file-type": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz", + "integrity": "sha512-Iq1nJ6D2+yIO4c8HHg4fyVb8mAJieo1Oloy1mLLaB2PvezNedhBVm+QU7g0qM42aiMbRXTxKKwGD17rjKNJYVQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress-tarbz2": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz", + "integrity": "sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==", + "dependencies": { + "decompress-tar": "^4.1.0", + "file-type": "^6.1.0", + "is-stream": "^1.1.0", + "seek-bzip": "^1.0.5", + "unbzip2-stream": "^1.0.9" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress-tarbz2/node_modules/file-type": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-6.2.0.tgz", + "integrity": "sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==", + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress-targz": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz", + "integrity": "sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==", + "dependencies": { + "decompress-tar": "^4.1.1", + "file-type": "^5.2.0", + "is-stream": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress-targz/node_modules/file-type": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz", + "integrity": "sha512-Iq1nJ6D2+yIO4c8HHg4fyVb8mAJieo1Oloy1mLLaB2PvezNedhBVm+QU7g0qM42aiMbRXTxKKwGD17rjKNJYVQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress-unzip": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz", + "integrity": "sha512-1fqeluvxgnn86MOh66u8FjbtJpAFv5wgCT9Iw8rcBqQcCo5tO8eiJw7NNTrvt9n4CRBVq7CstiS922oPgyGLrw==", + "dependencies": { + "file-type": "^3.8.0", + "get-stream": "^2.2.0", + "pify": "^2.3.0", + "yauzl": "^2.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress-unzip/node_modules/file-type": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "integrity": "sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decompress-unzip/node_modules/get-stream": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", + "integrity": "sha512-AUGhbbemXxrZJRD5cDvKtQxLuYaIbNtDTK8YqupCI393Q2KSTreEsLUN3ZxAWFGiKTzL6nKuzfcIvieflUX9qA==", + "dependencies": { + "object-assign": "^4.0.1", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decompress/node_modules/make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress/node_modules/make-dir/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "engines": { + "node": ">=4" + } + }, "node_modules/deep-eql": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", @@ -7038,7 +7192,6 @@ "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, "dependencies": { "once": "^1.4.0" } @@ -8162,7 +8315,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", - "dev": true, "dependencies": { "pend": "~1.2.0" } @@ -8185,6 +8337,22 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/file-type": { + "version": "16.5.4", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-16.5.4.tgz", + "integrity": "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==", + "dependencies": { + "readable-web-to-node-stream": "^3.0.0", + "strtok3": "^6.2.4", + "token-types": "^4.1.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, "node_modules/filename-reserved-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", @@ -8431,6 +8599,11 @@ "node": ">= 0.6" } }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, "node_modules/fs-extra": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", @@ -8484,8 +8657,7 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/fsevents": { "version": "2.3.2", @@ -8770,7 +8942,6 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -8966,8 +9137,7 @@ "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, "node_modules/graphemer": { "version": "1.4.0", @@ -9427,7 +9597,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, "funding": [ { "type": "github", @@ -9545,7 +9714,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -9554,8 +9722,7 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/ini": { "version": "1.3.8", @@ -9876,6 +10043,11 @@ "xtend": "^4.0.0" } }, + "node_modules/is-natural-number": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz", + "integrity": "sha512-Y4LTamMe0DDQIIAlaer9eKebAlDSV6huy+TWhJVPlzZh2o4tRP5SQWFlLn5N0To4mDD22/qdOq+veo1cSISLgQ==" + }, "node_modules/is-negative-zero": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", @@ -9995,7 +10167,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -12765,7 +12936,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -13062,6 +13232,11 @@ "node": ">=10.5.0" } }, + "node_modules/node-ensure": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/node-ensure/-/node-ensure-0.0.0.tgz", + "integrity": "sha512-DRI60hzo2oKN1ma0ckc6nQWlHU69RH6xN0sjQTjMpChPfTYvKZdcQFfdYK2RWbJcKyUizSIy/l8OTGxMAM1QDw==" + }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -13254,6 +13429,14 @@ "dev": true, "peer": true }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-copy": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", @@ -13420,6 +13603,32 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/officeparser": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/officeparser/-/officeparser-4.1.1.tgz", + "integrity": "sha512-bOh7l6Bt/caeyU9t+9yGdQF2N30j8puR7PhXmSI/NqssHNnfnTLp1ehpBo4KuIMeOvzhr8mvkXHFpR2qhH1uhg==", + "dependencies": { + "@xmldom/xmldom": "^0.8.10", + "decompress": "^4.2.0", + "file-type": "^16.5.4", + "node-ensure": "^0.0.0", + "rimraf": "^2.6.3" + }, + "bin": { + "officeparser": "officeParser.js" + } + }, + "node_modules/officeparser/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, "node_modules/ollama": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/ollama/-/ollama-0.5.0.tgz", @@ -13444,7 +13653,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "dependencies": { "wrappy": "1" } @@ -13795,7 +14003,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -13922,11 +14129,22 @@ "url": "https://ko-fi.com/killymxi" } }, + "node_modules/peek-readable": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-4.1.0.tgz", + "integrity": "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==", + "engines": { + "node": ">=8" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "dev": true + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==" }, "node_modules/picocolors": { "version": "1.0.0", @@ -13949,7 +14167,25 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", + "dependencies": { + "pinkie": "^2.0.0" + }, "engines": { "node": ">=0.10.0" } @@ -14258,8 +14494,7 @@ "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, "node_modules/progress": { "version": "2.0.3", @@ -14614,7 +14849,6 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -14624,6 +14858,21 @@ "node": ">= 6" } }, + "node_modules/readable-web-to-node-stream": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz", + "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==", + "dependencies": { + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/rechoir": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", @@ -15012,7 +15261,6 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, "funding": [ { "type": "github", @@ -15262,6 +15510,23 @@ "node": ">=10" } }, + "node_modules/seek-bzip": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.6.tgz", + "integrity": "sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ==", + "dependencies": { + "commander": "^2.8.1" + }, + "bin": { + "seek-bunzip": "bin/seek-bunzip", + "seek-table": "bin/seek-bzip-table" + } + }, + "node_modules/seek-bzip/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, "node_modules/selderee": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/selderee/-/selderee-0.11.0.tgz", @@ -16005,7 +16270,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, "dependencies": { "safe-buffer": "~5.2.0" } @@ -16136,6 +16400,14 @@ "node": ">=4" } }, + "node_modules/strip-dirs": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz", + "integrity": "sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==", + "dependencies": { + "is-natural-number": "^4.0.1" + } + }, "node_modules/strip-eof": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", @@ -16202,6 +16474,22 @@ "node": ">=0.8.0" } }, + "node_modules/strtok3": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.3.0.tgz", + "integrity": "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==", + "dependencies": { + "@tokenizer/token": "^0.3.0", + "peek-readable": "^4.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/sudo-prompt": { "version": "9.2.1", "resolved": "https://registry.npmjs.org/sudo-prompt/-/sudo-prompt-9.2.1.tgz", @@ -16318,6 +16606,64 @@ "node": ">=10" } }, + "node_modules/tar-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", + "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", + "dependencies": { + "bl": "^1.0.0", + "buffer-alloc": "^1.2.0", + "end-of-stream": "^1.0.0", + "fs-constants": "^1.0.0", + "readable-stream": "^2.3.0", + "to-buffer": "^1.1.1", + "xtend": "^4.0.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/tar-stream/node_modules/bl": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz", + "integrity": "sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==", + "dependencies": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/tar-stream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/tar-stream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/tar-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/tar-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/tar/node_modules/minipass": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", @@ -16488,6 +16834,11 @@ "dev": true, "peer": true }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" + }, "node_modules/tiny-each-async": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/tiny-each-async/-/tiny-each-async-2.0.3.tgz", @@ -16557,6 +16908,11 @@ "node": ">=0.12" } }, + "node_modules/to-buffer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", + "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==" + }, "node_modules/to-data-view": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/to-data-view/-/to-data-view-1.1.0.tgz", @@ -16636,6 +16992,22 @@ "node": ">=0.6" } }, + "node_modules/token-types": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-4.2.1.tgz", + "integrity": "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==", + "dependencies": { + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/totalist": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", @@ -16987,6 +17359,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/unbzip2-stream": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", + "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", + "dependencies": { + "buffer": "^5.2.1", + "through": "^2.3.8" + } + }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", @@ -17263,8 +17644,7 @@ "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "node_modules/utils-merge": { "version": "1.0.1", @@ -18101,8 +18481,7 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/write-file-atomic": { "version": "3.0.3", @@ -18165,8 +18544,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true, - "optional": true, "engines": { "node": ">=0.4" } @@ -18333,7 +18710,6 @@ "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", - "dev": true, "dependencies": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" diff --git a/package.json b/package.json index 2d3e9d8..af71293 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "witsy", "productName": "Witsy", - "version": "1.5.0", + "version": "1.5.1", "description": "Witsy: desktop AI assistant", "repository": { "type": "git", @@ -81,6 +81,7 @@ "markdown-it": "^14.1.0", "markdown-it-mark": "^4.0.0", "mitt": "^3.0.1", + "officeparser": "^4.1.1", "ollama": "^0.5.0", "openai": "^4.32.1", "openai-speech-stream-player": "^1.0.8", diff --git a/src/components/Attachment.vue b/src/components/Attachment.vue index 02bc598..c712c9d 100644 --- a/src/components/Attachment.vue +++ b/src/components/Attachment.vue @@ -2,6 +2,9 @@ diff --git a/src/main/text.ts b/src/main/text.ts index ca093bb..9cc311d 100644 --- a/src/main/text.ts +++ b/src/main/text.ts @@ -1,5 +1,6 @@ import PDFParser from 'pdf2json' +import officeParser from 'officeparser' function getPDFRawTextContent(contents: string): Promise { const pdfParser = new PDFParser(undefined, 1) @@ -14,10 +15,20 @@ function getPDFRawTextContent(contents: string): Promise { }) } +function getOfficeRawTextContent(contents: string): Promise { + return officeParser.parseOfficeAsync(Buffer.from(contents, 'base64')) +} + export function getTextContent(contents: string, format: string): Promise { switch (format) { + case 'txt': + return Promise.resolve(Buffer.from(contents, 'base64').toString('utf-8')) case 'pdf': return getPDFRawTextContent(contents) + case 'docx': + case 'pptx': + case 'xlsx': + return getOfficeRawTextContent(contents) default: return Promise.resolve(contents) } diff --git a/src/services/llm.ts b/src/services/llm.ts index dcd6d91..5b9203d 100644 --- a/src/services/llm.ts +++ b/src/services/llm.ts @@ -9,7 +9,7 @@ import Groq, { isGroqReady } from './groq' import LlmEngine from './engine' export const availableEngines = ['openai', 'ollama', 'anthropic', 'mistralai', 'groq'] -export const textFormats = ['pdf', 'txt'] +export const textFormats = ['pdf', 'txt', 'docx', 'pptx', 'xlsx'] export const imageFormats = ['jpeg', 'jpg', 'png', 'webp'] export const isEngineReady = (engine: string) => { diff --git a/src/services/openai.ts b/src/services/openai.ts index f22ba01..1e6fcf1 100644 --- a/src/services/openai.ts +++ b/src/services/openai.ts @@ -1,11 +1,9 @@ -import { Message, anyDict } from '../types/index.d' +import { Message } from '../types/index.d' import { LLmCompletionPayload, LlmChunk, LlmCompletionOpts, LlmResponse, LlmStream, LlmToolCall, LlmEventCallback } from '../types/llm.d' import { EngineConfig, Configuration } from '../types/config.d' -import { PluginParameter } from '../types/plugin.d' import LlmEngine from './engine' import OpenAI from 'openai' -import Plugin from '../plugins/plugin' import { ChatCompletionChunk } from 'openai/resources' import { Stream } from 'openai/streaming' diff --git a/tests/fixtures/sample.docx b/tests/fixtures/sample.docx new file mode 100644 index 0000000000000000000000000000000000000000..d443a2c5d610240bd8ae4e1b5ac1494b091f9076 GIT binary patch literal 13019 zcmeHuWpEtX(q#)QX0lioGc#MvY%w!4lf`5)vt%(dGcz;G7L!F5^Y+Ypvoqs2u^YR8 zzgH1ml=tMVTb+5~WR|=X7&saL0ssX700;r!@@K6yKmdRb?*IT)02HXEkgbiAv5k}N zCwDtzM{PPcYpV~r;Gh&=0ieM0|K0uvzk&LMF`HfnB$2zsN5t4BWrM@qQYz4Jz9d@3 zBS=h7P?eXsq4w8KR8R$FkXRTiB2wnNO=gwBfSJ`aD`?~fSJIRByh(oXI_B)uD@(hy zK1Nt%TL}!C0wZj`RxIruSki1D48#q6aT7`~av6CU0qAP?fGDcOXcZEVNM`)NXAI`= zcO9Ex$(y8hlyVJ_I0!mlQDEb1msq`R#ECGCRvhQlOcSL8_3F{Wj=hfNvlG0MD4-XO*y6TsNS9yKXa&M{5r;%O5{eG=nKwAH}N-Inc z5<9t`mRq5gvyt^i0a+Lc(#|8YvxbLA8T%a@zK z0Uc`wmg9aqwZFWY@g;xK{u;zQZ-;+yv&yPs()>Gs@o3IRJ6(LybPDpu`s+u$;r+EI z>w7Q&;Oz|zApbX)#E->pItSXBEYRa%fi9`*U~J_`PxssYzpnT{*#G|0^zyhKi#`Up zkLLj|0W%#6t38-`vh;@2YnUsLP?{3bsB6m>ORq0H%gdnJNBUx8vvcv&9*!BJ&fgQX zFR+u8;lFf1FFxq>X+E{P0TP4S3mDxN?Y804_is&JMu{aTM+3su(Z5V#L&V;Prc89B z?okQf?iInB6H!jh8B*XjG?g4SX4lK<~i?K!Ld0IvCOa%SnuE4V|rl zqt|c4)*lW70vx-5dj4l`<#A&`g8}y4L9YQR-n87_@`Afol7wrEF8~pI{$d*FiR-Pd zDshREX}#79hsqSkDPOj=sh%&c5%Zo!Uxyu#4kM~Om#ExI@T<-QkStX#Pw=QPWA!w3 zbgZ(bEo7zUZ~TOFITWWELmCo{xNupF`MyfgM@9$ivka<0SnD{4J0u>sGRb1eXE6*^ zsvx=Eu!dT|-4it0rfq%?V7CS1;!`5zw|k2iKjM&7!_zc+7tbIi9CIL|BK2jOAl#6t zNS>y&J8z(*TR*F;OS?8}j_$RQVDP*;#j#m^Hs>;mx}ym-I{T1

#>z0XUWUPpfGx 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 T!Te5=2ux52z|JnOt^oKy&0OnX literal 0 HcmV?d00001 diff --git a/tests/fixtures/sample.pptx b/tests/fixtures/sample.pptx new file mode 100644 index 0000000000000000000000000000000000000000..d1d5ab7883b9584eaf80947999b42594623836ff GIT binary patch literal 32657 zcmeFZW0a)Lwk=$?ZQHhOn_aeTbXi?Cx@@bXKb|$#oH1jq8KEQ#3Wf#*0R#mE1Vjw9J%ROD4GaV%0S5$x3IqkBE9zkHYG&_h zsOIHp=AzHwX=h7R00u(!9S8*Q_y6zpKR5ytX}XF(nb3x}<=+rw4dH3Oli)xg8sv7y zM-YCuhb(iqo5u)oxLySh4#@(kXOx|qdj6cQwP|&)o+3lfiv{=}n9!CJ0SUnYXKx!+fnU%}tzZ@_{XSg$gQwZxm<)o|;| z#%=m16=;>zd!|jK)c9nSQ5Z6d_aw1OV#ZgAmin}UIP=ooxE`a0FK0|)w|JRT*oJpk zB;u{wC9Hg6o=0O_kz=@h@k77ZcgFVa^1=A^nZHvs>F2}P-djCYw&I22|vzt8j?9%2eFf!kryBn zmJZxaJgJho?ymeDBP06Yu89dj!fi5bhbMcq>*NM_gT96e;rQh~t!-LQdqRv>B9=l< zE(k>!gk+<6J%g$|OECsfuR7*vkz4J)UcBp~32&nCHkbt8Q&(c{182Fen6m9U%C?Gg z+zfw~?-@*kOH@+2F5m_HVlRZcJjQGzRBlpD5#@4}EoPXr#%Gb}>E8)uxd~C#iN9uJ z>f~rfgy;C&S`K5U)EYeElz+-wK6~WPxM|G$&G1U*9(R^RI+S8*4~ey*eLGdwYFA$Q%9{52RMj2Hp@PIx}i;(b9N@VLx+6YKdMb1}sPw+E=v6<|wD zDTz}u^*NTpsm#h{NyJCsvh??5C~Jmz(td1{%7+%96H{ni^YK2*!9VOG4eIxqJZ1A{ zYhXj~6hrn_d0&R;Yl<7vC24Sqe6Oxkb+k!^G88Vi*#G|N*IiDtYXEFf0S*L&{?AYU zf1W!=ZPq@E3C%CBDL}}-6wDDyOukY`l+#L=wD=teF&koZCH1l-vf}-T^RT<){^(5Q%LcfTY8}o?oZvHV5P9GML3&VzVFWT z$C=H*YA*bguuZHIQ-xBTylbd_ zH*Ra+b-?vu??r$q`D4JvKYB~sG`l^rPI~T+on()~tIsZBHiG*>`fk2GVXtEGr+>!| zo|J4_<<}drbFYM5o%Ixcm@ZCT1iH#bAUNgwd05N^MtvvcZ^>%xt-J}+wdkgzpvjJ4 zvl8m5vR9|KOH!4@W=P)HE;D!WtvasJtFSEV{+CD3z-oqs-!fJH;F2DD!7t-)xxik= z7x*Sl-IEO@c`4%P3ap)QX>JWLjiavUQzhdaEK}noO>0pwLQobyqa0rhdMh9VZ-^V*3 zrgPxBm@ed}J?;L4X=gSLfJhEk#6@e$VIdr1<=mz+A*t&MV@6a@lkw}*0A6nPEVicS zu~(p>*BrJrJ&<}ad&7_<-ke&mr;Da5zhj{@(F^}wlO9CdV5Oy~8dJ$)sBt>Brabg} zBM#{;8^2hbp5FR=B+}y`mxV7ovR8Dto7c3yJCt;5#&Wa{W{ z35TCgZ(f=8n&;OFjr86sOHd<;XjqwZ8e?oATT#Q6ykF3Y%8Obcz#pXQrWhGD7|YBe z#208J=}NFrX2N!^xJw%b6;zDzGd)#ww?~nR9eyMohTFz7&bf}hGlAYIQ>?3jyp^;= zXgnZHkG8l~%lOtET7*+%TlVEhdOvY+n1nvSLjXh4Mq2eMiqaj05LMH>L9UD%a%KgO zn1_2NqRkNIUrf%%N>?3bYYBJT_`-J$^+iD(p^aQt5!Jib73FMXCH4QvNHVItIX`J8 zs1k${cnN?0d&Xo1KY4oY%{)Ri8y1=9L`_smIrHH3@xES_!2!bt)eqN>0!R9geCTQ+#|jUkfAQV? zMVNzVrX``m9HP}4H#zEk@P-|+V3!HnS_As*eNpr6&|DS$Mi%20XFl&M)lLmUS0#>L zMdRbbbbBo+6ine6QzlE6_8K=c>s~ z=<=~_38UEO>F+cnd|L(;>%)?FQIi;&cDT25#Rb)>#!gVd$c z98(t*btJ^_mW&-(+14mSx9aLY!!Fyj`)8{rBKv)ZE>@k(-_Fd+p)B0Bgy0M`Ycgv> z2t8N6mzbZe29v}%IL&a8muWY)E)J8Ea+v){0`kESTa^1$VVz8OI0W|}aMvv~8MNlQsr{d$SX^(bh~yfZ|z zinfviyt1lv!Yi7ui^qr|>3Fz1D@LE`nY|Y5BY+Tf%xe9tNA%!Jhc-=V{~S`tCg&}L zNkcI61C%P7Mbi&)MCCRx5km>hEYb(|A&?Fok?ImiW?wl#(y-i9NE>!!?v(MA79G)Y z4mdex{v)wl#mTTHPmuZRdO-BEARv$amAV#Z6K^{KGk=r`-G5}-UyHJwk(Yy;>p$s} zfBeM#zj+LxQeb}mQ7QU2_&+Zxs=r3Cxgq!Ui9Z0x9jrmDlN}VCtY^&zf8Fpbxt7kc&7w74iAg;`3gXJ8@ zTf73tMm&x*nkJ1o7t)qou8BN}@1t^Fu~znAC&bG-IBG3Lsr&UuH|e`cc#TUKLLVQ+ z-LquK-mEqcsfA4F;DYKB(V<5`i`qB}aE0B%#pV-$qW?7t(I!a*A7DU0A}D|A82?G1 z{jVter;z-m(k8TY?bo@`-U+`v5m|O)C!mQ-3+MdwhKG185S}87ItAgg`$v8cGo3h>2Qzm0V+TLw>mLdIAXBEL?b5|gcmqDnA zdXOQ(IM1UvUV_1Tukvv8`qePNYx(MznSZ(ghm94G#O8G=s|&eVhl93X3yS*MHG6g0 z?pDKTh1yRu5Bo#?7wo#qxdJSgN}1?2yk(DX+S_z#;uHlGx|#*4pi-1$Y9ep%6_&5i z)qZd~>yZRs9?r6eN5?T5QbOrMtxl{qAs`~WfHEwiSvb#{f`e4q`?ZqA86K*;`7cA1 zy1egwmKoNP6l%HFvRx$-XhWQetztQctX>+nPU75bo}U-we=(-&7wd3A?Hike7^a=A ztC;MG;H)yG+etPq-txONs{2mc+N|}it*V)^gL!b`^Axk z^C)aOnXRdc_%pfFB8LmE(-i)INf>b)mk@`0J~T7p`;9dpP=uGY8^%)lb^32Hv26m5 z)zYmpTq2oUo*Q?56C%~-OY3A+3SK|C{e|7`D_g`DgVab->><#A5G5{}SBiZBqkgbK zvZ6C*-|%VRj?FYo;UvR~MT;4bZ&o?gIG!(45EBh)uwL3Qo7>4n>Z)V+I>WWklY@7P zSQnqU8G4sIwro@QFT7aQFLT;%b!BoXjS)D0dT{HbU+d@^6vJ%c5-{g}$3LmKhWS7A zdIO08BP7G^R4Z7>h*~_=NnJ`8ML(_v%5Ik_*bVRY=7fVdT#=*=xS*-V(@Psq7!Sw3 zaev`cRM$4C%#vYojz&H-Ld(`Qh3x;DdNXQ%1SK@g zu~RS=8mi~jzzXyRp~F%e8~b!+lb%8tKhlT(@KW*7*Om&#Cc0Fmvol*!MyZQHRJ7S% zNdglWw&Qy&qbtdvuO@p=-t6d8+>TVyGGv3#?Wn^x<^(K_3%r?5O*K>Y$XA`pKlzQ<5h(DG zF&q!p>SIAT3#h()Y=nQqZ_K-y0&#eDH2>u+pk`Zp&LU81kFG9zq@=-W`1e z-RVOSH>m}6=w~@B!%J`E(G2?a+q}ZMFNXd)+Dk1`u;jDtoXR3Mm>XaxXNkXFD1lgne_gJOSF{NhwbUUfWKWrOa*Vd6aeHY5709IjyzfZ zEFKe@oA%jUXx)ZZ+rah92v`$_3(}#$wHh*6mXv-#kbKxJf!g~S3N29!%WHnw0CfPt zVwY%FS{a#)YddlK{MzkeYtXgT9NTPWT;|nUw;O4rTSe=o?!gKBx`_^4ZwH^~j&A2U zjPXQ|d$DXG_z4IQe&$*B)?I8|KlI?A^Jb?Ko6qwRh-KMpTC$N6O-?2EE>5gHh^OT*|Eijl9H{lUgtn?^H!O34&Bo7OsX^VXNs6ZV!Q4Q__i z*F|T)+Ys9}hTM2}bI>VF2{*Mumv8;IM~*|TCv@0?Q;^&~O~{WwKcphUzXlTtEx-|B zk_N&i1MNLK?TVA#zuhQYhwWNXrw1LjT9;tL5ely@74Lu=jwU6xqH>x?QL_nIfjgaGKn;W62A|l=1m1ZO$fHf6U8DVm8<@$vzkC1UrUzW? zgzwamhSj%V(vwR??iNSUc3YTn(a2XXbj4I_-<)8>o+<9V2IoxefNO5x{mx_$1uiD) zHrRmBsT;R5)>u_O{wwtWqte={EB~wMPnIWjXgcwW|nPxwwjP{I1` zc3aEQrOxtX;_J{kqHu$qpFbg`V_1=f?cr3Upl(3-d}d;yrQeT>g!M0+RI1Ze{QX&(;iiqwOvVNhqQd?uD!L^kM2i*+18Z$Eiv-ghcgR| zbH2!l_MxSp-&W&Hv#Ke6*fUcgf@MJ_BZ_onfZ&)d0raelfaDsz@aUNNGI;-MNv2|a z%#a3Ut*XhSeP)RCk}9P|<+wYNBbxcv2}<~C7i<8kmelPtOJa3PC`~nrM zv39ef?o}xF`dN>VVY-Ge4RiRyO+Wnc#$IVW#LLy93*3p0vqYGeR}+E{!WwYaWvJtJ zT;EZS2a#0_)O0i>VIYx6$T>Ou!efhdnWzN&0Q)kf@+@qRfh~W% zvVQC{GgZwA?+&Y4LLh?9g-Tr@lSB+8G%hlrel7fsk#V75XDl&ojo*WfxvQi*XW^2E z7t#}x*zN}mNg)Oy^?6^RXi|N&%y2t|p;(qBl|w|Q;cxuXu-S@o?yhEz?w;qjdwf40 zrjgsDv5y3ExzEixoFGlmqs}s5xQeVf6iMO}nUsDx66VX{V`K>6Ef7QyxJc3y4CUko zd?`3?ex?n%63e4s^hkw*@JFHefSVsl=xW9JT^wf^8|x|h8eL5+ZDTK$p?${AEIHd1 z5$E>-FYt=_cS;U-L}!BoP-hYV6q^1W8L|FFMp~Qp>l|p^gmoTBO*Ttp;>v1DrATI0 zxbZE08v=nev-L{vu?3r^;z8X@Ph5V93CX6sR%>lGsK18_%3Kh7T)Mw4B;*Nn)X-Yc zni{rDZip*>KaZerh;C@h?5H&?4s27XZ73P3XhC-QCGl|8c%K*Gy_WN+?v$|EgnM{` zB_EJFx!J6;xoW#~Y@=-Pp45UG>)&E+mP74FTWcOVv30uQ#@>%Kr=61$%lk5J^0vNM zF)>&$$=PhVfFu@0qly#I!_eU$wSa1jb^@-$D-Zb=FOSElKb%cMp z?RdPxF|%q3RW1=`^tLGe&Tv!BGTsYpx?ARZ*rOOx_)wblz*wwDrmK@fT9w3 zh!dMyNtEP)GqtQ3^`sq8<#=Du(~1&y&diEEsa4J{oAXpFt9g`fv0(aDSRs$2mL6{rlE)knK-|-D)ib!2#6S%g!`TdyD(XGXdfUvdwz9~gci->g9LZd$;k+?T0 z^%N_FEtoI?hS$=e0y!7iEgT*@iBoybt2EP(>S&B=EpOs zuw7v4zcF~=-QTq*wBV?A^>J7s-XbOU50rifOe{y4Hc)A!=OC+#GZh`S^1-Q_zauPPcGh zv^fD)9TyHzg~bXqMTQJyiT&z@1QOwd@(Lx4Hh@^$l9Kp!<72~ACCy%CBYyV4J zxexe=9V)_gx*a5+h=%x3Qu(hEPnPhvvPnJN8h2hxT^l+H3s0K_>*Z5WE=-$Q=^Le= z{L`NRYW>&kvz658Q6)eD+Mxb>(q;QYx))lT&e2kbfs4CL#btbNVrBeyQPfRM~Bf}dpd2r-ISVf zng0C1Qmn2dp60_y{a3$j_4c3+du{Ei-48X3`!1bu(fcSN9S2J z4b3L&orhpu=<6aD69f47@-K3~+9zuZ3?C?eR(W=CBWvhe!OqL9XBf!MPnfha;~x** z-M8sp7&3@g>NcY;9R^hcbt9j&95) zJDw}?0!W8nSX9hAQ?vV*N1&S#I;GGDFe(TZz}c?ELE0 z-2jM}w3l0PPImbc9LDP;5$0Wm(NX@gAO&20pd}2w=2DPHG_syoI-A?`R^o=SI(;?g zAW=qLOhB=PnK&g^NqHd?L{7DdMcCVVuMg`D>T7jU3Ds&8XNq%(0S&xTa;QK7(Ss5k z2_uwn@&VXyY6=1Pdd%Wcm$@&$Wd{w}WbvmC-S0SmIEHVCGOCp(&oaX$D8_#_-}zyQ z6i`}Nk(BdUHV+plVS-sWQ?}cb zFm-T@0V;CpBI#VQX+vN2x$ehL-la}Ol^FWPlrL4xBAGtPLh&RxK}h>p17uTyvIWU6 z_7I}M0#lqZGDcMAsfUlxI(7Ta-C`T&3G7WQR$81Caw!O*kWu$4Cj%WAgZvx_ zw+beDM16P*M3gUnx=@%Pq(bjLPmYWj%k-{z3ql5osMsv2Q54$q=7ORUu2HO2%n%k` zD?f1uP+h>wYua?)hgb0YAlp4IVgNr2m6+m7ZGR@wv`LYHB=5(mu(QQd{|ZZ8N_@o+%mmpu%2}!W2P}4!*9iU}r*>V- zN25lr+Vs4#M#+~3O@i94`dyqkEw?1}1yAzorN!QywV5Pm3(H5gnQXIdKN;?*!{QaA zsc0$t-^|mV7kzUF%MAy|N$5KWilpz$RG(U-tL!D4yfK^FrFFqg%ndoqJE~GzH@)1P zotzEub3T~sCw9z>+h-4io(ZS#251UJ+&tBlCFPeiioNESDCINHG}>O8%Pk(AOelP{ z@HsV7Ed!1AOnEx7!ZKj``cIYsOA_RkJWw^W9Fd$%DHXl zG{CdN#$^nqFD%^$nU583*6ea>-RvvmrPNLWMuV8lIUlx|bHmIb@3dAGx=8vWwhiPdB=hA_iFo3n-Ch!sbY=y6=T*^V zpUqHR@-*+4a&Me!aUZYs$@XzRc0=oTqX|p2L9e1DVV&S_jsA;Qz2l9)lNQxAGEH+H z1*|)o>SHWubg9s~?0bUN1VOp<8EtLb3{?(fRMrOf`J(2nY4*j7 zRo<)+xyL4(G`Usgt4OR7tT1@SjKMX-Tx zapGBm9#?XdcJ0*Q*}o^USHzapf5B8Eb11Yf15T+?Mn)b9xmBn$KXB8EW$$f(*NQBz z->t91u?al1(TXAG#C_8GGZ6)^B|JJ6WA}AY3$#kF-+E$`dL5*)9zx3pG?n$1Y>Z7~ zdn*DuDd>^Jx_8m({U)Z0beZhnX%ssn4tuq7Oo7{*o;nFLGZ}Px7wHuYinkZYXau;SpT)Q&xwkTwy4$K zSIvxKCv^&q+?qLGNU_H8U!=Ko4dx!byI7rwOjW7!tt;3GSApr1*TBA77Y)3X)Gx-B z)Za|teL zB*H(fZC1zjxiKGo3rT~H?+KI2y~1~2cvUTzgZgrHQJHgq4L`Y za#qZ_m^L&d*EiI+dlM6RM}No>wzVJeh-vc5ec1AzgO40XzNx>{P&oq%=LHVjc+!Ue zCFA~JF?gYj>Sv&xW8&52IGt@^McYzKSj5OlD^>PFAKnDcT2qx?>ItfK|`JoDmYi2EpkCQi}8K1;qe*Jm(ErP(I}I$TPINuUXZY zD7@B777%k1OttnY@=mpow)WG)DCLAoV|?5S4V`&p0uft5B3ei9HA@E?iTyB6FQYSYq0>wIEkuM1dQq|IPQ2gyH3VkY87{*o#Y{)us=R8Qz0LRa z{PR5H4a1BGV9+>6AE0km?+q+4VK$ zoDQqfnyLAc8kT!bhTyNh8VNN_--#B?Tlfhex+IpCtoJO|0v;!_ekuDkv5-*9g&t(o z=)g7-wi6Wi5q`D`{QlczM0^=hW**RqdVu}+%LvC`RSJ!D`wb2xKgO8=Kq}~epTDlCZKMPZ+;t`L3b)VyK9XQXQ%O<9#=270_zg0q;sVZvn^ z#JVV)HhkbDP(fapQ>FRDIV5$48AAfWD&{1ktTThfyeAG0tu4{Hqv6xhXGI-q1gMrO zr{VG$H&x57etVb5N%rHFHBrU_*ef&Sat>Q#lyT6wM8z>mBX-$D7Gz;UM6(BLqWz9a zTU~&Oj?LJpvazq>e2Iuordkaf}Qg-kGeVhzfRJdZQ5hbQBLN{?tktQ_tv?7sp>bE02}Y?VQXWbb4%VH!=7j(jr?Cc*J6 zb41NPG!XM;^znmNgYiDg3k>1J&B4MG^5F4iEQo|>E0Mlc9_wUtR92Y73Y-HY57O{O zIxhbP%Ly}bId?|0qqJE4ODGK!9JfU2^up z({P+D)n>tu!C&BLzZ%qDVHeL!YC;~Jmi_&Oxcmx3hQX;t5|s{SiG+6B`#Vw_CbAeuo_ra6xjsFybfkOo~{%#>IhJc7H6~=O9E(AJW97 zw{_M=HcKOg5=qopzqabU_rL8x4@~#N2LWboq5nN2aQ+P=ctq)#;zzWjfp4sgP!I~q z>eV7`$~bX$z{4H1u1$}onMy^vTXzH!+7dC6uxIX0WVo=+Wwj31G$ZKAP0rNXT~9RA zZ)u8dK5*K+?uVlwuV<2-+bkjQ8?I9dtU62wydO^f$Xt5~&I#*k+KKVksc-H|a@OOX zQoHmVE@?D6;OARMC@?{>ABWYP#hvjSW_GjEVbjkiKIBSvyaC%(Ip4hy=yrd&P^_%xY=zUJ44bMl{PF zLJW3kK1b|{c49SvzI?rMB(EybsF#hoq(j8u^jjx<81h2$H)*kT+lW%Ao&h=k(Gpai z)Xpk#vve<6Ew}|0GykUMh9e5#(u%=Z;bPOX*|sW%eu@KRbG>Y>1hTN8m!OIbc3 z7(pYX_-9En-td(i4eGq3v3$*X+c9Si;8xa6*!gBc_2JHW6cHgtQ7YLOBZJZkq^&nl zPY$s&Hakij_>5UuT{~$$<?gPsz}K_G@C%!xRfknQ^ND=lvIlNO7c1I~;tgH4 zmNn(-ejzHPXYpFcK*XDrH#w?8)#ec6OTs-oET-@EX%f<+FmMxFDaUh>6MF&hTM5Sx!0#t|iCpk|`W%sDecJDl(iyum2`n>vq#@?P zZrs@pYF26)pR%jA{=@a<0RPvg$8F4X25)9U%Xzmh-6m0Lp`eV&GR#z}i6pFI@;D`I zaHDkPM04T%CRXoVlgq$cuQlxOX}uWH!T^0fkA&%vqd({95?+nbuW`W% zmo(l+hsM7yK^=g^vD)FJFjy@6V1jSHRpbOf{#^-7ARbPx22dR11+4e~uJ6G07k;%a z|7;B3|6^k)izcotoFtOhAJ_zc^Z<+&8P|MjgL2733-i$2GJKkJk%)pi>|`?huJxg{ ze78%?cs<*eY}l=%SzN!TZ^-s|I^b=j0B2w!Z?Ldjn&goNZH!uoX_ZrIrg}d=3V%&m zI|bMmn)%G7J$rf2(}e=#xdkNVDqx5-QO;ZC(R-#mF=nPN->i26JS4r#SKgT(r#?G1 zW7Hh-Jk@grxiR}4qC=ebUJ18lqp9e%gFn{V<~E2%HoRM)u{eGK(t9PL zwpo>e49lpZeeLKQbrcryUL3S8g$lGkkU5%xAol%;F(lGk5~Yn^o*MS`Bp%O}YjT<~ zb8ueF-Da`H8OkN1MqS^GLdt6&Wx(gFcb9qO6qoL65;a$lP5gL*er}FTmv;^?{~QU+ zTHV~Vy>*A=&>i5NQRVM*HBre}tospu`Xy(|j9A8gBv$2|>pIk6wt-fGk}werQXv~D zStuj|8wqV-Koq(~J7$w?fa9G?Qul%E{71#4L)%iiIN?GwO+(FTR-0!6`yv%mH7ixQ z_)y}Qx_?CEVdFqkg7hX7cs@2sdMXJcNlDVqjETmZ(*}@MV|N8ziAlG~UOSSX&z5Px zlfaX3{uV<1Rzn^+x8F&HM-q z_DSS=?XjRXe&qA-qYeirv_C|k=XrG5ZomxSv&|UI_eu_E3{sYUe9xW^fOP!ioNyoZ zhmF4->ecWsbHazCQMID(l(;tU$J?W$i@s14WrLW8HR@FG>Eji>g!e>=nC6;TLFO9wzt~J3?j{}t71uFaJ zlXx|Vo%7GQ5wQ{&Y0ggzK5%9;d=Ut0RiFw&=;X2=Ay9wXmzMCCy4SjK?iH{K+Zf0T zK0zIS^xWBgfXS>=(hk6Wl{A1@U zOW)*&@!wBiySB|!4}o52Cl|}+uJm$XqItrPs72YWpCJsu7s}ur;vwT0y|1O%b9E5* zJC56QKltvvFcQl8e_uU@(IXN*)2*vp;Ys{XpS2MQ%eY{bM6!4Lnt)^?L4E=CvpO{& zTEC$E2suR8&x|bDHXQS?{-_YTe4ju&`LkM-e^M9!vRawuH@^e<^nxz)?XE9o$jGf; z)LuF{l{uYA4LUc+lhzjq!5w-UOvLO(oQh9z8};j@Z#dsy3fNe}(|l9xMuGPrSKgpCcR3m3(-3KrS!!}FNpG<3GOx{@|ohG zT2Rlxju4gB^;`kH4xJKw+Z>IJT+ZTBs;l+XwZ-Dx6!EE8&hazr%AJP{3_ch?+zLDr z93@n84rt_-H>g#2OL1n`^cDD?fWB9jaQ=1edw$3tg?dAvDUd<*@(cY?kag=pr%Gs< z98Q}+2Cq3gOJ?5Mb2u6vWo>`D%Zzz~@2~J1;`$E(S>DU)Y}S-eMCdniXj){#WOsT8 z{i0DQ!xUt2>4a;gcB3Gp+;}0Nv$o~S_|7Dl_ zV;9^2kUirJKhzl~D~Y&FGBPjHBNYr+q%tq85?QQdnL!FylG9h9RgI49s-m=HHblY1 zNLGY5)&j1y@J_z)@FIi1$bPH?U>8(H?WvO^t(~W!Jv&#u-&BwJVfDaL-dJRN_O{SD z>T|!iTHDiUtN6mgt7xBD%m!3=rI@JdLTu*4lJ$5>Qk(c8K7Yji#_7;blW?=9Ki$Ca z478%oO`3W*QBhPjS?}i|^=dA_ILBu@XO^<;40L+OlECzb?B7F{2!fZ~y%3q&A3x#` zR7)xmx*-fWdUr@e&Jef@zBPq zNYt#oS{~EkI#M2UmVdUf{OhVhDNUu3qsKh4DvxF;or|b^GQqGHV<|Fy$KJvl*05L| zRS8uXNa9R+A{@cHo&c}<|VN5SglbirD`S4zl_{S%t_j-W? zh?-ZaQKi3Y2Let?WJ5B5>>0uZ4z!gpP7T9sWB6!`ZUG+ZEo^W5UY9?%K{SAEaLNd$ zL*e+mpovmhVf>*$Zq9c$8QWDXw_L1{=*|i)bY#dN!g9n;ERr(5j-SGD#8>HRUKXuN zG1j6hRzR-|&IR|I%;OJP=ylDCulfZCg()&^HoT$(#4>(Txe_U^$7}sjJfMo2;+p`v z>Tx!$9WN1oo=3%Z~AdaHH-p%@h7=^7nwr&)am3S6x(6v_h9 zfG?!*V{9Ke5XKD5wIwjbQ8JVtL@7Te@CxC(EW67$AT=sdqHPeGrhe&FP{8-^t)M4N zzY>b5l3)4RY8#`AWYrpV8D6kdi?VWbs)zL@oASy@+f$*KZ%IvIafpvX1sXtxkmQ={ zI#*oEK2h5?7#vPs^Nc5v0F^D;a@wm4;(Oc1@4W)`}qkYCGIs)l4;nlH+)jLPqg<);Qk#_q51B?tMPLelrpQ6 zUGCt+DoH@q0iExOs#O#$Ts{u!5qdA5HvV{bWp`l6sW)#+m(^=n`?mMv>rzGSs3E)1 zdjUI3_-p$V#|}wL=vQT_kZ`VIfjj&aExKV?@J5me{5z}mcZ=S9x(Ysqn%%1#cWU;~ zzla=ejIW#%CfUOFXcB6(0{TVpL*PnICpt^w)Kk;$BBLU;biUG#p+nd{fH4W71kkGHwCC9_{IRccsi0<_}I4 ztU4BdzJn#s7J-Bn73z!9$mD3dhkAVqeLW6$bYU>n;}qzVJno6$6bhv1#$1*Sm2PLm z3<;hpudm7Fc!i3ac8Xri9&@GS@_hHaz1{8maSe4 zmK*)HVnk)mQ5>}6|3wmDS1bY86;u9@x4^+4yP^+6i^Ovk%E~-*;lfdN$CT9k_k>GJ z^xHI=0MFFT^CG{FIg;{+ErOE@4QLmr&9JhKQp6Ptz|qyeX;#Zy!hKo*n$;lyyCTWI z^LjJ=CDk=`9RO0@pRn$e$Z|TyeqpjqHKH93bdDl@v#SvWA9m3W?Q)G4=y4bVLT6) zCHhV!XY@70>@ste=No2+-`|qoFRj&&=&bJLCvljmcB=|N-maI;mKE39Rh!(hAHPdn zcAqD%cp%Y}Nzi$;mmK`Y(4c$A6PdJj=eVO31Osz6oWt?N8= zP__Co)gkUNxOmmNPFs@_y1-#Q8++SNRbr4;or(3U#ZXa|XT92!UUDR=CF>k$%Rm6W z4MM^4xh|bh52A#!4BV<42`tC$ex9H!a%7o|D^M{?JWdKb5Z5$lfa8T&s8iHffeBk*o9=yj zAllXk91%4(X9n-xiq(sL?4*7~$Tn+TMHTuK_7vo88*d17A8%ya$Es!hjw0c}zNVUxC(xSzx7#JLqr`N5mi0>{M|h8H2i zXFd@jxv|EAQt_fJRH9`aBb<@gB}ey|hQNbMpP&gd*6b;pjz9v^76yI#!s57)U?BH& z$tyDsATgw9Udk}X3h7A8a@DDpYlz6QSPUU)KoL5}Nf zw?Ia`^0W`}Wrc}P;!(DItvPMGy$413l#XwNWY)A;LG{2sDY`pLB-)0tGesTYVAhA) zcCEWco#xPYUN`fGwgTPCS-}4SV(TM8Mri6;$wicu`oFgXpN@_N#g~h!YPb>)Q z&)~K&FY|l0US1eVjm|$?3q9?ulmFT(uBmZ(_|w-Tfq0t@BBRyJz=Pf}UDKDzFz4fC zeEh?-Y`pQUVqFT<6|hWqGcBt;QKqt^*=!y3N7o$Fcj~$ojFR(3$yTC0OU`UAV6@no zvwJOO&igw}eAqWvz~s6Pmh4sooYe-$C>MlF>VKToCIDwO;&+Mbk6ALW#jmYMSYFDb znc;R`^{nvpj4d}q%!>pzTt z=$&jjv>g8|lPR{FGuD}pmX;H2(dDm4Xfd3ce=}`GiNzu28L~G5iO@V-R(UFM?*wrGwvSLD&iTg6C0)UuWF| z(?LAJZgh;r)MMYhNwUT0)X z9rd@JeD?@N_$sax4;aqIXq#X8V}Pm~ERF8;@_8z%A={EQ*_RQ!US=hrk!oDjg*~IU z=q0tiaF*3?`{Hx@#$|A7?}eR>ENk-z0x`@6vGfkBH7qYDXHqvOd7YCy#%}L<7=O1{ z%5otHs=~A@goz3|BHe4pK=l#G1Os86mWKR>zGq?d@rI%Kj)(# z6_Qv=W?aklqq3p7>~&nn%bZui-!(Qa}Gl*MKk|dAs)3SU++fJ@FOY zd=p^Mp2ayu#;IJyhapVea#gq{OM45iqL^l@WF-uzyH!D3p%%8^DUI-^X2Td$$x8t8 zaesNx))&D*%A760J6ucXO879yDxb~2Or_}4X5 zVq9N+l3<}WCw}+gxkyZJ0$#lV${Nloj#of@V0o|G7H`e8(@}gQ@WwId5cA^2ln4$oI-b0wJw^LTgzG z6&b(Rs8kU+BTM4%vAv0pNBee7g1!iTcR@>T60jpG2+(I^fU6ilwx*_|>B**Kt4$r) zVA5y$@_l_*dW;$UejKdXyGl#VDhQpw{~k!M)WIu~os8hgUKm1{3s+pPQ`Wd{5KeV5 zVDiZUKBQyirUrg0`uTRxwrAKNukV3QAn%5)^omQGe$E@p=*)siXk2VJ11mLu{=TZX z&K3jYuG+{~Xf8G1e&_mB}y1wbE>Z6b+~t5F&M! z*pQv!M`hEeZq;ud^AslLv&hhUm?k2zM{)Lqe2P4nP`5hBdW)iw!NQJym2=(g?upxO z^YnYoW_o3BlgA&*!JHu`9Woy!f-LxnxjTK>wYOj9yMZ0IOKx+5D|dFN3dYu{JfC70 zSy%hgb)%Jf)_HYpFWgI}yQfN@N5?M3obgcrKh?XmBUeT-FPWww*BxRwG$_RMtno0a zTR3)P%=ze;IfYEy16`xU8cJ^Uf_D3I;bWd|*^jHFPkl@Ge8Z~9Is+3|azE;Yh3qIh zJ8Rz>TEV0oxxmu_jm)}HL^rC5rh-qA_?q>d#g$ik`{fr)X4YzMcQMDegD}CZLI)xd z++NI~8Fciz7A5-&xbKZP?)!PB-np_=d-G+k`sW#kqT9Xd^bR(9^DHxuIF%bUb-Ev3 zUwqs@Ua(?ibb{N?pD`G(#SzM47}$cAOG$LAmRwL zVfef6^GtvzFt(*-vpHK|*jRJhVuSwqe)cE-)QHZ96EdZZ3lI4zS=5D9df(R4L> z(wr=Kasp;~Sq6nV1$>zsu&bcScVc!J20KIvdO9$0s5fk$+o0@RnH)NPI@TN7kmW*CVbyA+Q=9XflrOIVf&e+YS!?hBgNBobr zSgJUr%s064W#U?5mZ?V`5-?UvYaygUu__eR^F&uqd)rRwknZob%dVURwc&?IOmBVV zFOyr9)ey#_!YNW-ia{C-UaYG*QWVRS7%)FoAfTdD+p{Ca38ZP?3>U>nUOR|&IxP3l zz)hh<9WZ3VNsy(PIngCh37mH5j4qLToK)z30uA1uz$+xuK6ZM@G)6&09G9oVlFSk* zEN;jvO10sB;J0*-)4;GYNl-RHS4-wCbwLZQt-Kx$i%Yesodr|ICu)EMeGlJPz z?dmC^+(42Xo2IzxM{QD5_Y*lj7MlqTWg~wx#(~h+DU(+!U3CQ@$Mig_1m^7`i0a(E z+hxub9oTmnoFaJ|4fGHELJNw8l65M`u1Q!ZN3PHIW$mJ(P)D-5C*3NvIO)#ypY_5a z>rs^wFm0uO9Kt1tf)sD%>&~`^`~g|oqtSx?u_HU?*QYAkfBeD{<)aF4V%z`h-xo=9GfJ(c*G%m|^vi zn-9+Bv_s3Mu%ms$lOv?WJl{1uri{pj)yU(6Lr8ta(F>!%XUEr z8FC}B^5xCW?m)fhZ2i*qwCg-_eaxl(oaCXe`RRe0~P(-jqnvWAX zN)z2s+E%Ps5HIK_^;|S8DJOIaEi$key(PQVM*XDsQ|_A~0lJdv4Rst`UBgfK853`L zWh%$J8b0+>M%SD?Rc~7*6=$Z$8Qmk2S;h=C8POUj$}o`k#Z|<+jq+g$DZz)wxOvVj zB%M)8EhtMx>3PDe1sZLibTT2IK88ETWA~@0rF2U-JQGm0XOpI4nK!A|B9?B9Z%arC z&kC`jQn>^~sd~$)BaHVc8Yh|GHg1E|ylBrO^?}s9YR?-O1>eqY&ntNgzCAGKT;HhZ zS@WLHZR%p7U>|?OryVz6+l|BfEH`5=>c0nHj|&TIc%&PvL(206*~YVy9LXq~nYa4u zknt#QUT#d+m^fYKiBx?Mg&uHGYsv^YbkOxu%x54AG~`vNF?6IL3V2|;-%wIy%}c0Ag9 zyH<6Lho#kLBvFmjZ8pt0)W#VbySr}8#pma6%pc9Z&OAZudjc53gIU)>$3q3iO?-e)|M*PsztqD<38%i zJe2i6d0uf$QF&~+KX&?;w-W#QGaT$r?LWtV&c9v?=Z+x$!|vMu^-?%rf&O1#3g=5g z|LaTPe7V?vSSjenCKr?u_f*HHT*|9iXP1nUNcfUBJ6v4&Bw|$r?8PMzS`dTDkp09EP+R;(v+xKhy`dGR zoFohv6_$yZipk9HdU~xNEbb+z+)0{ro1>xsfWZ-FKHyqo`4XbSnGL?)ROfi=r1MGe z0eU<%q93~RjAyo&jPH*38d41#4Z$U<1$-}x-Dh$^Up#7i7afSVEV-qF?^UF-1W34O zYhe=$$9ddU-MdwJE1dc|c|?O&Zf&L32LXg^g-l1s^m`KX^-^{`R~S~Ma%2Mh+K?2L zYd3i)aglndmW3RWA0(%wq0fL2sv@ut!}3m(4tvOUB!Ys_#-b)~o-R04i4}b*Cr3B+ zPZip%Rx0c-1WT>X(cju(`eF`B>RV9YVl&Pkv8cW!+WtgLdoZnfWRsM>E~CD9rdNi4tsdcAU@ z_bsB4cqc1!&4^9)GkdJ*w3W=Iu6g_$gq9rl6ylsP_cK*jk)!1_UN7AYr{Jd{Hj zRZob@A9cZDbVo!P>Gt5xqqqwGnlOENL9&GHTIyEh4T0vIC85epjcn+_LOW%O@X1N% z9A$FN=}C0^^aBXFOcRYOI=zAR=RJ1O!@2Ff2HWZTn@8;_qnkCM-iJHL%HD?ug+u7R z@3N<_9-mCyxq7rQQh2qZ0U4vWYeS9v64vK5N4slxtHipYW^KvJv)n$fSUBV)u<&0P zu`C4#VSJh5q3!n}qZtgtj=y$+_d{Y{Aks{9Pyw_8v@TT)7S4#lU@U#P9F~x0<*$x6 z&S09y`I)$hYhp5;f?Zi7C)CqQO&}yfd^<=kt{I0;4zv@>hFcO;>sxFvA}MSdGw2Um z*T3R7Y7wO-%#~)&?1bJvz-~zI#y6wTFxo}j zmJurafTko;7^olvJGP8$NvX^6&>+XeHzF0hOYa|Y+G+PDc+As9cdEyn|BM z-8!_Z+VjmJAEIErsmGleY4vfC;2FX}7%rkz2;s9LMU6 z-9=h=9E7bSF8e3Srx6513JbdgvX70_7ARtRbP*hJ3r@w&SjI}ZI6&mzT;$hG5>)6T z&$@3)Ey~wvlBD3Tm&qj8l(GIs+Gg21N=LP=m3Zs2@3QPuuptW$1lrCy+U-RpAFKzM z`)S4|6(()uMh@Bw@t|8NB zL(@c#k4^KmY<+ewc3paoId$!#KkZ|EuZ);dcchUupX=rn3Co{z-+0x4^GqvhrCq|Wbl zNJOx|@g`}8E09q8Qe{jRkEcdV$}usyb=_poK+eUfj3soi2FBhq*Qs}@0$aN22@}>z z7Ckb91x zq4J^=Lcc}LQ#1)3>47bV+k=$*>EmXr<(5j`lP{fx9*bMgSGJ#snQZMkZFGQ5Nmnu3 z=a$PBM#e7%TfX+yu)bmyYO(g&Xi2$N-f>mI?sL#1VdTY%QK^L<(H|Z(&;W(VnNWk) zknS{x04vYWHo)u@WMq3+#lhCj5g19GtqpAq%&pig?2JuMM^8V1a1>d{EJdE)bKD($OP#+``ex*~Qh(-NV<=!w? zC8cHM6_u~5UN<$jw6?W(bao944h_E>866v+nVp+o__(;Vyt28q{bgr&Z~x%%EMI5& z{9V4~3@%@|K)w*5PzV(HEMH&**Rzb{LJ_GsknqHBARE}@({MgSA&`iCUfhU!iA!~Z z(9q#68WAn`4Bh5g*3NVGYZ>$Tw{rG9W3YTpfFwZ(=K?`MfFMF35JV(IK#)+7&IAPw zgatc7e00RnzA_IRo=&0y8|FWD;0hcAt={N`j2xftR3&916f{wqY zg{goJ=UGF@(nqH*}a8+Qop_R!Y# zv3=>;rv>?5BXd3Cg)DTh-4;W+-F5wfB}&}$GpIw0dS!bXB?<{Ye^Ej9ZQbx~<m}wXgQQxC6KyU@L!gIn-`4DGgx)Vj~rS zm#O{`JQOt%%#v%Fzfs@Jj%Oyg^Q82}{)o!(Y{jmz%0j4SLoP=E`DyMc=yTn8W5jyMvcjJ$w5rjy=NuFFU4pe-PszvmCTaY=h4LM8m z8?l!gdv>?89~b0D-|iLni$Cn4EAC%qzTtZpr9z@(Pd3N@4hi$38!u_LbdavAm@j_# zCW6Rwg1Z4_DqOGTtHb9L?Qho3XNfMO@L#ZZ(4jqfk?&n#BC5LfI@~qG+Di4Rf8kX< zjn2*-Qejub31{PXVgbuoCbWtMx-P6nN&N_SeG<#u4q}x`EL7?)*zBjZio5ut=|ML` zE{#zN7kHS~kTmD=9S$&*e7qrOIW)l37`NwvqlS4!YywlqZvAmm6jEu~L+L6TeVIW# zUe?7T>qO^Xtq@0_=}-rPY(^Ftn_=(yw`KaR(?Ooeu18rjIIh)LCIV~vRasDzd?oXV zR5xqyP4kL6VIeXU4e?G?itL4Q*`dyfGEp(0k-KdFUsvTWuRvMtS-%H3L~)v z>JIsbx^po%cKu#=p{m_BfFtl4%@t9tx;XxzENYq=)$$8iv2wsXIfIsiRX1zO^)+2$8iA}x0`N9|NWYuJp<2nP(tzK!)twYB{Aas!Lxgu^; z{Gtfa!{V!gK?BLvNqi?X6VfZ*VaRtiZTbq1uFJ1)6BrMC*1YHFjHo+F+V!E78fpGO zI7}ZisC>}9pX9`D_b&}aM~@$e=L7Xt0c?Q(weyzcWMF7ztmfoyW$bv?wlLlRrFex{ zY^FBY)$HUxtKYpURv8R zxiM*7g^{7V?>QJt`*4vWcs3eoC^)&1FV}}Et*jxtdm+eN4x;D5-DZFD-EdXTF%yfI zSjk_zr=NO?O@t2Iei8sTs5n4mjqi8826lF5Is5-m8bFN$l5tZheEbSdt>%h%k;5H? zV=+Qb?EYrQIYChKP_jg?zw@?`nqT%{3ahJ>u!HOF803Y#itt+i(N$!=umHO zbf;O;m7)Pg>%DEGxI&vcT8#7}1!7Z!WO8uy#Okq79cHg&5NSlDU#q)n+mfkyNQs}q zhoG(}O#bMxK?DK!S3)EdwFD1J5y%Fy=Qg?h7g01GzTT|WA7JXm+Potj8}019A5ST= zY%J`0BVJE4;<392+I}=TT4}hjHMoBJy%{4-Rit$p2G%XyF&R1Q^yVn*DRvZHSzP5n zy%I=7m~EQ+;H}*Z!c+{=v(-uzcvnqzZ<*IQeP$KJ>-eLufvSE zo2jr|8gI*so_=3#iOM^osln%3Cz=6{+-v0xo}j~~N(p4ci?gdB&eshpr&-J?^sCik zFA9R&4&sC1PKYujIWimcwF-9N9UNS5i#QeK6q-J)Y90q*M@VdbqrRj5kY70)2NS z(1>tuCR z9{qln>Uu>j>G**NOZA5p+vEcaSYl%)O3)Y;hDEZOMAX7rr4XL*1b>6AgubnF7kgwAm^!9EaN6CzC z&2|?)IR>u3Z&xN5ffdjL{B^ugSXF%6`-|wIDvEyu{4o^akAROrWB4I3A$&skcxoS{U56BTLHnWVSb&Aq5CUj zn1u;E8Qx9kp2a`=f9SaKKZ-wNT6(<<)ZCb%}iA-GEj9xOP)JGi?BcL@%S%h#EC z@6AkRzF+WO^}4rKufFGW)vdE@pV~)N0TJmT00n>s003wK#{20O`VRnrC?o)Y5P*iD z`@-JN$<)rtK-0~^6s-5e)z*e43kiWa6Mz7J{(tSi_ze`t^(nV;;z^y$-n>}jke{m% zM|-*l=^|iN6NR?Nb{2wkGb}CdGeWNMBt8*Y@s|?}&3SMi4Ompz+15aUI~&xo!u-0S zTKW|Hz>cn6#ugH?7)PDg2bm8k#JR`}3?j@@0ZDIb+thg_m!HcQlUooGi~6pnfA7-9 zU+k>ID%2?u?4JF$q^!MwFVOpW3~zOWaNNq0#Y6vOl|v3GjLu_@*jYbUmG+eei?Mp0 z<5i+UGnn}+&x9ceVSzI+stIR)a1o`UnV(9Dm%QS0=xL!&XOuNti{~3p_ZH?4VW+BB z^?XDY1{}H>;(oM{(P@CHbyyW~3Af#lSGyQdwqoC5E6w~h<1!&-`yz!&eMV%Ii83jj zE&Eb#SB9jDAU`X_JHNZoAzvlw>XU8p%8tIOG#kj)!TQdtW1sx(r=5G+wq+zU=wG_GV*TB&P|c5 zMPCa(DfBGTOOG8%mg5aQc}b2=m`fQ*ERyV})2Z}&!T6%|!GwgyUP)kCEq~@t>`PEWjD+?;!CLjY~X%zma-4NXP+b z4_s}4zvINk-qG6F-ro8rd;J?T58xCA_wwJ}iq(}A+Bxx>FzqKL^*)tvrY+Nuik_Bbw(Mg`xjRx{s4RXHYwgL}7 z>V?NlU)7rL)q9QI5=d2TElmoG+4_Cmi`g@jj}SNNkrOg>Ibm!!z{u7(E!)L6sWY_W z#v3yww?XS~*;iQWltl_ug-=C11D$Asx@D++SYp8IS;-Ar2g^ciyA7sO>IR6#WXS+o24Z zMdPJ~yt4~lI(3GjTKbNiydj0R2SQA=w*2`TH4R!;prONyy%X}q0vY|K5S+;bs zX+~p9N|PH|ZsGmFzHto2Y>ncc*Aslg+_jB7VtO~1U6=LoD!8`SCIIm{4vjd4??1YPB(G>v}zPg={9E-A^eYopZGBj~|BDFiUoB+D=Y2stMus zu;uxBzYNvl&zhKFad;Z5a@*Vc-5nVZH;ceZ$TvkgJs>esiEZWuvXOnTwGG3K@Wo?N z6c1r^MH!h1x>(yO;vKBakq@V8H%Rmn3%Q2iH2<+E=9rt;TjW%)QYTpa-tuaF_;}E-KRz~rI!uZGhgzW z3ZU|wjN_6dsDg{K^qa^(*s76eBM+l3L9^dpc|bT<%XLSGR#-Jkfm&9!J%0zg^{(VO3PP> zigYSnu=|zGTcVJC?1)|#`eAMVvdaYUlI(+HquQwNX5S@I+**Rj3qNs`%VrgvH8W8!IE>y-v6aUB zW&}K*Ix#rB8yY>qZL_$}qo0_XO<(%4uSMllgO1!Jz3Y@HYWF*hUnDqK@4~4{|^BAg`lMSt(!3x#~HM(Fuo(W&0`l5?*S}vAMA7(Dw z4hHg#i99QQA(D4J-)2@mJ3Y0nHx>|9H+X%wcMon1*kbd}0=v6pt?r({j;|yai%c$I zq9PN5%^8EMW4Egvba&>-HzHJicRZggn7>k?CTFQJku3h>=3YnKV)7ZjvA=}pN7%n3 z73}0@V+#Jcspn|fTF-Lg-HA^7;9c$aIpQt8VpXKH4oZ?h7f<56{UN?7Z_( zan`4NRx{!qqLM!b9-61u8!ghts50$zEa9fqdr!)xwG&#T71$z1^bLvrM?qo1aHy$k z7khKBn2&tPEc)43vwm7%RlYXs%@i`)nm0UhdjWPykaQgixku~+q0^n5Iy&RIZ{a3^uy#%>g^s>j`Ca!!IR~H1rmx9UK*hQwO)7VR z8Pym;Z4ok4ISMR1)Lod7S^s<+QlshnxRXIefurgLfxRTyTZnBbr40DPGm>V>+!^A z$WFa9R#R*fXuK3jLZD81FhAU@;i|{OW~iRzRenuCtq6-T&&ArXq2Ry*gN4%lZ~$?kRpF=)PO*JJoE$~ z76@e=O}@|j8=`+=pEJX%=U-6)0DQWihwb0k$H~If))e^Lo$F_0@9GRhQq&T)pmyJj z_v=meZ+RFA4(Icq?Yl3>>#fPk#RcjQI*6+`uswW$u{o~bhhRCP;J4t;9ExJ$i&D21 z$z@2dbRR72D4nt#8rpOx9%JkRf(j@vS ztk`u|VOo`+eWAW=@_|f6c1o9U+spA_HFAFiktjNeCQ4=P!M=O6`u6i`R&&nq^+y>o zNZz^*_#ot3MIcW;D;(drTqnTC(6kDP-=Berg!Q zu;OdZX{l)w+~U19njf_nPrkd_=lLL%ZPeCEp&DqvyX4_LeK5}6_`vjH1E*PZr@A#d zfIl;MWX+Rg-llqXfU+roT|mgGRcIh@k$mG20ePe>;)zHHk4Mic0=%DaP3 zaMu%&XnIrRb9XmUyLcPt{>mzVMgQ)y=%s-V>=@{CaYt?Ntm+&oBVn14oG?VWo6)@= zLKSLDOIr{6eWr>eFMENGAEv@lr4VvuK$KJ zlw*l&vv??A41f!J!-%P(-7_ju_bUEvBv-S;fCw~XeQAU>;8b3Z6&1hSB7-}Pxsd@~ zP4>QX2>q?_ym_3Cvq&D#H%0^IQ!*(p#dN0R;m<^}0mHNr-+Msb5y)Gc*v!(nDRC(C+x8pHWP8cdg~uo3OM z$su@48`!?#bj_vP#i!2fzCY^rUJythbmNJ)Ebz`TPfoMbn{|j*lIz)eFS6Li8OF%@ z5^CD+yK@bBc9#}Wd|^MNmwB2`ky0)8bnl7OS9OJHZSjNc^IK@Fcs#JbhzAtG~SX6Ln zEDcz1;eQP(d-8GKNe_ZmSB}MS#$Pjl#A6MiGh|B2qJLSBjNf+~5WMg*V!D^5wv5Df z8HG8#*!^){&yUcLgzQKyH*QuRakXJziCPqzDuG`p^@|+Omc0Y22k^ecXduk?*saua z*xD&j9Nafo&D18?W2VePHfl28HbK0iAbABwnT;$uB6&xI2Yc7-%ph~Cf$nJD5^D4> zPfxh8QZnK!745ElEs+wh+zb&gYadQ`(|nt-F9;o^OOxMYQUTNUky?P*tcIXjgOme8B$2e3@Q~_K!f{geLTm zQ4_w96n{x;o(pne`gT0(gME8Q-1e304VS_Jt(wv)+Pa~GOm1$jb zyw6*0(1U@j|BR=9gU8eHS#jL9OZKs;y=aWU&(&0yfyFqC!4m{Jk%8cy3zULx+{ zqRdE1jg(xp`EanUCb>Ac#ac)s~{~KNZ_lp=-|8R<*wGBNzRB4d*3SEhB>?t^)Jcsqe=K>>FVIbFhL5q4vBoUO#)B!9Md@ z&nv8$E<9!Ym+V4&X6SRtque4yowXXG2AheBW+6-`bx-6gHSdzx@zM?6H4SSCZUl9? zJ%tj8r>2l-Piqgq-kX8ASN2I&A~z5X_D);F3OO3%rztD#0+g&rq;&OcmB-(e*qKe@ z^ksa{F#@rC{a#I1)q@;3Dk#)H;0RnxW{ZFM-6235;@q@&6`}8i$A?1HGEb_tQ(bRh z$?sk*|7D~AIa_)O_j-9#Tt({baCfsOX`3aU?&hLc(owoS&B}n^R61E?(VXY1>eB`O zMRBC%J9`>vv^X0jGOUaQht=O`xOlwHsB+Y9!ae@^G-aU3bXDr=!X{Nc=TfE^<%h0_ zvQbJv>Hax+ynYK`0q<=W*i=XD*VmNXp<)`jLSzdMlm$781aagUxHJbA^1 ztLmhG95cZdrlw9{;BVb;Y@ZgZWWCCX7kGeqi!0$GQ?rU)6QYsAWa^hEN4O%kGxhS+Wkd#BD zusA>UH^;VJrHI_>u4Jwes0rY+lDxfq{AFDVf$CD*S3qZb}(u zMxD4w3ecl3L_4AkWF%`#hjMR~#QMUI!sK-@%$sc6ka~8gbVh%EYwOy!$Vk%A zAWi4!Yfkr*TKcEw4)zm8HWERAMnvo?&-_;EhI?ekG34+;)iy&&gsi4vD67QrR*qe+s*#Foi(6Bj`H5^l8%jm7?=HrR*mjemIQhi!m9_Ck z0$oN#5qd_URFf!mnM`5T9MW!JB~RH1u(EPQ1;sd1PMpAq_cy=)axSZ=0#jm_AL?h* z?v_(xE7#&4=6R{k+^aa`Tdr`73C+rr$B?^ZWW227)R(q0mbbzWHZRVJO897iu(~xn zvz53>VoGA3lb104Q_=dn$`P#rXWw;(}IL`}v6^gAz%WD?mitBHxxKdAwh9XD|h&%_?2>S*r(27>GzP5)J?`k%}Pe|6z81}bed4?7l>Zg8-N zYEFl06i1zVcs(3D+sPvAk@oqF{ZNKW7xll}R-1`lbc+O0Z_Y&laVjvHp|3t^`Nc3x zKVZ(QQ*A(Kve90qM4nXZu*@ZTSb!WxybjYEv}&#Ldz@{}sqBj_Ecd!~2VpP02)#35 z@3}gKThQ@}HicZeSjwYPmg`I*s3ql48Ip|PyN52&GOFOM_iHxdlOOYT9;6u+h(jES zAFDxp8gas_eDt^Cqct$5CY#4t_LBx?)F|gr*-$`ZylSy9F3&rpmJ;fW>%vc}^QEMj z+?(N?h~t(TPpY#jn$W3`$B3#0sVwKIZu?7adutIcDw?VcbcCCLU{3_srPXLqa^-Q# z{Jgz(f#RwYLFX7}^E?AMCfDSd#{t1j{bGj*&KgVCeF&Kx6a5OA>c+)NSlmBLhpqar z(W2ndM*)`)@%|irMh*`D3qE+%{i~(LDA~{ctVP)`4t<>zC0~6eh}kq zuAB$@RKXBj9KAcGQZq6>8mKRtRj`kf&gQOY;{T=HQ7sgq3B4M2EiZgP^+YTT zLh6bA#I?8`Uv6$YOvNfzqP?B?b2o7Y>j-yOe4L5St}k)j9ylNUGvc7-_r77?$t5pN z`$z5xFHPOpj-XrC#lY1bRmBfQ8TV6FIA%-PiSt}$h9%AITg49szl^I)=7@h*v z{n7|!gNvuF4~hvy7%($s%Z8kO zYn_5CgB35X7Hnva#DWvSE73+P=?*oBi@e}Leo;Y{t2U2E^=NlV6!}}JurH^SF3VJ; zi&Jj;nzO_Nlb5KqD) b$^2jYqN)Nie02c;4EV { test('OpenAI addImageToPayload', async () => { const openAI = new OpenAI(store.config) const message = new Message('user', 'text') - message.attachFile({ type: 'image', url: '', format:'png', contents: 'image', downloaded: true }) + message.attachFile({ url: '', format:'png', contents: 'image', downloaded: true }) const payload: LLmCompletionPayload = { role: 'user', content: message } openAI.addImageToPayload(message, payload) expect(payload.content).toStrictEqual([ diff --git a/tests/unit/text.test.ts b/tests/unit/text.test.ts new file mode 100644 index 0000000..cb98a23 --- /dev/null +++ b/tests/unit/text.test.ts @@ -0,0 +1,34 @@ + +import { getTextContent } from '../../src/main/text' +import { expect, test } from 'vitest' +import fs from 'fs' + +test('TXT', async () => { + const contents = fs.readFileSync('./tests/fixtures/sample.txt', 'base64') + const text = await getTextContent(contents, 'txt') + expect(text).toContain('Hello from TEXT') +}) + +test('PDF', async () => { + const contents = fs.readFileSync('./tests/fixtures/sample.pdf', 'base64') + const text = await getTextContent(contents, 'pdf') + expect(text).toContain('Hello from PDF') +}) + +test('Word', async () => { + const contents = fs.readFileSync('./tests/fixtures/sample.docx', 'base64') + const text = await getTextContent(contents, 'docx') + expect(text).toContain('Hello from Word') +}) + +test('PowerPoint', async () => { + const contents = fs.readFileSync('./tests/fixtures/sample.pptx', 'base64') + const text = await getTextContent(contents, 'pptx') + expect(text).toContain('Hello from PowerPoint') +}) + +test('Excel', async () => { + const contents = fs.readFileSync('./tests/fixtures/sample.xlsx', 'base64') + const text = await getTextContent(contents, 'xlsx') + expect(text).toContain('Hello from Excel') +})