diff --git a/package.json b/package.json index b1036303..32d40aa2 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,10 @@ "dependencies": { "@headlessui/react": "^2.1.10", "@tauri-apps/api": ">=2.0.0", - "@tauri-apps/plugin-http": "~2", + "@tauri-apps/plugin-http": "~2.0.1", "@tauri-apps/plugin-shell": ">=2.0.0", + "@tauri-apps/plugin-websocket": "~2", + "axios": "^1.7.7", "clsx": "^2.1.1", "framer-motion": "^11.11.11", "i18next": "^23.16.2", @@ -23,6 +25,7 @@ "react-dom": "^18.2.0", "react-hotkeys-hook": "^4.5.1", "react-i18next": "^15.1.0", + "react-markdown": "^9.0.1", "react-router-dom": "^6.27.0", "zustand": "^5.0.0" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 41e2ad01..44cc86e5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,11 +15,17 @@ importers: specifier: '>=2.0.0' version: 2.0.2 '@tauri-apps/plugin-http': - specifier: ~2 + specifier: ~2.0.1 version: 2.0.1 '@tauri-apps/plugin-shell': specifier: '>=2.0.0' version: 2.0.0 + '@tauri-apps/plugin-websocket': + specifier: ~2 + version: 2.0.0 + axios: + specifier: ^1.7.7 + version: 1.7.7 clsx: specifier: ^2.1.1 version: 2.1.1 @@ -47,6 +53,9 @@ importers: react-i18next: specifier: ^15.1.0 version: 15.1.0(i18next@23.16.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-markdown: + specifier: ^9.0.1 + version: 9.0.1(@types/react@18.3.11)(react@18.3.1) react-router-dom: specifier: ^6.27.0 version: 6.27.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -610,6 +619,9 @@ packages: '@tauri-apps/plugin-shell@2.0.0': resolution: {integrity: sha512-OpW2+ycgJLrEoZityWeWYk+6ZWP9VyiAfbO+N/O8VfLkqyOym8kXh7odKDfINx9RAotkSGBtQM4abyKfJDkcUg==} + '@tauri-apps/plugin-websocket@2.0.0': + resolution: {integrity: sha512-O2qRxZCljd4g+ceJhW7LfgQr+fg0fBBiAaLiMopoKL6TXKMnhBHOenp4nZ5/MoVTr77OQIDNO6Jp/c1YwiRVtQ==} + '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -622,12 +634,27 @@ packages: '@types/babel__traverse@7.20.6': resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} + '@types/debug@4.1.12': + resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + + '@types/estree-jsx@1.0.5': + resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} + '@types/estree@1.0.6': resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + '@types/hast@3.0.4': + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + '@types/lodash@4.17.12': resolution: {integrity: sha512-sviUmCE8AYdaF/KIHLDJBQgeYzPBI0vf/17NaYehBJfYD1j6/L95Slh07NlyK2iNyBNaEkb3En2jRt+a8y3xZQ==} + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/ms@0.7.34': + resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==} + '@types/node@22.8.4': resolution: {integrity: sha512-SpNNxkftTJOPk0oN+y2bIqurEXHTA2AOZ3EJDDKeJ5VzkvvORSvmQXGQarcOzWV1ac7DCaPBEdMDxBsM+d8jWw==} @@ -644,6 +671,15 @@ packages: '@types/react@18.3.11': resolution: {integrity: sha512-r6QZ069rFTjrEYgFdOck1gK7FLVsgJE7tTz0pQBczlBNUhBNk0MQH4UbnFSwjpQLMkLzgqvBBa+qGpLje16eTQ==} + '@types/unist@2.0.11': + resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + + '@ungap/structured-clone@1.2.0': + resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + '@vitejs/plugin-react@4.3.2': resolution: {integrity: sha512-hieu+o05v4glEBucTcKMK3dlES0OeJlD9YVOAPraVMOInBCwzumaIFiUjr4bHK7NPgnAHgiskUoceKercrN8vg==} engines: {node: ^14.18.0 || >=16.0.0} @@ -680,6 +716,9 @@ packages: arg@5.0.2: resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + autoprefixer@10.4.20: resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==} engines: {node: ^10 || ^12 || >=14} @@ -687,6 +726,12 @@ packages: peerDependencies: postcss: ^8.1.0 + axios@1.7.7: + resolution: {integrity: sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==} + + bail@2.0.2: + resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -713,10 +758,25 @@ packages: caniuse-lite@1.0.30001668: resolution: {integrity: sha512-nWLrdxqCdblixUO+27JtGJJE/txpJlyUy5YN1u53wLZkP0emYCo5zgS6QYft7VUYR42LGgi/S5hdLZTrnyIddw==} + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + + character-entities@2.0.2: + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + + character-reference-invalid@2.0.1: + resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} + chokidar@3.6.0: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} @@ -738,6 +798,13 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + commander@4.1.1: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} @@ -766,6 +833,20 @@ packages: supports-color: optional: true + decode-named-character-reference@1.0.2: + resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} @@ -797,6 +878,12 @@ packages: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} + estree-util-is-identifier-name@3.0.0: + resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} + + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + fast-glob@3.3.2: resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} engines: {node: '>=8.6.0'} @@ -808,10 +895,23 @@ packages: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} + follow-redirects@1.15.9: + resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + foreground-child@3.3.0: resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} engines: {node: '>=14'} + form-data@4.0.1: + resolution: {integrity: sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==} + engines: {node: '>= 6'} + fraction.js@4.3.7: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} @@ -865,12 +965,30 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + hast-util-to-jsx-runtime@2.3.2: + resolution: {integrity: sha512-1ngXYb+V9UT5h+PxNRa1O1FYguZK/XL+gkeqvp7EdHlB9oHUG0eYRo/vY5inBdcqo3RkPMC58/H94HvkbfGdyg==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + html-parse-stringify@3.0.1: resolution: {integrity: sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==} + html-url-attributes@3.0.1: + resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} + i18next@23.16.2: resolution: {integrity: sha512-dFyxwLXxEQK32f6tITBMaRht25mZPJhQ0WbC0p3bO2mWBal9lABTMqSka5k+GLSRWLzeJBKDpH7BeIA9TZI7Jg==} + inline-style-parser@0.2.4: + resolution: {integrity: sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==} + + is-alphabetical@2.0.1: + resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} + + is-alphanumerical@2.0.1: + resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} + is-binary-path@2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} @@ -879,6 +997,9 @@ packages: resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==} engines: {node: '>= 0.4'} + is-decimal@2.0.1: + resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} + is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -891,10 +1012,17 @@ packages: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} + is-hexadecimal@2.0.1: + resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -932,6 +1060,9 @@ packages: lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + 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 @@ -947,14 +1078,109 @@ packages: peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc + mdast-util-from-markdown@2.0.2: + resolution: {integrity: sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==} + + mdast-util-mdx-expression@2.0.1: + resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==} + + mdast-util-mdx-jsx@3.1.3: + resolution: {integrity: sha512-bfOjvNt+1AcbPLTFMFWY149nJz0OjmewJs3LQQ5pIyVGxP4CdOqNVJL6kTaM5c68p8q82Xv3nCyFfUnuEcH3UQ==} + + mdast-util-mdxjs-esm@2.0.1: + resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==} + + mdast-util-phrasing@4.1.0: + resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} + + mdast-util-to-hast@13.2.0: + resolution: {integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==} + + mdast-util-to-markdown@2.1.2: + resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} + + mdast-util-to-string@4.0.0: + resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} + micromark-core-commonmark@2.0.1: + resolution: {integrity: sha512-CUQyKr1e///ZODyD1U3xit6zXwy1a8q2a1S1HKtIlmgvurrEpaw/Y9y6KSIbF8P59cn/NjzHyO+Q2fAyYLQrAA==} + + micromark-factory-destination@2.0.0: + resolution: {integrity: sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==} + + micromark-factory-label@2.0.0: + resolution: {integrity: sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==} + + micromark-factory-space@2.0.0: + resolution: {integrity: sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==} + + micromark-factory-title@2.0.0: + resolution: {integrity: sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==} + + micromark-factory-whitespace@2.0.0: + resolution: {integrity: sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==} + + micromark-util-character@2.1.0: + resolution: {integrity: sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==} + + micromark-util-chunked@2.0.0: + resolution: {integrity: sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==} + + micromark-util-classify-character@2.0.0: + resolution: {integrity: sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==} + + micromark-util-combine-extensions@2.0.0: + resolution: {integrity: sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==} + + micromark-util-decode-numeric-character-reference@2.0.1: + resolution: {integrity: sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ==} + + micromark-util-decode-string@2.0.0: + resolution: {integrity: sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==} + + micromark-util-encode@2.0.0: + resolution: {integrity: sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==} + + micromark-util-html-tag-name@2.0.0: + resolution: {integrity: sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==} + + micromark-util-normalize-identifier@2.0.0: + resolution: {integrity: sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==} + + micromark-util-resolve-all@2.0.0: + resolution: {integrity: sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==} + + micromark-util-sanitize-uri@2.0.0: + resolution: {integrity: sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==} + + micromark-util-subtokenize@2.0.1: + resolution: {integrity: sha512-jZNtiFl/1aY73yS3UGQkutD0UbhTt68qnRpw2Pifmz5wV9h8gOVsN70v+Lq/f1rKaU/W8pxRe8y8Q9FX1AOe1Q==} + + micromark-util-symbol@2.0.0: + resolution: {integrity: sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==} + + micromark-util-types@2.0.0: + resolution: {integrity: sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==} + + micromark@4.0.0: + resolution: {integrity: sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==} + micromatch@4.0.8: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + minimatch@9.0.5: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} @@ -996,6 +1222,9 @@ packages: package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + parse-entities@4.0.1: + resolution: {integrity: sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==} + path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -1063,6 +1292,12 @@ packages: resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==} engines: {node: ^10 || ^12 || >=14} + property-information@6.5.0: + resolution: {integrity: sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -1090,6 +1325,12 @@ packages: react-native: optional: true + react-markdown@9.0.1: + resolution: {integrity: sha512-186Gw/vF1uRkydbsOIkcGXw7aHq0sZOCRFFjGrr7b9+nVZg4UfA4enXCaxm4fUzecU38sWfrNDitGhshuU7rdg==} + peerDependencies: + '@types/react': '>=18' + react: '>=18' + react-refresh@0.14.2: resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} engines: {node: '>=0.10.0'} @@ -1121,6 +1362,12 @@ packages: regenerator-runtime@0.14.1: resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + remark-parse@11.0.0: + resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} + + remark-rehype@11.1.1: + resolution: {integrity: sha512-g/osARvjkBXb6Wo0XvAeXQohVta8i84ACbenPpoSsxTOQH/Ae0/RGP4WZgnMH5pMLpsj4FG7OHmcIcXxpza8eQ==} + resolve@1.22.8: resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} hasBin: true @@ -1160,6 +1407,9 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -1168,6 +1418,9 @@ packages: resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} engines: {node: '>=12'} + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -1176,6 +1429,9 @@ packages: resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} engines: {node: '>=12'} + style-to-object@1.0.8: + resolution: {integrity: sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g==} + sucrase@3.35.0: resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} engines: {node: '>=16 || 14 >=14.17'} @@ -1212,6 +1468,12 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + + trough@2.2.0: + resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} @@ -1226,6 +1488,24 @@ packages: undici-types@6.19.8: resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + unified@11.0.5: + resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + + unist-util-is@6.0.0: + resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-parents@6.0.1: + resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==} + + unist-util-visit@5.0.0: + resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} + update-browserslist-db@1.1.1: resolution: {integrity: sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==} hasBin: true @@ -1235,6 +1515,12 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + vfile-message@4.0.2: + resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + vite@5.4.8: resolution: {integrity: sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==} engines: {node: ^18.0.0 || >=20.0.0} @@ -1309,6 +1595,9 @@ packages: use-sync-external-store: optional: true + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + snapshots: '@alloc/quick-lru@5.2.0': {} @@ -1745,6 +2034,10 @@ snapshots: dependencies: '@tauri-apps/api': 2.0.2 + '@tauri-apps/plugin-websocket@2.0.0': + dependencies: + '@tauri-apps/api': 2.0.2 + '@types/babel__core@7.20.5': dependencies: '@babel/parser': 7.25.8 @@ -1766,10 +2059,28 @@ snapshots: dependencies: '@babel/types': 7.25.8 + '@types/debug@4.1.12': + dependencies: + '@types/ms': 0.7.34 + + '@types/estree-jsx@1.0.5': + dependencies: + '@types/estree': 1.0.6 + '@types/estree@1.0.6': {} + '@types/hast@3.0.4': + dependencies: + '@types/unist': 3.0.3 + '@types/lodash@4.17.12': {} + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/ms@0.7.34': {} + '@types/node@22.8.4': dependencies: undici-types: 6.19.8 @@ -1794,6 +2105,12 @@ snapshots: '@types/prop-types': 15.7.13 csstype: 3.1.3 + '@types/unist@2.0.11': {} + + '@types/unist@3.0.3': {} + + '@ungap/structured-clone@1.2.0': {} + '@vitejs/plugin-react@4.3.2(vite@5.4.8(@types/node@22.8.4))': dependencies: '@babel/core': 7.25.8 @@ -1828,6 +2145,8 @@ snapshots: arg@5.0.2: {} + asynckit@0.4.0: {} + autoprefixer@10.4.20(postcss@8.4.47): dependencies: browserslist: 4.24.0 @@ -1838,6 +2157,16 @@ snapshots: postcss: 8.4.47 postcss-value-parser: 4.2.0 + axios@1.7.7: + dependencies: + follow-redirects: 1.15.9 + form-data: 4.0.1 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + + bail@2.0.2: {} + balanced-match@1.0.2: {} binary-extensions@2.3.0: {} @@ -1861,12 +2190,22 @@ snapshots: caniuse-lite@1.0.30001668: {} + ccount@2.0.1: {} + chalk@2.4.2: dependencies: ansi-styles: 3.2.1 escape-string-regexp: 1.0.5 supports-color: 5.5.0 + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + + character-entities@2.0.2: {} + + character-reference-invalid@2.0.1: {} + chokidar@3.6.0: dependencies: anymatch: 3.1.3 @@ -1893,6 +2232,12 @@ snapshots: color-name@1.1.4: {} + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + comma-separated-tokens@2.0.3: {} + commander@4.1.1: {} convert-source-map@2.0.0: {} @@ -1911,6 +2256,18 @@ snapshots: dependencies: ms: 2.1.3 + decode-named-character-reference@1.0.2: + dependencies: + character-entities: 2.0.2 + + delayed-stream@1.0.0: {} + + dequal@2.0.3: {} + + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + didyoumean@1.2.2: {} dlv@1.1.3: {} @@ -1953,6 +2310,10 @@ snapshots: escape-string-regexp@1.0.5: {} + estree-util-is-identifier-name@3.0.0: {} + + extend@3.0.2: {} + fast-glob@3.3.2: dependencies: '@nodelib/fs.stat': 2.0.5 @@ -1969,11 +2330,19 @@ snapshots: dependencies: to-regex-range: 5.0.1 + follow-redirects@1.15.9: {} + foreground-child@3.3.0: dependencies: cross-spawn: 7.0.3 signal-exit: 4.1.0 + form-data@4.0.1: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + fraction.js@4.3.7: {} framer-motion@11.11.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1): @@ -2015,14 +2384,49 @@ snapshots: dependencies: function-bind: 1.1.2 + hast-util-to-jsx-runtime@2.3.2: + dependencies: + '@types/estree': 1.0.6 + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.1.3 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 6.5.0 + space-separated-tokens: 2.0.2 + style-to-object: 1.0.8 + unist-util-position: 5.0.0 + vfile-message: 4.0.2 + transitivePeerDependencies: + - supports-color + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.4 + html-parse-stringify@3.0.1: dependencies: void-elements: 3.1.0 + html-url-attributes@3.0.1: {} + i18next@23.16.2: dependencies: '@babel/runtime': 7.25.9 + inline-style-parser@0.2.4: {} + + is-alphabetical@2.0.1: {} + + is-alphanumerical@2.0.1: + dependencies: + is-alphabetical: 2.0.1 + is-decimal: 2.0.1 + is-binary-path@2.1.0: dependencies: binary-extensions: 2.3.0 @@ -2031,6 +2435,8 @@ snapshots: dependencies: hasown: 2.0.2 + is-decimal@2.0.1: {} + is-extglob@2.1.1: {} is-fullwidth-code-point@3.0.0: {} @@ -2039,8 +2445,12 @@ snapshots: dependencies: is-extglob: 2.1.1 + is-hexadecimal@2.0.1: {} + is-number@7.0.0: {} + is-plain-obj@4.1.0: {} + isexe@2.0.0: {} jackspeak@3.4.3: @@ -2065,6 +2475,8 @@ snapshots: lodash@4.17.21: {} + longest-streak@3.1.0: {} + loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 @@ -2079,13 +2491,241 @@ snapshots: dependencies: react: 18.3.1 + mdast-util-from-markdown@2.0.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + decode-named-character-reference: 1.0.2 + devlop: 1.1.0 + mdast-util-to-string: 4.0.0 + micromark: 4.0.0 + micromark-util-decode-numeric-character-reference: 2.0.1 + micromark-util-decode-string: 2.0.0 + micromark-util-normalize-identifier: 2.0.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + unist-util-stringify-position: 4.0.0 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-expression@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-jsx@3.1.3: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + parse-entities: 4.0.1 + stringify-entities: 4.0.4 + unist-util-stringify-position: 4.0.0 + vfile-message: 4.0.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdxjs-esm@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-phrasing@4.1.0: + dependencies: + '@types/mdast': 4.0.4 + unist-util-is: 6.0.0 + + mdast-util-to-hast@13.2.0: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.2.0 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.0 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + + mdast-util-to-markdown@2.1.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + longest-streak: 3.1.0 + mdast-util-phrasing: 4.1.0 + mdast-util-to-string: 4.0.0 + micromark-util-classify-character: 2.0.0 + micromark-util-decode-string: 2.0.0 + unist-util-visit: 5.0.0 + zwitch: 2.0.4 + + mdast-util-to-string@4.0.0: + dependencies: + '@types/mdast': 4.0.4 + merge2@1.4.1: {} + micromark-core-commonmark@2.0.1: + dependencies: + decode-named-character-reference: 1.0.2 + devlop: 1.1.0 + micromark-factory-destination: 2.0.0 + micromark-factory-label: 2.0.0 + micromark-factory-space: 2.0.0 + micromark-factory-title: 2.0.0 + micromark-factory-whitespace: 2.0.0 + micromark-util-character: 2.1.0 + micromark-util-chunked: 2.0.0 + micromark-util-classify-character: 2.0.0 + micromark-util-html-tag-name: 2.0.0 + micromark-util-normalize-identifier: 2.0.0 + micromark-util-resolve-all: 2.0.0 + micromark-util-subtokenize: 2.0.1 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + + micromark-factory-destination@2.0.0: + dependencies: + micromark-util-character: 2.1.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + + micromark-factory-label@2.0.0: + dependencies: + devlop: 1.1.0 + micromark-util-character: 2.1.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + + micromark-factory-space@2.0.0: + dependencies: + micromark-util-character: 2.1.0 + micromark-util-types: 2.0.0 + + micromark-factory-title@2.0.0: + dependencies: + micromark-factory-space: 2.0.0 + micromark-util-character: 2.1.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + + micromark-factory-whitespace@2.0.0: + dependencies: + micromark-factory-space: 2.0.0 + micromark-util-character: 2.1.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + + micromark-util-character@2.1.0: + dependencies: + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + + micromark-util-chunked@2.0.0: + dependencies: + micromark-util-symbol: 2.0.0 + + micromark-util-classify-character@2.0.0: + dependencies: + micromark-util-character: 2.1.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + + micromark-util-combine-extensions@2.0.0: + dependencies: + micromark-util-chunked: 2.0.0 + micromark-util-types: 2.0.0 + + micromark-util-decode-numeric-character-reference@2.0.1: + dependencies: + micromark-util-symbol: 2.0.0 + + micromark-util-decode-string@2.0.0: + dependencies: + decode-named-character-reference: 1.0.2 + micromark-util-character: 2.1.0 + micromark-util-decode-numeric-character-reference: 2.0.1 + micromark-util-symbol: 2.0.0 + + micromark-util-encode@2.0.0: {} + + micromark-util-html-tag-name@2.0.0: {} + + micromark-util-normalize-identifier@2.0.0: + dependencies: + micromark-util-symbol: 2.0.0 + + micromark-util-resolve-all@2.0.0: + dependencies: + micromark-util-types: 2.0.0 + + micromark-util-sanitize-uri@2.0.0: + dependencies: + micromark-util-character: 2.1.0 + micromark-util-encode: 2.0.0 + micromark-util-symbol: 2.0.0 + + micromark-util-subtokenize@2.0.1: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + + micromark-util-symbol@2.0.0: {} + + micromark-util-types@2.0.0: {} + + micromark@4.0.0: + dependencies: + '@types/debug': 4.1.12 + debug: 4.3.7 + decode-named-character-reference: 1.0.2 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.1 + micromark-factory-space: 2.0.0 + micromark-util-character: 2.1.0 + micromark-util-chunked: 2.0.0 + micromark-util-combine-extensions: 2.0.0 + micromark-util-decode-numeric-character-reference: 2.0.1 + micromark-util-encode: 2.0.0 + micromark-util-normalize-identifier: 2.0.0 + micromark-util-resolve-all: 2.0.0 + micromark-util-sanitize-uri: 2.0.0 + micromark-util-subtokenize: 2.0.1 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + transitivePeerDependencies: + - supports-color + micromatch@4.0.8: dependencies: braces: 3.0.3 picomatch: 2.3.1 + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + minimatch@9.0.5: dependencies: brace-expansion: 2.0.1 @@ -2114,6 +2754,17 @@ snapshots: package-json-from-dist@1.0.1: {} + parse-entities@4.0.1: + dependencies: + '@types/unist': 2.0.11 + character-entities: 2.0.2 + character-entities-legacy: 3.0.0 + character-reference-invalid: 2.0.1 + decode-named-character-reference: 1.0.2 + is-alphanumerical: 2.0.1 + is-decimal: 2.0.1 + is-hexadecimal: 2.0.1 + path-key@3.1.1: {} path-parse@1.0.7: {} @@ -2168,6 +2819,10 @@ snapshots: picocolors: 1.1.0 source-map-js: 1.2.1 + property-information@6.5.0: {} + + proxy-from-env@1.1.0: {} + queue-microtask@1.2.3: {} react-dom@18.3.1(react@18.3.1): @@ -2190,6 +2845,23 @@ snapshots: optionalDependencies: react-dom: 18.3.1(react@18.3.1) + react-markdown@9.0.1(@types/react@18.3.11)(react@18.3.1): + dependencies: + '@types/hast': 3.0.4 + '@types/react': 18.3.11 + devlop: 1.1.0 + hast-util-to-jsx-runtime: 2.3.2 + html-url-attributes: 3.0.1 + mdast-util-to-hast: 13.2.0 + react: 18.3.1 + remark-parse: 11.0.0 + remark-rehype: 11.1.1 + unified: 11.0.5 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + react-refresh@0.14.2: {} react-router-dom@6.27.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): @@ -2218,6 +2890,23 @@ snapshots: regenerator-runtime@0.14.1: {} + remark-parse@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.2 + micromark-util-types: 2.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-rehype@11.1.1: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + mdast-util-to-hast: 13.2.0 + unified: 11.0.5 + vfile: 6.0.3 + resolve@1.22.8: dependencies: is-core-module: 2.15.1 @@ -2268,6 +2957,8 @@ snapshots: source-map-js@1.2.1: {} + space-separated-tokens@2.0.2: {} + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -2280,6 +2971,11 @@ snapshots: emoji-regex: 9.2.2 strip-ansi: 7.1.0 + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 @@ -2288,6 +2984,10 @@ snapshots: dependencies: ansi-regex: 6.1.0 + style-to-object@1.0.8: + dependencies: + inline-style-parser: 0.2.4 + sucrase@3.35.0: dependencies: '@jridgewell/gen-mapping': 0.3.5 @@ -2347,6 +3047,10 @@ snapshots: dependencies: is-number: 7.0.0 + trim-lines@3.0.1: {} + + trough@2.2.0: {} + ts-interface-checker@0.1.13: {} tslib@2.8.0: {} @@ -2355,6 +3059,39 @@ snapshots: undici-types@6.19.8: {} + unified@11.0.5: + dependencies: + '@types/unist': 3.0.3 + bail: 2.0.2 + devlop: 1.1.0 + extend: 3.0.2 + is-plain-obj: 4.1.0 + trough: 2.2.0 + vfile: 6.0.3 + + unist-util-is@6.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.1: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.0 + + unist-util-visit@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.0 + unist-util-visit-parents: 6.0.1 + update-browserslist-db@1.1.1(browserslist@4.24.0): dependencies: browserslist: 4.24.0 @@ -2363,6 +3100,16 @@ snapshots: util-deprecate@1.0.2: {} + vfile-message@4.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.2 + vite@5.4.8(@types/node@22.8.4): dependencies: esbuild: 0.21.5 @@ -2398,3 +3145,5 @@ snapshots: optionalDependencies: '@types/react': 18.3.11 react: 18.3.1 + + zwitch@2.0.4: {} diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 0a3b568e..e23b0fa8 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -350,6 +350,7 @@ dependencies = [ "tauri-nspanel", "tauri-plugin-http", "tauri-plugin-shell", + "tauri-plugin-websocket", ] [[package]] @@ -591,6 +592,12 @@ dependencies = [ "syn 2.0.79", ] +[[package]] +name = "data-encoding" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" + [[package]] name = "data-url" version = "0.3.1" @@ -3043,6 +3050,17 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sha2" version = "0.10.8" @@ -3547,6 +3565,25 @@ dependencies = [ "tokio", ] +[[package]] +name = "tauri-plugin-websocket" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2793b69e1dd494beed1e0a29865f38bd00011e7ccc35a3cfde8e3939328b790" +dependencies = [ + "futures-util", + "http", + "log", + "rand 0.8.5", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "thiserror", + "tokio", + "tokio-tungstenite", +] + [[package]] name = "tauri-runtime" version = "2.1.0" @@ -3759,6 +3796,22 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-tungstenite" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9" +dependencies = [ + "futures-util", + "log", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tungstenite", + "webpki-roots", +] + [[package]] name = "tokio-util" version = "0.7.12" @@ -3883,6 +3936,26 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "tungstenite" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand 0.8.5", + "rustls", + "rustls-pki-types", + "sha1", + "thiserror", + "utf-8", +] + [[package]] name = "typeid" version = "1.0.2" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index e91dc0e0..bf265f72 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -25,6 +25,7 @@ serde_json = "1" tauri-plugin-http = "2" tauri-nspanel = { git = "https://github.com/ahkohd/tauri-nspanel", rev = "005240c" } +tauri-plugin-websocket = "2" [profile.dev] incremental = true # Compile your binary in smaller steps. diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index 97a4f839..daa7ace7 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -5,7 +5,7 @@ "windows": ["main"], "permissions": [ "core:event:allow-listen", - "core:window:default", + "core:window:default", "core:window:allow-start-dragging", "core:webview:allow-create-webview", "core:window:allow-show", @@ -24,8 +24,15 @@ "http:allow-fetch-send", { "identifier": "http:default", - "allow": [{ "url": "https://*.tauri.app" }], - "deny": [{ "url": "https://private.tauri.app" }] - } + "allow": [ + { + "url": "http://localhost:2900" + } + ], + "deny": [] + }, + "websocket:default", + "websocket:allow-connect", + "websocket:allow-send" ] } diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 11e1e224..fd806101 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -12,6 +12,8 @@ fn main() { coco_lib::run(); tauri::Builder::default() + .plugin(tauri_plugin_websocket::init()) + .plugin(tauri_plugin_http::init()) .plugin(tauri_nspanel::init()) .invoke_handler(tauri::generate_handler![ show_panel, diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index ca7f8965..5eb020c6 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -14,15 +14,13 @@ "windows": [ { "title": "Coco AI", - "width": 800, - "height": 110, - "maxHeight": 600, + "width": 900, + "height": 800, + "maxHeight": 800, "transparent": true, "resizable": true, "fullscreen": false, - "decorations": false, - "label": "main", - "url": "/" + "decorations": false } ], "security": { @@ -46,6 +44,7 @@ ] }, "plugins": { - "window": {} + "window": {}, + "websocket": {} } } \ No newline at end of file diff --git a/src/api/tauriFetchClient.ts b/src/api/tauriFetchClient.ts new file mode 100644 index 00000000..1703af96 --- /dev/null +++ b/src/api/tauriFetchClient.ts @@ -0,0 +1,73 @@ +import { fetch } from "@tauri-apps/plugin-http"; + +const baseURL = "http://localhost:2900"; + +interface FetchRequestConfig { + url: string; + method?: "GET" | "POST" | "PUT" | "DELETE"; + headers?: Record; + body?: any; + timeout?: number; + parseAs?: "json" | "text" | "binary"; +} + +interface FetchResponse { + data: T; + status: number; + statusText: string; + headers: Headers; +} + +const timeoutPromise = (ms: number) => { + return new Promise((_, reject) => + setTimeout(() => reject(new Error(`Request timed out after ${ms} ms`)), ms) + ); +}; + +export const tauriFetch = async ({ + url, + method = "GET", + headers = {}, + body, + timeout = 30, + parseAs = "json", +}: FetchRequestConfig): Promise> => { + try { + url = baseURL + url; + if (method !== "GET") { + headers["Content-Type"] = "application/json"; + } + + const fetchPromise = fetch(url, { + method, + headers, + body, + }); + + const response = await Promise.race([ + fetchPromise, + timeoutPromise(timeout * 1000), + ]); + + const statusText = response.ok ? "OK" : "Error"; + + let data: any; + if (parseAs === "json") { + data = await response.json(); + } else if (parseAs === "text") { + data = await response.text(); + } else { + data = await response.arrayBuffer(); + } + + return { + data, + status: response.status, + statusText, + headers: response.headers, + }; + } catch (error) { + console.error("Request failed:", error); + throw error; + } +}; diff --git a/src/components/ChatAI/ChatInput.tsx b/src/components/ChatAI/ChatInput.tsx index 4a34583d..071225a2 100644 --- a/src/components/ChatAI/ChatInput.tsx +++ b/src/components/ChatAI/ChatInput.tsx @@ -1,4 +1,4 @@ -import { SendHorizontal } from "lucide-react"; +import { SendHorizontal, OctagonX } from "lucide-react"; import { useState, type FormEvent, @@ -9,10 +9,15 @@ import { interface ChatInputProps { onSend: (message: string) => void; - disabled?: boolean; + disabled: boolean; + disabledChange: (disabled: boolean) => void; } -export function ChatInput({ onSend, disabled }: ChatInputProps) { +export function ChatInput({ + onSend, + disabled, + disabledChange, +}: ChatInputProps) { const [input, setInput] = useState(""); const textareaRef = useRef(null); @@ -62,13 +67,25 @@ export function ChatInput({ onSend, disabled }: ChatInputProps) { className="w-full resize-none rounded-lg border-0 bg-gray-50 dark:bg-gray-800/50 py-3 pl-4 pr-12 text-sm leading-6 text-gray-900 dark:text-gray-100 shadow-sm ring-1 ring-gray-200 dark:ring-gray-700 placeholder:text-gray-400 dark:placeholder:text-gray-500 focus:ring-2 focus:ring-indigo-500 dark:focus:ring-indigo-400 transition-shadow" disabled={disabled} /> - + {disabled ? ( + + ) : ( + + )}

Press Enter to send, Shift + Enter for new line diff --git a/src/components/ChatAI/ChatMessage.tsx b/src/components/ChatAI/ChatMessage.tsx index b599ee81..c2f14205 100644 --- a/src/components/ChatAI/ChatMessage.tsx +++ b/src/components/ChatAI/ChatMessage.tsx @@ -11,15 +11,18 @@ interface ChatMessageProps { export function ChatMessage({ message, isTyping }: ChatMessageProps) { const [isAnimationComplete, setIsAnimationComplete] = useState(!isTyping); - const isAssistant = message.role === "assistant"; + const isAssistant = message._source?.type === "assistant"; return (

-
+
+ {/* 头像部分 */}
)}
-
+ + {/* 消息内容 */} +

{isAssistant ? "Assistant" : "You"}

@@ -42,7 +51,7 @@ export function ChatMessage({ message, isTyping }: ChatMessageProps) { {isTyping && isAssistant ? ( <> setIsAnimationComplete(true)} /> {!isAnimationComplete && ( @@ -50,7 +59,7 @@ export function ChatMessage({ message, isTyping }: ChatMessageProps) { )} ) : ( - message.content + message._source?.message || "" )}

diff --git a/src/components/ChatAI/Sidebar.tsx b/src/components/ChatAI/Sidebar.tsx index cf6530c1..3f101efb 100644 --- a/src/components/ChatAI/Sidebar.tsx +++ b/src/components/ChatAI/Sidebar.tsx @@ -42,9 +42,9 @@ export function Sidebar({
{chats.map((chat) => (
- {chat.title} + {chat.title || chat._id} - {chats.length > 1 && ( + {/* {chats.length > 1 && ( - )} - {activeChat.id === chat.id && ( + )} */} + {activeChat._id === chat._id && (
void; @@ -27,5 +29,7 @@ export function TypingAnimation({ } }, [currentIndex, text, speed, onComplete]); - return <>{displayedText}; + // console.log("text", text); + + return {text}; } diff --git a/src/components/ChatAI/index.css b/src/components/ChatAI/index.css new file mode 100644 index 00000000..6fd10af6 --- /dev/null +++ b/src/components/ChatAI/index.css @@ -0,0 +1,8 @@ +.markdown-content p { + margin: 1em 0; + line-height: 1.6; +} + +.markdown-content strong { + font-weight: bold; +} diff --git a/src/components/ChatAI/index.tsx b/src/components/ChatAI/index.tsx index 8665f320..155819cc 100644 --- a/src/components/ChatAI/index.tsx +++ b/src/components/ChatAI/index.tsx @@ -1,41 +1,114 @@ import { useState, useRef, useEffect } from "react"; -import { Menu } from "lucide-react"; +import { Menu, Loader } from "lucide-react"; import { ThemeToggle } from "./ThemeToggle"; import { ChatMessage } from "./ChatMessage"; import { ChatInput } from "./ChatInput"; import { Sidebar } from "./Sidebar"; -import type { Message, Chat } from "./types"; +import type { Chat, Message } from "./types"; import { useTheme } from "../ThemeProvider"; import ChatSwitch from "../SearchChat/ChatSwitch"; import { Footer } from "../SearchChat/Footer"; - -const INITIAL_CHAT: Chat = { - id: "1", - title: "New Chat", - messages: [ - { - id: "1", - role: "assistant", - content: "Hello! How can I help you today?", - timestamp: new Date(), - }, - ], - createdAt: new Date(), -}; +import { tauriFetch } from "../../api/tauriFetchClient"; +import { useWebSocket } from "../../hooks/useWebSocket"; interface ChatAIProps { changeMode: (isChatMode: boolean) => void; } export default function ChatAI({ changeMode }: ChatAIProps) { - const [chats, setChats] = useState([INITIAL_CHAT]); - const [activeChat, setActiveChat] = useState(INITIAL_CHAT); + const [chats, setChats] = useState([]); + const [activeChat, setActiveChat] = useState(); const [isSidebarOpen, setIsSidebarOpen] = useState(false); const [isTyping, setIsTyping] = useState(false); const messagesEndRef = useRef(null); const { theme } = useTheme(); + const [websocketId, setWebsocketId] = useState(""); + const [curMessage, setCurMessage] = useState(""); + const [curChatEnd, setCurChatEnd] = useState(true); + const { messages, setMessages } = useWebSocket( + "ws://localhost:2900/ws", + (msg) => { + if (msg.includes("websocket_session_id")) { + const array = msg.split(" "); + setWebsocketId(array[2]); + } + + if (msg.includes("PRIVATE")) { + if (msg.includes("assistant finished output")) { + setCurChatEnd(true); + } else { + const cleanedData = msg.replace(/^PRIVATE /, ""); + try { + const chunkData = JSON.parse(cleanedData); + setCurMessage((prev) => prev + chunkData.message_chunk); + return chunkData.message_chunk; + } catch (error) { + console.error("JSON Parse error:", error); + } + return ""; + } + } + + return ""; + } + ); + + // websocket + // websocket + useEffect(() => { + if (messages.length === 0 || !activeChat?._id) return; + + const simulateAssistantResponse = () => { + console.log("messages", messages); + + const assistantMessage: Message = { + _id: activeChat._id, + _source: { + type: "assistant", + message: messages, + }, + }; + + const updatedChat = { + ...activeChat, + messages: [...(activeChat.messages || []), assistantMessage], + }; + setMessages(""); + setCurMessage(""); + setActiveChat(updatedChat); + setTimeout(() => setIsTyping(false), 1000); + }; + if (curChatEnd) { + simulateAssistantResponse(); + } + }, [messages, isTyping, curChatEnd]); + + // getChatHistory + useEffect(() => { + getChatHistory(); + }, []); + + const getChatHistory = async () => { + try { + const response = await tauriFetch({ + url: "/chat/_history", + method: "GET", + }); + console.log("_history", response); + const hits = response.data?.hits?.hits || []; + setChats(hits); + if (hits[0]) { + onSelectChat(hits[0]); + } else { + createNewChat(); + } + } catch (error) { + console.error("Failed to fetch user data:", error); + } + }; + const scrollToBottom = () => { messagesEndRef.current?.scrollIntoView({ behavior: "smooth", @@ -45,31 +118,28 @@ export default function ChatAI({ changeMode }: ChatAIProps) { useEffect(() => { scrollToBottom(); - }, [activeChat.messages, isTyping]); + }, [activeChat?.messages, isTyping, curMessage]); - const createNewChat = () => { - const newChat: Chat = { - id: Date.now().toString(), - title: "New Chat", - messages: [ - { - id: "1", - role: "assistant", - content: "Hello! How can I help you today?", - timestamp: new Date(), - }, - ], - createdAt: new Date(), - }; - setChats((prev) => [newChat, ...prev]); - setActiveChat(newChat); - setIsSidebarOpen(false); + const createNewChat = async () => { + try { + const response = await tauriFetch({ + url: "/chat/_new", + method: "POST", + }); + console.log("_new", response); + const newChat: Chat = response.data; + setChats((prev) => [newChat, ...prev]); + setActiveChat(newChat); + setIsSidebarOpen(false); + } catch (error) { + console.error("Failed to fetch user data:", error); + } }; const deleteChat = (chatId: string) => { - setChats((prev) => prev.filter((chat) => chat.id !== chatId)); - if (activeChat.id === chatId) { - const remainingChats = chats.filter((chat) => chat.id !== chatId); + setChats((prev) => prev.filter((chat) => chat._id !== chatId)); + if (activeChat?._id === chatId) { + const remainingChats = chats.filter((chat) => chat._id !== chatId); if (remainingChats.length > 0) { setActiveChat(remainingChats[0]); } else { @@ -78,54 +148,79 @@ export default function ChatAI({ changeMode }: ChatAIProps) { } }; - const handleSendMessage = (content: string) => { - const newMessage: Message = { - id: Date.now().toString(), - role: "user", - content, - timestamp: new Date(), - }; - - const updatedChat = { - ...activeChat, - title: - activeChat.messages.length === 1 - ? content.slice(0, 30) + "..." - : activeChat.title, - messages: [...activeChat.messages, newMessage], - }; - - setActiveChat(updatedChat); - setChats((prev) => - prev.map((chat) => (chat.id === activeChat.id ? updatedChat : chat)) - ); - setIsTyping(true); - - // Simulate assistant response - setTimeout(() => { - const assistantMessage: Message = { - id: (Date.now() + 1).toString(), - role: "assistant", - content: - "This is a simulated response. In a real application, this would be connected to an AI backend.", - timestamp: new Date(), + const handleSendMessage = async (content: string) => { + if (!activeChat?._id) return; + try { + const response = await tauriFetch({ + url: `/chat/${activeChat?._id}/_send`, + method: "POST", + headers: { + WEBSOCKET_SESSION_ID: websocketId, + }, + body: JSON.stringify({ message: content }), + }); + console.log("_send", response, websocketId); + const updatedChat: Chat = { + ...activeChat, + messages: [...(activeChat?.messages || []), ...(response.data || [])], }; - const finalChat = { - ...updatedChat, - messages: [...updatedChat.messages, assistantMessage], - }; + setActiveChat(updatedChat); + setIsTyping(true); + setCurChatEnd(false); + } catch (error) { + console.error("Failed to fetch user data:", error); + } + }; - setActiveChat(finalChat); - setChats((prev) => - prev.map((chat) => (chat.id === activeChat.id ? finalChat : chat)) - ); - setTimeout(() => setIsTyping(false), 500); - }, 1000); + const chatHistory = async (chat: Chat) => { + try { + const response = await tauriFetch({ + url: `/chat/${chat._id}/_history`, + method: "GET", + }); + console.log("id_history", response); + const hits = response.data?.hits?.hits || []; + const updatedChat: Chat = { + ...chat, + messages: hits, + }; + setActiveChat(updatedChat); + } catch (error) { + console.error("Failed to fetch user data:", error); + } + }; + + const chatClose = async () => { + if (!activeChat?._id) return; + try { + const response = await tauriFetch({ + url: `/chat/${activeChat._id}/_close`, + method: "POST", + }); + console.log("_close", response); + } catch (error) { + console.error("Failed to fetch user data:", error); + } + }; + + const onSelectChat = async (chat: any) => { + chatClose(); + try { + const response = await tauriFetch({ + url: `/chat/${chat._id}/_open`, + method: "POST", + }); + console.log("_open", response); + chatHistory(response.data); + setIsSidebarOpen(false); + } catch (error) { + console.error("Failed to fetch user data:", error); + } }; return ( -
+
{/* Sidebar */}
- { - setActiveChat(chat); - setIsSidebarOpen(false); - }} - onDeleteChat={deleteChat} - /> + {activeChat ? ( + + ) : null}
{/* Main content */} @@ -178,18 +272,44 @@ export default function ChatAI({ changeMode }: ChatAIProps) { {/* Chat messages */} -
- {activeChat.messages.map((message, index) => ( +
+ {activeChat?.messages?.map((message, index) => ( ))} + {!curChatEnd && activeChat?._id ? ( + + ) : null} + {/* Loading */} + {/* {isTyping && ( +
+ +
+ )} */} + {isTyping && ( +
+
+
+
+
+ )}
@@ -199,12 +319,16 @@ export default function ChatAI({ changeMode }: ChatAIProps) { theme === "dark" ? "border-gray-800" : "border-gray-200" }`} > - +
-
+
); } diff --git a/src/components/ChatAI/types.ts b/src/components/ChatAI/types.ts index b9af1e1b..1f9ac968 100644 --- a/src/components/ChatAI/types.ts +++ b/src/components/ChatAI/types.ts @@ -1,13 +1,26 @@ export interface Message { - id: string; - role: 'user' | 'assistant'; - content: string; - timestamp: Date; + _id: string; + _source: ISource; + [key: string]: any; } +export interface ISource { + id?: string; + created?: string; + updated?: string; + status?: string; + session_id?: string; + type?: string; + message?: any; +} export interface Chat { - id: string; - title: string; - messages: Message[]; - createdAt: Date; -} \ No newline at end of file + _id: string; + _index?: string; + _type?: string; + _source?: ISource; + _score?: number; + found?: boolean; + title?: string; + messages?: any[]; + [key: string]: any; +} diff --git a/src/components/SearchChat/Footer.tsx b/src/components/SearchChat/Footer.tsx index 5ef5b28e..7f7160ba 100644 --- a/src/components/SearchChat/Footer.tsx +++ b/src/components/SearchChat/Footer.tsx @@ -44,7 +44,7 @@ export const Footer = ({ isChat }: FooterProps) => { return (
diff --git a/src/components/SearchChat/index.tsx b/src/components/SearchChat/index.tsx index e7c2f3f1..e82b13f1 100644 --- a/src/components/SearchChat/index.tsx +++ b/src/components/SearchChat/index.tsx @@ -6,14 +6,13 @@ import Search from "./Search"; import ChatAI from "../ChatAI"; export default function SearchChat() { - const [isChatMode, setIsChatMode] = useState(false); + const [isChatMode, setIsChatMode] = useState(true); async function changeMode(value: boolean) { - console.log(11111, value); if (value) { - await getCurrentWebviewWindow().setSize(new LogicalSize(900, 700)); + await getCurrentWebviewWindow()?.setSize(new LogicalSize(900, 800)); } else { - await getCurrentWebviewWindow().setSize(new LogicalSize(800, 110)); + await getCurrentWebviewWindow()?.setSize(new LogicalSize(900, 110)); } setIsChatMode(value); } diff --git a/src/hooks/useWebSocket.ts b/src/hooks/useWebSocket.ts new file mode 100644 index 00000000..b425eb59 --- /dev/null +++ b/src/hooks/useWebSocket.ts @@ -0,0 +1,55 @@ +import { useEffect, useState } from "react"; + +export function useWebSocket( + url: string, + filterMessages?: (message: string) => string +) { + const [ws, setWs] = useState(null); + const [messages, setMessages] = useState(""); + const [connected, setConnected] = useState(false); + + useEffect(() => { + // 创建 WebSocket 连接 + const websocket = new WebSocket(url); + + websocket.onopen = () => { + console.log("WebSocket 连接成功"); + setConnected(true); + }; + + websocket.onmessage = (event) => { + // console.log("收到消息:", event.data); + const data = filterMessages ? filterMessages(event.data) : event.data; + if (data) { + setMessages((prevMessages) => prevMessages + data); + } + }; + + websocket.onclose = () => { + console.log("WebSocket 连接关闭"); + setConnected(false); + }; + + websocket.onerror = (error) => { + console.error("WebSocket 连接错误:", error); + }; + + // 将 WebSocket 实例保存在状态中 + setWs(websocket); + + // 在组件卸载时关闭 WebSocket 连接 + return () => { + websocket.close(); + }; + }, [url]); + + // 发送消息的函数 + const sendMessage = (message: string) => { + if (ws && connected) { + ws.send(message); + console.log("发送消息:", message); + } + }; + + return { messages, connected, sendMessage, setMessages }; +} diff --git a/src/main.css b/src/main.css index 5fd36192..541866c1 100644 --- a/src/main.css +++ b/src/main.css @@ -26,7 +26,7 @@ } .dark body { - @apply text-gray-100; + @apply text-gray-100 rounded-lg shadow-lg overflow-hidden antialiased; } } diff --git a/vite.config.ts b/vite.config.ts index 34758c15..06da5522 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -27,5 +27,12 @@ export default defineConfig(async () => ({ // 3. tell vite to ignore watching `src-tauri` ignored: ["**/src-tauri/**"], }, + proxy: { + "/chat": { + target: "http://localhost:2900", + changeOrigin: true, + rewrite: (path) => path.replace(/^\/chat/, ""), + }, + }, }, }));