From a304fb5435f20622d28c01666e12f713412f3215 Mon Sep 17 00:00:00 2001 From: chengyao Date: Thu, 5 Mar 2026 17:10:03 +0800 Subject: [PATCH 1/2] update --- pnpm-lock.yaml | 362 +++++++----------- pnpm-workspace.yaml | 2 + src/i18n/en-US.ts | 15 + src/i18n/zh-CN.ts | 14 + .../tools/Media/ImageBase64Converter.tsx | 335 ++++++++++++++++ src/pages/tools/Media/index.tsx | 9 + 6 files changed, 511 insertions(+), 226 deletions(-) create mode 100644 pnpm-workspace.yaml create mode 100644 src/pages/tools/Media/ImageBase64Converter.tsx diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c07c336..c7ba598 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -32,6 +32,9 @@ importers: '@milkdown/react': specifier: ^7.18.0 version: 7.18.0(@codemirror/language@6.12.1)(@codemirror/state@6.5.4)(@codemirror/view@6.39.11)(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.5)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3) + '@monaco-editor/react': + specifier: ^4.7.0 + version: 4.7.0(monaco-editor@0.55.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@tailwindcss/vite': specifier: ^4.1.18 version: 4.1.18(vite@7.3.1(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)) @@ -110,21 +113,30 @@ importers: react-dom: specifier: ^19.2.3 version: 19.2.3(react@19.2.3) + react-draggable: + specifier: ^4.5.0 + version: 4.5.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) react-intl: specifier: ^8.0.11 version: 8.0.11(@types/react@19.2.7)(react@19.2.3)(typescript@5.9.3) react-qrcode-logo: specifier: ^4.0.0 version: 4.0.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react-rnd: + specifier: ^10.5.2 + version: 10.5.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) react-router-dom: specifier: ^7.12.0 version: 7.12.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) tailwindcss: specifier: ^4.1.18 version: 4.1.18 - vanilla-jsoneditor: - specifier: ^3.11.0 - version: 3.11.0 + ulid: + specifier: ^3.0.2 + version: 3.0.2 + uuid: + specifier: ^13.0.0 + version: 13.0.0 xlsx: specifier: ^0.18.5 version: 0.18.5 @@ -692,10 +704,6 @@ packages: resolution: {integrity: sha512-fNxRUk1KhjSbnbuBxlWSnBLKLBNun52ZBTcs22H/xEEzM6Ap81ZFTQ4bZBxVQGQgVY0xugKGoRcCbaKjLQ3XZA==} engines: {node: '>=6'} - '@fortawesome/free-regular-svg-icons@7.1.0': - resolution: {integrity: sha512-0e2fdEyB4AR+e6kU4yxwA/MonnYcw/CsMEP9lH82ORFi9svA6/RhDyhxIv5mlJaldmaHLLYVTb+3iEr+PDSZuQ==} - engines: {node: '>=6'} - '@fortawesome/free-solid-svg-icons@7.1.0': resolution: {integrity: sha512-Udu3K7SzAo9N013qt7qmm22/wo2hADdheXtBfxFTecp+ogsc0caQNRKEb7pkvvagUGOpG9wJC1ViH6WXs8oXIA==} engines: {node: '>=6'} @@ -755,10 +763,6 @@ packages: peerDependencies: jsep: ^0.4.0||^1.0.0 - '@jsonquerylang/jsonquery@5.1.1': - resolution: {integrity: sha512-Fj4SoA6Ku09EF+t7OEI8QLipA2A+fJCdEOwnDWG84o5jXMRjkcN5NCMH7kFZb5fP62xz914XV5LBOiDdiUXObg==} - hasBin: true - '@lezer/common@1.5.0': resolution: {integrity: sha512-PNGcolp9hr4PJdXR4ix7XtixDrClScvtSCYW3rQG106oVMOOI+jFb+0+J3mbeL/53g1Zd6s0kJzaw6Ri68GmAA==} @@ -894,6 +898,16 @@ packages: '@milkdown/utils@7.18.0': resolution: {integrity: sha512-+o/1sky+QwbS0Y92HthTupMFziJKhZUgF7IBS55Ft4Wjt63kX8PHaLC9KtewNawpzyM/CjPJ9ySCIa+C/06Bsg==} + '@monaco-editor/loader@1.7.0': + resolution: {integrity: sha512-gIwR1HrJrrx+vfyOhYmCZ0/JcWqG5kbfG7+d3f/C1LXk2EvzAbHSg3MQ5lO2sMlo9izoAZ04shohfKLVT6crVA==} + + '@monaco-editor/react@4.7.0': + resolution: {integrity: sha512-cyzXQCtO47ydzxpQtCGSQGOC8Gk3ZUeBXFAxD+CWXYFo5OqZyZUonFl0DwUlTyAfRHntBfw2p3w4s9R6oe1eCA==} + peerDependencies: + monaco-editor: '>= 0.25.0 < 1' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + '@ocavue/utils@1.3.1': resolution: {integrity: sha512-jd8D6fUdkspMUYr2EE8mTj1GgPivXeHMyPeI2S9v7DEJcNN1DN2Wo/7xZ5H7vUycgfpr8ucv63aKh3NKURHc1Q==} @@ -1190,13 +1204,6 @@ packages: react: '>=16.9.0' react-dom: '>=16.9.0' - '@replit/codemirror-indentation-markers@6.5.3': - resolution: {integrity: sha512-hL5Sfvw3C1vgg7GolLe/uxX5T3tmgOA3ZzqlMv47zjU1ON51pzNWiVbS22oh6crYhtVhv8b3gdXwoYp++2ilHw==} - peerDependencies: - '@codemirror/language': ^6.0.0 - '@codemirror/state': ^6.0.0 - '@codemirror/view': ^6.0.0 - '@rolldown/pluginutils@1.0.0-beta.53': resolution: {integrity: sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==} @@ -1342,14 +1349,6 @@ packages: resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} engines: {node: '>=10'} - '@sphinxxxx/color-conversion@2.2.2': - resolution: {integrity: sha512-XExJS3cLqgrmNBIP3bBw6+1oQ1ksGjFh0+oClDKFYpCCqx/hlqwWO5KO/S63fzUo67SxI9dMrF0y5T/Ey7h8Zw==} - - '@sveltejs/acorn-typescript@1.0.8': - resolution: {integrity: sha512-esgN+54+q0NjB0Y/4BomT9samII7jGwNy/2a3wNZbT2A2RpmXsXwUt24LvLhx6jUq2gVk4cWEvcRO6MFQbOfNA==} - peerDependencies: - acorn: ^8.9.0 - '@svta/common-media-library@0.17.4': resolution: {integrity: sha512-nP/KThzQW5FZKdc9V7ICTa9/A7xGw66VQoLPYOEwwMZTTrISp1zIQAX4KAYJw2PN/VPnxJQJXIYbzZTXgMHctw==} engines: {node: '>=20'} @@ -1683,9 +1682,6 @@ packages: ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - ajv@8.17.1: - resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} - ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -1733,10 +1729,6 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - aria-query@5.3.2: - resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} - engines: {node: '>= 0.4'} - artplayer@5.3.0: resolution: {integrity: sha512-yExO39MpEg4P+bxgChxx1eJfiUPE4q1QQRLCmqGhlsj+ANuaoEkR8hF93LdI5ZyrAcIbJkuEndxEiUoKobifDw==} @@ -1765,10 +1757,6 @@ packages: axios@1.13.2: resolution: {integrity: sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==} - axobject-query@4.1.0: - resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} - engines: {node: '>= 0.4'} - bail@2.0.2: resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} @@ -1918,6 +1906,10 @@ packages: clone-response@1.0.3: resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==} + clsx@1.2.1: + resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==} + engines: {node: '>=6'} + clsx@2.1.1: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} @@ -1925,13 +1917,6 @@ packages: codem-isoboxer@0.3.10: resolution: {integrity: sha512-eNk3TRV+xQMJ1PEj0FQGY8KD4m0GPxT487XJ+Iftm7mVa9WpPFDMWqPt+46buiP5j5Wzqe5oMIhqBcAeKfygSA==} - codemirror-wrapped-line-indent@1.0.9: - resolution: {integrity: sha512-oc976hHLt35u6Ojbhub+IWOxEpapZSqYieLEdGhsgFZ4rtYQtdb5KjxzgjCCyVe3t0yk+a6hmaIOEsjU/tZRxQ==} - peerDependencies: - '@codemirror/language': ^6.9.0 - '@codemirror/state': ^6.2.1 - '@codemirror/view': ^6.17.1 - codemirror@6.0.2: resolution: {integrity: sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==} @@ -2090,16 +2075,9 @@ packages: detect-node@2.1.0: resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} - devalue@5.6.2: - resolution: {integrity: sha512-nPRkjWzzDQlsejL1WVifk5rvcFi/y1onBRxjaFMjZeR9mFpqu2gmAZ9xUB9/IEanEP/vBtGeGganC/GO1fmufg==} - devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} - diff-sequences@29.6.3: - resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dijkstrajs@1.0.3: resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==} @@ -2252,9 +2230,6 @@ packages: jiti: optional: true - esm-env@1.2.2: - resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==} - espree@10.4.0: resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2263,9 +2238,6 @@ packages: resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} engines: {node: '>=0.10'} - esrap@2.2.1: - resolution: {integrity: sha512-GiYWG34AN/4CUyaWAgunGt0Rxvr1PTMlGC0vvEov/uOQYWne2bpN03Um+k8jT+q3op33mKouP2zeJ6OlM+qeUg==} - esrecurse@4.3.0: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} engines: {node: '>=4.0'} @@ -2309,9 +2281,6 @@ packages: fast-png@6.4.0: resolution: {integrity: sha512-kAqZq1TlgBjZcLr5mcN6NP5Rv4V2f22z00c3g8vRrwkcqjerx7BEhPbOnWCPqaHUl2XWQBJQvOT/FQhdMT7X/Q==} - fast-uri@3.1.0: - resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} - fd-slicer@1.1.0: resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} @@ -2551,9 +2520,6 @@ packages: immediate@3.0.6: resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} - immutable-json-patch@6.0.2: - resolution: {integrity: sha512-KwCA5DXJiyldda8SPha1zB+6+vbEi5/jRRcYii/6yFXlyu9ZjiSH/wPq8Ri2Hk8iGjjTMcHW3Z21S4MOpl7sOw==} - import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} @@ -2613,9 +2579,6 @@ packages: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} - is-reference@3.0.3: - resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} - isarray@1.0.0: resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} @@ -2642,10 +2605,6 @@ packages: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true - jmespath@0.16.0: - resolution: {integrity: sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==} - engines: {node: '>= 0.6.0'} - js-binary-schema-parser@2.0.3: resolution: {integrity: sha512-xezGJmOb4lk/M1ZZLTR/jaBHQ4gG/lqQnJqdIv4721DMggsa1bDVlHXNeHYogaIEHD9vCRv0fcL4hMA+Coarkg==} @@ -2671,12 +2630,6 @@ packages: json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - json-schema-traverse@1.0.0: - resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - - json-source-map@0.6.1: - resolution: {integrity: sha512-1QoztHPsMQqhDq0hlXY5ZqcEdUzxQEIxgFkKl4WUp2pgShObl+9ovi4kRh2TfvAfxAoHOJ9vIMEqk3k4iex7tg==} - json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} @@ -2702,10 +2655,6 @@ packages: engines: {node: '>=18.0.0'} hasBin: true - jsonrepair@3.13.2: - resolution: {integrity: sha512-Leuly0nbM4R+S5SVJk3VHfw1oxnlEK9KygdZvfUtEtTawNDyzB4qa1xWTmFt1aeoA7sXZkVTRuIixJ8bAvqVUg==} - hasBin: true - jspdf@4.0.0: resolution: {integrity: sha512-w12U97Z6edKd2tXDn3LzTLg7C7QLJlx0BPfM3ecjK2BckUl9/81vZ+r5gK4/3KQdhAcEZhENUxRhtgYBj75MqQ==} @@ -2820,9 +2769,6 @@ packages: localforage@1.10.0: resolution: {integrity: sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==} - locate-character@3.0.0: - resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==} - locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} @@ -2858,6 +2804,10 @@ packages: longest-streak@3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + lowercase-keys@2.0.0: resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==} engines: {node: '>=8'} @@ -2935,9 +2885,6 @@ packages: mdast-util-to-string@4.0.0: resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} - memoize-one@6.0.0: - resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==} - micromark-core-commonmark@2.0.3: resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} @@ -3103,9 +3050,6 @@ packages: napi-build-utils@2.0.0: resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==} - natural-compare-lite@1.4.0: - resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} - natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} @@ -3127,6 +3071,10 @@ packages: resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} engines: {node: '>=10'} + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + object-keys@1.1.1: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} @@ -3254,6 +3202,9 @@ packages: resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} engines: {node: '>=10'} + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + prosemirror-changeset@2.3.1: resolution: {integrity: sha512-j0kORIBm8ayJNl3zQvD1TTPHJX3g042et6y/KQhZhnPrruO8exkTgG8X+NRpj7kIyMMEx74Xb3DyMIBtO0IKkQ==} @@ -3342,6 +3293,12 @@ packages: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true + re-resizable@6.11.2: + resolution: {integrity: sha512-2xI2P3OHs5qw7K0Ud1aLILK6MQxW50TcO+DetD9eIV58j84TqYeHoZcL9H4GXFXXIh7afhH8mv5iUCXII7OW7A==} + peerDependencies: + react: ^16.13.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.13.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-cropper@2.3.3: resolution: {integrity: sha512-zghiEYkUb41kqtu+2jpX2Ntigf+Jj1dF9ew4lAobPzI2adaPE31z0p+5TcWngK6TvmWQUwK3lj4G+NDh1PDQ1w==} peerDependencies: @@ -3352,6 +3309,18 @@ packages: peerDependencies: react: ^19.2.3 + react-draggable@4.4.6: + resolution: {integrity: sha512-LtY5Xw1zTPqHkVmtM3X8MUOxNDOUhv/khTgBgrUvwaS064bwVvxT+q5El0uUFNx5IEPKXuRejr7UqLwBIg5pdw==} + peerDependencies: + react: '>= 16.3.0' + react-dom: '>= 16.3.0' + + react-draggable@4.5.0: + resolution: {integrity: sha512-VC+HBLEZ0XJxnOxVAZsdRi8rD04Iz3SiiKOoYzamjylUcju/hP9np/aZdLHf/7WOD268WMoNJMvYfB5yAK45cw==} + peerDependencies: + react: '>= 16.3.0' + react-dom: '>= 16.3.0' + react-intl@8.0.11: resolution: {integrity: sha512-j30yAvd2HrTgqUvzdzHUlHppF479LlIiE5P+M5iw8C5R4TRcpRaNHOXmBVlkSt2W32avLW46qfTwXG1BAjTqWg==} peerDependencies: @@ -3378,6 +3347,12 @@ packages: resolution: {integrity: sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==} engines: {node: '>=0.10.0'} + react-rnd@10.5.2: + resolution: {integrity: sha512-0Tm4x7k7pfHf2snewJA8x7Nwgt3LV+58MVEWOVsFjk51eYruFEa6Wy7BNdxt4/lH0wIRsu7Gm3KjSXY2w7YaNw==} + peerDependencies: + react: '>=16.3.0' + react-dom: '>=16.3.0' + react-router-dom@7.12.0: resolution: {integrity: sha512-pfO9fiBcpEfX4Tx+iTYKDtPbrSLLCbwJ5EqP+SPYQu1VYCXdy79GSj0wttR0U4cikVdlImZuEZ/9ZNCgoaxwBA==} engines: {node: '>=20.0.0'} @@ -3438,10 +3413,6 @@ packages: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} - require-from-string@2.0.2: - resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} - engines: {node: '>=0.10.0'} - require-main-filename@2.0.0: resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} @@ -3578,6 +3549,9 @@ packages: resolution: {integrity: sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==} engines: {node: '>= 6'} + state-local@1.0.7: + resolution: {integrity: sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==} + string-convert@0.2.1: resolution: {integrity: sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==} @@ -3622,10 +3596,6 @@ packages: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} - svelte@5.46.4: - resolution: {integrity: sha512-VJwdXrmv9L8L7ZasJeWcCjoIuMRVbhuxbss0fpVnR8yorMmjNDwcjIH08vS6wmSzzzgAG5CADQ1JuXPS2nwt9w==} - engines: {node: '>=18'} - svg-pathdata@6.0.3: resolution: {integrity: sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==} engines: {node: '>=12.0.0'} @@ -3681,6 +3651,9 @@ packages: peerDependencies: typescript: '>=4.8.4' + tslib@2.6.2: + resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} @@ -3711,6 +3684,10 @@ packages: resolution: {integrity: sha512-LbBDqdIC5s8iROCUjMbW1f5dJQTEFB1+KO9ogbvlb3nm9n4YHa5p4KTvFPWvh2Hs8gZMBuiB1/8+pdfe/tDPug==} hasBin: true + ulid@3.0.2: + resolution: {integrity: sha512-yu26mwteFYzBAot7KVMqFGCVpsF6g8wXfJzQUHvu1no3+rRRSFcSV2nKeYvNPLD2J4b08jYBDhHUjeH0ygIl9w==} + hasBin: true + undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} @@ -3761,11 +3738,9 @@ packages: utrie@1.0.2: resolution: {integrity: sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==} - vanilla-jsoneditor@3.11.0: - resolution: {integrity: sha512-/Zw09Yv8Q2i1yC2q5cDsNtFpyFOQu1Aci4u8dsHwyzhueWnibCMteZz6n1aE9+RGgU92Ni5K4cXTMDUowJPE0A==} - - vanilla-picker@2.12.3: - resolution: {integrity: sha512-qVkT1E7yMbUsB2mmJNFmaXMWE2hF8ffqzMMwe9zdAikd8u2VfnsVY2HQcOUi2F38bgbxzlJBEdS1UUhOXdF9GQ==} + uuid@13.0.0: + resolution: {integrity: sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==} + hasBin: true verror@1.10.1: resolution: {integrity: sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==} @@ -3908,9 +3883,6 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - zimmerframe@1.1.4: - resolution: {integrity: sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==} - zip-stream@4.1.1: resolution: {integrity: sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==} engines: {node: '>= 10'} @@ -4629,10 +4601,6 @@ snapshots: dependencies: '@fortawesome/fontawesome-common-types': 7.1.0 - '@fortawesome/free-regular-svg-icons@7.1.0': - dependencies: - '@fortawesome/fontawesome-common-types': 7.1.0 - '@fortawesome/free-solid-svg-icons@7.1.0': dependencies: '@fortawesome/fontawesome-common-types': 7.1.0 @@ -4689,8 +4657,6 @@ snapshots: dependencies: jsep: 1.4.0 - '@jsonquerylang/jsonquery@5.1.1': {} - '@lezer/common@1.5.0': {} '@lezer/cpp@1.1.5': @@ -5073,6 +5039,17 @@ snapshots: transitivePeerDependencies: - supports-color + '@monaco-editor/loader@1.7.0': + dependencies: + state-local: 1.0.7 + + '@monaco-editor/react@4.7.0(monaco-editor@0.55.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@monaco-editor/loader': 1.7.0 + monaco-editor: 0.55.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + '@ocavue/utils@1.3.1': {} '@pkgjs/parseargs@0.11.0': @@ -5430,12 +5407,6 @@ snapshots: react: 19.2.3 react-dom: 19.2.3(react@19.2.3) - '@replit/codemirror-indentation-markers@6.5.3(@codemirror/language@6.12.1)(@codemirror/state@6.5.4)(@codemirror/view@6.39.11)': - dependencies: - '@codemirror/language': 6.12.1 - '@codemirror/state': 6.5.4 - '@codemirror/view': 6.39.11 - '@rolldown/pluginutils@1.0.0-beta.53': {} '@rollup/rollup-android-arm-eabi@4.55.1': @@ -5515,12 +5486,6 @@ snapshots: '@sindresorhus/is@4.6.0': {} - '@sphinxxxx/color-conversion@2.2.2': {} - - '@sveltejs/acorn-typescript@1.0.8(acorn@8.15.0)': - dependencies: - acorn: 8.15.0 - '@svta/common-media-library@0.17.4': {} '@szmarczak/http-timer@4.0.6': @@ -5904,13 +5869,6 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 - ajv@8.17.1: - dependencies: - fast-deep-equal: 3.1.3 - fast-uri: 3.1.0 - json-schema-traverse: 1.0.0 - require-from-string: 2.0.2 - ansi-regex@5.0.1: {} ansi-regex@6.2.2: {} @@ -6052,8 +6010,6 @@ snapshots: argparse@2.0.1: {} - aria-query@5.3.2: {} - artplayer@5.3.0: dependencies: option-validator: 2.0.6 @@ -6080,8 +6036,6 @@ snapshots: transitivePeerDependencies: - debug - axobject-query@4.1.0: {} - bail@2.0.2: {} balanced-match@1.0.2: {} @@ -6267,16 +6221,12 @@ snapshots: dependencies: mimic-response: 1.0.1 + clsx@1.2.1: {} + clsx@2.1.1: {} codem-isoboxer@0.3.10: {} - codemirror-wrapped-line-indent@1.0.9(@codemirror/language@6.12.1)(@codemirror/state@6.5.4)(@codemirror/view@6.39.11): - dependencies: - '@codemirror/language': 6.12.1 - '@codemirror/state': 6.5.4 - '@codemirror/view': 6.39.11 - codemirror@6.0.2: dependencies: '@codemirror/autocomplete': 6.20.0 @@ -6429,14 +6379,10 @@ snapshots: detect-node@2.1.0: optional: true - devalue@5.6.2: {} - devlop@1.1.0: dependencies: dequal: 2.0.3 - diff-sequences@29.6.3: {} - dijkstrajs@1.0.3: {} dir-compare@3.3.0: @@ -6680,8 +6626,6 @@ snapshots: transitivePeerDependencies: - supports-color - esm-env@1.2.2: {} - espree@10.4.0: dependencies: acorn: 8.15.0 @@ -6692,10 +6636,6 @@ snapshots: dependencies: estraverse: 5.3.0 - esrap@2.2.1: - dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 - esrecurse@4.3.0: dependencies: estraverse: 5.3.0 @@ -6735,8 +6675,6 @@ snapshots: iobuffer: 5.4.0 pako: 2.1.0 - fast-uri@3.1.0: {} - fd-slicer@1.1.0: dependencies: pend: 1.2.0 @@ -6998,8 +6936,6 @@ snapshots: immediate@3.0.6: {} - immutable-json-patch@6.0.2: {} - import-fresh@3.3.1: dependencies: parent-module: 1.0.1 @@ -7054,10 +6990,6 @@ snapshots: is-plain-obj@4.1.0: {} - is-reference@3.0.3: - dependencies: - '@types/estree': 1.0.8 - isarray@1.0.0: {} isbinaryfile@4.0.10: {} @@ -7080,8 +7012,6 @@ snapshots: jiti@2.6.1: {} - jmespath@0.16.0: {} - js-binary-schema-parser@2.0.3: {} js-tokens@4.0.0: {} @@ -7098,10 +7028,6 @@ snapshots: json-schema-traverse@0.4.1: {} - json-schema-traverse@1.0.0: {} - - json-source-map@0.6.1: {} - json-stable-stringify-without-jsonify@1.0.1: {} json-stringify-safe@5.0.1: @@ -7129,8 +7055,6 @@ snapshots: '@jsep-plugin/regex': 1.0.4(jsep@1.4.0) jsep: 1.4.0 - jsonrepair@3.13.2: {} - jspdf@4.0.0: dependencies: '@babel/runtime': 7.28.4 @@ -7233,8 +7157,6 @@ snapshots: dependencies: lie: 3.1.1 - locate-character@3.0.0: {} - locate-path@5.0.0: dependencies: p-locate: 4.1.0 @@ -7261,6 +7183,10 @@ snapshots: longest-streak@3.1.0: {} + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + lowercase-keys@2.0.0: {} lru-cache@10.4.3: {} @@ -7412,8 +7338,6 @@ snapshots: dependencies: '@types/mdast': 4.0.4 - memoize-one@6.0.0: {} - micromark-core-commonmark@2.0.3: dependencies: decode-named-character-reference: 1.2.0 @@ -7671,8 +7595,6 @@ snapshots: napi-build-utils@2.0.0: {} - natural-compare-lite@1.4.0: {} - natural-compare@1.4.0: {} node-abi@3.86.0: @@ -7688,6 +7610,8 @@ snapshots: normalize-url@6.1.0: {} + object-assign@4.1.1: {} + object-keys@1.1.1: optional: true @@ -7810,6 +7734,12 @@ snapshots: err-code: 2.0.3 retry: 0.12.0 + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + prosemirror-changeset@2.3.1: dependencies: prosemirror-transform: 1.10.5 @@ -7933,6 +7863,11 @@ snapshots: minimist: 1.2.8 strip-json-comments: 2.0.1 + re-resizable@6.11.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + dependencies: + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + react-cropper@2.3.3(react@19.2.3): dependencies: cropperjs: 1.6.2 @@ -7943,6 +7878,20 @@ snapshots: react: 19.2.3 scheduler: 0.27.0 + react-draggable@4.4.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + dependencies: + clsx: 1.2.1 + prop-types: 15.8.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + react-draggable@4.5.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + dependencies: + clsx: 2.1.1 + prop-types: 15.8.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + react-intl@8.0.11(@types/react@19.2.7)(react@19.2.3)(typescript@5.9.3): dependencies: '@formatjs/ecma402-abstract': 3.0.8 @@ -7969,6 +7918,14 @@ snapshots: react-refresh@0.18.0: {} + react-rnd@10.5.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + dependencies: + re-resizable: 6.11.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + react-draggable: 4.4.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + tslib: 2.6.2 + react-router-dom@7.12.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: react: 19.2.3 @@ -8069,8 +8026,6 @@ snapshots: require-directory@2.1.1: {} - require-from-string@2.0.2: {} - require-main-filename@2.0.0: {} resolve-alpn@1.2.1: {} @@ -8216,6 +8171,8 @@ snapshots: stat-mode@1.0.0: {} + state-local@1.0.7: {} + string-convert@0.2.1: {} string-width@4.2.3: @@ -8260,24 +8217,6 @@ snapshots: dependencies: has-flag: 4.0.0 - svelte@5.46.4: - dependencies: - '@jridgewell/remapping': 2.3.5 - '@jridgewell/sourcemap-codec': 1.5.5 - '@sveltejs/acorn-typescript': 1.0.8(acorn@8.15.0) - '@types/estree': 1.0.8 - acorn: 8.15.0 - aria-query: 5.3.2 - axobject-query: 4.1.0 - clsx: 2.1.1 - devalue: 5.6.2 - esm-env: 1.2.2 - esrap: 2.2.1 - is-reference: 3.0.3 - locate-character: 3.0.0 - magic-string: 0.30.21 - zimmerframe: 1.1.4 - svg-pathdata@6.0.3: optional: true @@ -8342,6 +8281,8 @@ snapshots: dependencies: typescript: 5.9.3 + tslib@2.6.2: {} + tslib@2.8.1: {} tunnel-agent@0.6.0: @@ -8370,6 +8311,8 @@ snapshots: ua-parser-js@1.0.41: {} + ulid@3.0.2: {} + undici-types@6.21.0: {} undici-types@7.16.0: {} @@ -8431,38 +8374,7 @@ snapshots: base64-arraybuffer: 1.0.2 optional: true - vanilla-jsoneditor@3.11.0: - dependencies: - '@codemirror/autocomplete': 6.20.0 - '@codemirror/commands': 6.10.1 - '@codemirror/lang-json': 6.0.2 - '@codemirror/language': 6.12.1 - '@codemirror/lint': 6.9.2 - '@codemirror/search': 6.6.0 - '@codemirror/state': 6.5.4 - '@codemirror/view': 6.39.11 - '@fortawesome/free-regular-svg-icons': 7.1.0 - '@fortawesome/free-solid-svg-icons': 7.1.0 - '@jsonquerylang/jsonquery': 5.1.1 - '@lezer/highlight': 1.2.3 - '@replit/codemirror-indentation-markers': 6.5.3(@codemirror/language@6.12.1)(@codemirror/state@6.5.4)(@codemirror/view@6.39.11) - ajv: 8.17.1 - codemirror-wrapped-line-indent: 1.0.9(@codemirror/language@6.12.1)(@codemirror/state@6.5.4)(@codemirror/view@6.39.11) - diff-sequences: 29.6.3 - immutable-json-patch: 6.0.2 - jmespath: 0.16.0 - json-source-map: 0.6.1 - jsonpath-plus: 10.3.0 - jsonrepair: 3.13.2 - lodash-es: 4.17.22 - memoize-one: 6.0.0 - natural-compare-lite: 1.4.0 - svelte: 5.46.4 - vanilla-picker: 2.12.3 - - vanilla-picker@2.12.3: - dependencies: - '@sphinxxxx/color-conversion': 2.2.2 + uuid@13.0.0: {} verror@1.10.1: dependencies: @@ -8597,8 +8509,6 @@ snapshots: yocto-queue@0.1.0: {} - zimmerframe@1.1.4: {} - zip-stream@4.1.1: dependencies: archiver-utils: 3.0.4 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..e4a4b5b --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +onlyBuiltDependencies: + - better-sqlite3 diff --git a/src/i18n/en-US.ts b/src/i18n/en-US.ts index 39c783f..b127554 100644 --- a/src/i18n/en-US.ts +++ b/src/i18n/en-US.ts @@ -776,6 +776,21 @@ export default { "tools.imageWatermark.name": "Image Watermark", "tools.imageWatermark.description": "Add text or image watermarks to images.", "tools.imageToGifConverter.name": "Image to GIF Converter", + "tools.imageToGifConverter.description": + "Convert multiple images into a single animated GIF.", + + "tools.imageBase64Converter.name": "Image Base64 Converter", + "tools.imageBase64Converter.description": "Convert images to base64 strings and vice versa.", + "tools.imageBase64Converter.mode.imageToBase64": "Image to Base64", + "tools.imageBase64Converter.mode.base64ToImage": "Base64 to Image", + "tools.imageBase64Converter.inputTitle": "Input", + "tools.imageBase64Converter.outputTitle": "Output", + "tools.imageBase64Converter.uploadText": "Click or drag image to this area to upload", + "tools.imageBase64Converter.uploadHint": "Support image files only", + "tools.imageBase64Converter.noResults": "Output will appear here", + "tools.imageBase64Converter.base64Placeholder": "Paste your Base64 string here...", + "tools.imageBase64Converter.withPrefix": "Data URI Prefix", + "tools.imageBase64Converter.preview": "Preview Image", "tools.qrCodeTool.generate": "Generate", "tools.qrCodeTool.recognize": "Recognize", diff --git a/src/i18n/zh-CN.ts b/src/i18n/zh-CN.ts index d3a92ce..d20726d 100644 --- a/src/i18n/zh-CN.ts +++ b/src/i18n/zh-CN.ts @@ -735,6 +735,20 @@ export default { "tools.imageWatermark.name": "图片水印", "tools.imageWatermark.description": "为图片添加文本或图片水印。", "tools.imageToGifConverter.name": "图片转GIF", + "tools.imageToGifConverter.description": "将多张图片合成为一个动态 GIF 文件。", + + "tools.imageBase64Converter.name": "图片 Base64 转换", + "tools.imageBase64Converter.description": "实现图片与 Base64 字符串之间的相互转换。", + "tools.imageBase64Converter.mode.imageToBase64": "图片转 Base64", + "tools.imageBase64Converter.mode.base64ToImage": "Base64 转图片", + "tools.imageBase64Converter.inputTitle": "输入", + "tools.imageBase64Converter.outputTitle": "输出", + "tools.imageBase64Converter.uploadText": "点击或拖拽图片到此区域上传", + "tools.imageBase64Converter.uploadHint": "仅支持图片文件", + "tools.imageBase64Converter.noResults": "结果将显示在此处", + "tools.imageBase64Converter.base64Placeholder": "在此粘贴 Base64 字符串...", + "tools.imageBase64Converter.withPrefix": "数据 URI 前缀", + "tools.imageBase64Converter.preview": "预览图片", "tools.qrCodeTool.generate": "生成", "tools.qrCodeTool.recognize": "识别", diff --git a/src/pages/tools/Media/ImageBase64Converter.tsx b/src/pages/tools/Media/ImageBase64Converter.tsx new file mode 100644 index 0000000..7787577 --- /dev/null +++ b/src/pages/tools/Media/ImageBase64Converter.tsx @@ -0,0 +1,335 @@ +import React, { useState, useEffect } from 'react'; +import { Card, Input, Button, Typography, Space, message, Upload, Radio, Switch, Image, List, Tooltip } from 'antd'; +import { + DownloadOutlined, + CopyOutlined, + InboxOutlined, + DeleteOutlined, + FileImageOutlined, + CodeOutlined, + PictureOutlined, + BlockOutlined +} from '@ant-design/icons'; +import { useCopy } from '@/hooks/useCopy'; +import { FormattedMessage, useIntl } from 'react-intl'; + +const { TextArea } = Input; +const { Title, Text } = Typography; +const { Dragger } = Upload; + +type Mode = 'imageToBase64' | 'base64ToImage'; + +interface ImageItem { + id: string; + name: string; + size: number; + preview: string; + base64: string; + base64NoPrefix: string; +} + +const ImageBase64Converter: React.FC = () => { + const intl = useIntl(); + const copy = useCopy(); + + const [mode, setMode] = useState('imageToBase64'); + + // Image to Base64 State + const [imageItems, setImageItems] = useState([]); + const [withPrefix, setWithPrefix] = useState(true); + + // Base64 to Image State + const [base64Input, setBase64Input] = useState(''); + const [decodedImage, setDecodedImage] = useState(null); + + // ------------------------------------------------------------------------- + // Logic + // ------------------------------------------------------------------------- + + const handleBeforeUpload = (file: File, fileList: File[]) => { + const isImage = file.type.startsWith('image/'); + if (!isImage) { + message.error(`${file.name} is not an image file!`); + return Upload.LIST_IGNORE; + } + + const reader = new FileReader(); + reader.onload = (e) => { + const fullBase64 = e.target?.result as string; + const split = fullBase64.split(','); + const base64NoPrefix = split.length > 1 ? split[1] : split[0]; + + const newItem: ImageItem = { + id: Math.random().toString(36).substr(2, 9), + name: file.name, + size: file.size, + preview: fullBase64, + base64: fullBase64, + base64NoPrefix: base64NoPrefix, + }; + + setImageItems(prev => [...prev, newItem]); + + if (file === fileList[fileList.length - 1]) { + message.success(intl.formatMessage({ id: 'toast.success' })); + } + }; + reader.readAsDataURL(file); + return false; + }; + + const handleDownload = (data: string, name?: string) => { + if (!data) return; + const link = document.createElement('a'); + link.href = data; + link.download = name || `image_${Date.now()}.png`; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + }; + + const handleClear = () => { + if (mode === 'imageToBase64') { + setImageItems([]); + } else { + setBase64Input(''); + setDecodedImage(null); + } + }; + + const formatSize = (bytes: number) => { + if (bytes === 0) return '0 B'; + const k = 1024; + const sizes = ['B', 'KB', 'MB', 'GB']; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; + }; + + // ------------------------------------------------------------------------- + // Render + // ------------------------------------------------------------------------- + + return ( +
+ {/* Header */} +
+ + <FormattedMessage id="tools.imageBase64Converter.name" /> + + + + +
+ + {/* Mode Switcher */} +
+ setMode(e.target.value)} + optionType="button" + buttonStyle="solid" + size="large" + > + + + + + + + +
+ + {/* Content Area */} + {mode === 'imageToBase64' ? ( +
+ {/* Upload Dragger */} + {imageItems.length === 0 ? ( + + +

+ +

+

+

+ +

+
+
+ ) : ( + <> + {/* Global Controls */} + +
+ +
+ + +
+
+ + Images: {imageItems.length} + +
+
+ + + + +
+
+ + {/* List of Conversion Cards */} +
+ {imageItems.map((item) => ( + +
+ {/* Full Frame Preview Area - Removed internal Padding */} +
+
+ {item.name}Click to Expand
+ }} + /> +
+ {/* Bottom info overlay */} +
+ + {item.name} + + {formatSize(item.size)} +
+
+ + {/* Base64 Area */} +
+
+
+
+ +
+
+ Base64 Output + + {(withPrefix ? item.base64.length : item.base64NoPrefix.length).toLocaleString()} characters + +
+
+ + + + +
+