diff --git a/docs/.vitepress/theme/components/VocabQuiz.vue b/docs/.vitepress/theme/components/VocabQuiz.vue
index 919df48..2e41078 100644
--- a/docs/.vitepress/theme/components/VocabQuiz.vue
+++ b/docs/.vitepress/theme/components/VocabQuiz.vue
@@ -66,6 +66,15 @@
+
+
+
+
@@ -125,6 +134,8 @@ import yaml from "yaml";
import SentenceAudio from "./SentenceAudio.vue";
import { supabase } from "../supabase.ts";
import { IVoc, Voc } from "../types.ts";
+import { LLMstream } from "../llm.ts";
+import MarkdownIt from "markdown-it";
const { fmKey = "vocabulary", category } = defineProps<{
fmKey?: string;
@@ -140,6 +151,8 @@ const exIndex = ref(0);
const isEdit = ref(false);
const vEditContent = ref("");
+const exNotes = ref("");
+
const quizState = reactive({
vChosenCats: [] as string[],
isAutoplayAudio: true,
@@ -275,6 +288,8 @@ function makeNewExIndex() {
}
function mark(isRight: boolean) {
+ exNotes.value = "";
+
nextTick(() => {
if (sentenceDetails.value) {
sentenceDetails.value.open = false;
@@ -378,6 +393,32 @@ async function onVContentSave() {
isEdit.value = false;
}
+const markdownIt = new MarkdownIt({
+ html: false,
+ breaks: true,
+ linkify: true,
+ typographer: true,
+});
+
+async function explainSentence(s: string) {
+ for await (const data of LLMstream({
+ model: "deepseek-v4-flash",
+ messages: [
+ {
+ role: "system",
+ content:
+ "Explain how this sentence works in 500 characters. Give key vocabularies.",
+ },
+ { role: "user", content: s },
+ ],
+ })) {
+ if (data.done) return;
+ if (data.content) {
+ exNotes.value += data.content;
+ }
+ }
+}
+
function onModalClickOutside(ev: MouseEvent) {
if (!(ev.target instanceof HTMLElement)) return;
if (ev.target.matches('[name="edit"]')) return;
diff --git a/docs/.vitepress/theme/llm.ts b/docs/.vitepress/theme/llm.ts
new file mode 100644
index 0000000..a946bce
--- /dev/null
+++ b/docs/.vitepress/theme/llm.ts
@@ -0,0 +1,115 @@
+import { fetch } from "@tauri-apps/plugin-http";
+
+//@ts-ignore
+const { VITE_LLM_API_KEY } = import.meta.env;
+
+// Minimal type definitions
+type DeepSeekChunk = {
+ choices: Array<{
+ delta: {
+ content?: string;
+ reasoning_content?: string;
+ };
+ }>;
+};
+
+// Type guard - simple and effective
+function isValidChunk(data: unknown): data is DeepSeekChunk {
+ return (
+ typeof data === "object" &&
+ data !== null &&
+ "choices" in data &&
+ Array.isArray((data as any).choices) &&
+ (data as any).choices.length > 0 &&
+ "delta" in (data as any).choices[0]
+ );
+}
+
+// Helper - one-liner safe parse
+function safeParse(text: string): T | null {
+ try {
+ return JSON.parse(text);
+ } catch {
+ return null;
+ }
+}
+
+export async function* LLMstream(body: {
+ model: string;
+ messages: {
+ role: "system" | "user";
+ content: string;
+ }[];
+}) {
+ if (!VITE_LLM_API_KEY) return;
+
+ let endpoint = "https://api.deepseek.com/chat/completions";
+
+ const response = await fetch(endpoint, {
+ method: "POST",
+ headers: {
+ Authorization: `Bearer ${VITE_LLM_API_KEY}`,
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ ...body,
+ stream: true,
+ }),
+ });
+
+ if (!response.ok || !response.body) {
+ throw response;
+ }
+
+ const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
+
+ let buffer = "";
+
+ while (true) {
+ const { value, done } = await reader.read();
+ if (done) break;
+
+ buffer += value;
+
+ const events = buffer.split("\n\n");
+ buffer = events.pop() || "";
+
+ for (const event of events) {
+ for (const line of event.split("\n")) {
+ const DATA_START = "data: ";
+
+ if (!line.startsWith(DATA_START)) continue;
+ const payload = line.slice(DATA_START.length);
+
+ if (payload === "[DONE]") {
+ yield { content: "", done: true };
+ return;
+ }
+
+ const chunk = safeParse(payload);
+
+ if (!chunk?.choices?.[0]?.delta) continue;
+
+ const delta = chunk.choices[0].delta;
+
+ // Yield content if present
+ if (delta.content) {
+ yield {
+ content: delta.content,
+ reasoning: delta.reasoning_content,
+ done: false,
+ };
+ }
+
+ // Yield reasoning separately if present and no content
+ if (delta.reasoning_content && !delta.content) {
+ yield {
+ content: "",
+ reasoning: delta.reasoning_content,
+ done: false,
+ };
+ }
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
index 60526ab..a11d62f 100644
--- a/package.json
+++ b/package.json
@@ -26,7 +26,10 @@
},
"dependencies": {
"@supabase/supabase-js": "^2.110.5",
+ "@tauri-apps/plugin-http": "~2.5.9",
+ "@types/markdown-it": "^14.2.0",
"@types/node": "^24.13.3",
+ "markdown-it": "^14.3.0",
"pdfjs-dist": "^5.7.284",
"tauri-plugin-tts-api": "^0.1.11",
"vite": "^8.1.5",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ab5e6c4..36969ec 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -11,9 +11,18 @@ importers:
'@supabase/supabase-js':
specifier: ^2.110.5
version: 2.110.5
+ '@tauri-apps/plugin-http':
+ specifier: ~2.5.9
+ version: 2.5.9
+ '@types/markdown-it':
+ specifier: ^14.2.0
+ version: 14.2.0
'@types/node':
specifier: ^24.13.3
version: 24.13.3
+ markdown-it:
+ specifier: ^14.3.0
+ version: 14.3.0
pdfjs-dist:
specifier: ^5.7.284
version: 5.7.284
@@ -692,6 +701,9 @@ packages:
engines: {node: '>= 10'}
hasBin: true
+ '@tauri-apps/plugin-http@2.5.9':
+ resolution: {integrity: sha512-lCiY0+vs4HvIUSvZrBs8TC3TiCB0MOPRmiUjTq4prW7SlcJE2jdLeT6KBsJrT9Tlplufl7W1pY6SFAO3gCWxDA==}
+
'@tybys/wasm-util@0.10.3':
resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==}
@@ -704,8 +716,8 @@ packages:
'@types/linkify-it@5.0.0':
resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==}
- '@types/markdown-it@14.1.2':
- resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==}
+ '@types/markdown-it@14.2.0':
+ resolution: {integrity: sha512-NoQ2yGlLWj4wpxMs+TYmRKk3thDrQ97agr7sFqfLsAlvoS8SNQuTrlObhFqG9iugdTtgOE9jpJ6FNM4ZGsa5xQ==}
'@types/mdast@4.0.4':
resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
@@ -825,6 +837,9 @@ packages:
peerDependencies:
vue: ^3.5.0
+ argparse@2.0.1:
+ resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
+
birpc@2.9.0:
resolution: {integrity: sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==}
@@ -854,6 +869,10 @@ packages:
devlop@1.1.0:
resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
+ entities@4.5.0:
+ resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
+ engines: {node: '>=0.12'}
+
entities@7.0.1:
resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==}
engines: {node: '>=0.12'}
@@ -973,15 +992,25 @@ packages:
resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==}
engines: {node: '>= 12.0.0'}
+ linkify-it@5.0.2:
+ resolution: {integrity: sha512-ONTm2jCMAVZjgQa/Fy1kScXsuOoF5NPTsoFBdE1KVIZ2vAh/r9+Bqo+0jINCBYnavTPQZz38QzFTme79ENoN3Q==}
+
magic-string@0.30.21:
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
mark.js@8.11.1:
resolution: {integrity: sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==}
+ markdown-it@14.3.0:
+ resolution: {integrity: sha512-RCEsPjR+sr0x+AuYp601tKTkgFG4YEPLCzHST3cQ/fhlJkqAkz1L2/Qbp1j9qw5SBwQHFBoW8+hoN5xssOF0Tw==}
+ hasBin: true
+
mdast-util-to-hast@13.2.1:
resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==}
+ mdurl@2.1.0:
+ resolution: {integrity: sha512-1+HBaOx0zi/dQWht8rNv9MYf9qqpqL/kxI0hXImU6Y547zM6Sni8BQibt7ifgMcYtQg41ao3Ivd6cnSM86inpg==}
+
micromark-util-character@2.1.1:
resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==}
@@ -1036,6 +1065,10 @@ packages:
property-information@7.2.0:
resolution: {integrity: sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==}
+ punycode.js@2.3.1:
+ resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==}
+ engines: {node: '>=6'}
+
regex-recursion@6.0.2:
resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==}
@@ -1084,6 +1117,9 @@ packages:
tslib@2.8.1:
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
+ uc.micro@2.1.0:
+ resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==}
+
undici-types@7.18.2:
resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==}
@@ -1650,6 +1686,10 @@ snapshots:
'@tauri-apps/cli-win32-ia32-msvc': 2.11.4
'@tauri-apps/cli-win32-x64-msvc': 2.11.4
+ '@tauri-apps/plugin-http@2.5.9':
+ dependencies:
+ '@tauri-apps/api': 2.11.1
+
'@tybys/wasm-util@0.10.3':
dependencies:
tslib: 2.8.1
@@ -1663,7 +1703,7 @@ snapshots:
'@types/linkify-it@5.0.0': {}
- '@types/markdown-it@14.1.2':
+ '@types/markdown-it@14.2.0':
dependencies:
'@types/linkify-it': 5.0.0
'@types/mdurl': 2.0.0
@@ -1778,6 +1818,8 @@ snapshots:
dependencies:
vue: 3.5.39
+ argparse@2.0.1: {}
+
birpc@2.9.0: {}
ccount@2.0.1: {}
@@ -1798,6 +1840,8 @@ snapshots:
dependencies:
dequal: 2.0.3
+ entities@4.5.0: {}
+
entities@7.0.1: {}
esbuild@0.28.1:
@@ -1915,12 +1959,25 @@ snapshots:
lightningcss-win32-arm64-msvc: 1.32.0
lightningcss-win32-x64-msvc: 1.32.0
+ linkify-it@5.0.2:
+ dependencies:
+ uc.micro: 2.1.0
+
magic-string@0.30.21:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
mark.js@8.11.1: {}
+ markdown-it@14.3.0:
+ dependencies:
+ argparse: 2.0.1
+ entities: 4.5.0
+ linkify-it: 5.0.2
+ mdurl: 2.1.0
+ punycode.js: 2.3.1
+ uc.micro: 2.1.0
+
mdast-util-to-hast@13.2.1:
dependencies:
'@types/hast': 3.0.4
@@ -1933,6 +1990,8 @@ snapshots:
unist-util-visit: 5.1.0
vfile: 6.0.3
+ mdurl@2.1.0: {}
+
micromark-util-character@2.1.1:
dependencies:
micromark-util-symbol: 2.0.1
@@ -1986,6 +2045,8 @@ snapshots:
property-information@7.2.0: {}
+ punycode.js@2.3.1: {}
+
regex-recursion@6.0.2:
dependencies:
regex-utilities: 2.3.0
@@ -2083,6 +2144,8 @@ snapshots:
tslib@2.8.1: {}
+ uc.micro@2.1.0: {}
+
undici-types@7.18.2: {}
unist-util-is@6.0.1:
@@ -2154,7 +2217,7 @@ snapshots:
'@shikijs/core': 3.23.0
'@shikijs/transformers': 3.23.0
'@shikijs/types': 3.23.0
- '@types/markdown-it': 14.1.2
+ '@types/markdown-it': 14.2.0
'@vitejs/plugin-vue': 6.0.7(vite@7.3.6(@types/node@24.13.3)(lightningcss@1.32.0)(yaml@2.9.0))(vue@3.5.39)
'@vue/devtools-api': 8.1.5
'@vue/shared': 3.5.39
diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock
index 856f79c..e9c3fb0 100644
--- a/src-tauri/Cargo.lock
+++ b/src-tauri/Cargo.lock
@@ -84,6 +84,7 @@ dependencies = [
"serde_json",
"tauri",
"tauri-build",
+ "tauri-plugin-http",
"tauri-plugin-log",
"tauri-plugin-tts",
]
@@ -458,6 +459,17 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
+[[package]]
+name = "chacha20"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81"
+dependencies = [
+ "cfg-if",
+ "cpufeatures 0.3.0",
+ "rand_core 0.10.1",
+]
+
[[package]]
name = "chrono"
version = "0.4.44"
@@ -517,10 +529,29 @@ version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747"
dependencies = [
+ "percent-encoding",
"time",
"version_check",
]
+[[package]]
+name = "cookie_store"
+version = "0.22.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "15b2c103cf610ec6cae3da84a766285b42fd16aad564758459e6ecf128c75206"
+dependencies = [
+ "cookie",
+ "document-features",
+ "idna",
+ "log",
+ "publicsuffix",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "time",
+ "url",
+]
+
[[package]]
name = "core-foundation"
version = "0.9.4"
@@ -591,6 +622,15 @@ dependencies = [
"libc",
]
+[[package]]
+name = "cpufeatures"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
+dependencies = [
+ "libc",
+]
+
[[package]]
name = "crc32fast"
version = "1.5.0"
@@ -709,6 +749,12 @@ dependencies = [
"syn 2.0.117",
]
+[[package]]
+name = "data-url"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376"
+
[[package]]
name = "deranged"
version = "0.5.8"
@@ -830,6 +876,15 @@ dependencies = [
"syn 2.0.117",
]
+[[package]]
+name = "document-features"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61"
+dependencies = [
+ "litrs",
+]
+
[[package]]
name = "dom_query"
version = "0.27.0"
@@ -928,6 +983,15 @@ version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7"
+[[package]]
+name = "encoding_rs"
+version = "0.8.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
+dependencies = [
+ "cfg-if",
+]
+
[[package]]
name = "env_filter"
version = "0.1.4"
@@ -1282,8 +1346,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
dependencies = [
"cfg-if",
+ "js-sys",
"libc",
"wasi 0.11.1+wasi-snapshot-preview1",
+ "wasm-bindgen",
]
[[package]]
@@ -1305,10 +1371,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
dependencies = [
"cfg-if",
+ "js-sys",
"libc",
"r-efi 6.0.0",
+ "rand_core 0.10.1",
"wasip2",
"wasip3",
+ "wasm-bindgen",
]
[[package]]
@@ -1459,6 +1528,25 @@ dependencies = [
"syn 2.0.117",
]
+[[package]]
+name = "h2"
+version = "0.4.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef8e5e5a340588f4452631496976cf8636d4a7ecf600239fdc27615d2530bc16"
+dependencies = [
+ "atomic-waker",
+ "bytes",
+ "fnv",
+ "futures-core",
+ "futures-sink",
+ "http",
+ "indexmap 2.13.0",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tracing",
+]
+
[[package]]
name = "hashbrown"
version = "0.12.3"
@@ -1572,6 +1660,7 @@ dependencies = [
"bytes",
"futures-channel",
"futures-core",
+ "h2",
"http",
"http-body",
"httparse",
@@ -1583,6 +1672,22 @@ dependencies = [
"want",
]
+[[package]]
+name = "hyper-rustls"
+version = "0.27.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f"
+dependencies = [
+ "http",
+ "hyper",
+ "hyper-util",
+ "rustls",
+ "tokio",
+ "tokio-rustls",
+ "tower-service",
+ "webpki-roots",
+]
+
[[package]]
name = "hyper-util"
version = "0.1.20"
@@ -1601,9 +1706,11 @@ dependencies = [
"percent-encoding",
"pin-project-lite",
"socket2",
+ "system-configuration",
"tokio",
"tower-service",
"tracing",
+ "windows-registry",
]
[[package]]
@@ -2016,6 +2123,12 @@ version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77"
+[[package]]
+name = "litrs"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092"
+
[[package]]
name = "lock_api"
version = "0.4.14"
@@ -2034,6 +2147,12 @@ dependencies = [
"value-bag",
]
+[[package]]
+name = "lru-slab"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
+
[[package]]
name = "mac"
version = "0.1.1"
@@ -2338,6 +2457,7 @@ checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272"
dependencies = [
"bitflags 2.11.0",
"block2",
+ "libc",
"objc2",
"objc2-core-foundation",
]
@@ -2814,6 +2934,12 @@ dependencies = [
"unicode-ident",
]
+[[package]]
+name = "psl-types"
+version = "2.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac"
+
[[package]]
name = "ptr_meta"
version = "0.1.4"
@@ -2834,6 +2960,16 @@ dependencies = [
"syn 1.0.109",
]
+[[package]]
+name = "publicsuffix"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f42ea446cab60335f76979ec15e12619a2165b5ae2c12166bef27d283a9fadf"
+dependencies = [
+ "idna",
+ "psl-types",
+]
+
[[package]]
name = "quick-xml"
version = "0.38.4"
@@ -2843,6 +2979,62 @@ dependencies = [
"memchr",
]
+[[package]]
+name = "quinn"
+version = "0.11.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c1a41e437b6bbd489372cd4971de128e85c855f56c57f283d20ff016cf7c0a8"
+dependencies = [
+ "bytes",
+ "cfg_aliases",
+ "pin-project-lite",
+ "quinn-proto",
+ "quinn-udp",
+ "rustc-hash",
+ "rustls",
+ "socket2",
+ "thiserror 2.0.18",
+ "tokio",
+ "tracing",
+ "web-time",
+]
+
+[[package]]
+name = "quinn-proto"
+version = "0.11.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04759210543be93709136e28212294a659ef5001836ff4eab4d663e4529bba83"
+dependencies = [
+ "bytes",
+ "getrandom 0.4.2",
+ "lru-slab",
+ "rand 0.10.2",
+ "rand_pcg 0.10.2",
+ "ring",
+ "rustc-hash",
+ "rustls",
+ "rustls-pki-types",
+ "slab",
+ "thiserror 2.0.18",
+ "tinyvec",
+ "tracing",
+ "web-time",
+]
+
+[[package]]
+name = "quinn-udp"
+version = "0.5.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694"
+dependencies = [
+ "cfg_aliases",
+ "libc",
+ "once_cell",
+ "socket2",
+ "tracing",
+ "windows-sys 0.61.2",
+]
+
[[package]]
name = "quote"
version = "1.0.45"
@@ -2881,7 +3073,7 @@ dependencies = [
"rand_chacha 0.2.2",
"rand_core 0.5.1",
"rand_hc",
- "rand_pcg",
+ "rand_pcg 0.2.1",
]
[[package]]
@@ -2895,6 +3087,17 @@ dependencies = [
"rand_core 0.6.4",
]
+[[package]]
+name = "rand"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80"
+dependencies = [
+ "chacha20",
+ "getrandom 0.4.2",
+ "rand_core 0.10.1",
+]
+
[[package]]
name = "rand_chacha"
version = "0.2.2"
@@ -2933,6 +3136,12 @@ dependencies = [
"getrandom 0.2.17",
]
+[[package]]
+name = "rand_core"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
+
[[package]]
name = "rand_hc"
version = "0.2.0"
@@ -2951,6 +3160,15 @@ dependencies = [
"rand_core 0.5.1",
]
+[[package]]
+name = "rand_pcg"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a"
+dependencies = [
+ "rand_core 0.10.1",
+]
+
[[package]]
name = "raw-window-handle"
version = "0.6.2"
@@ -3035,6 +3253,49 @@ dependencies = [
"bytecheck",
]
+[[package]]
+name = "reqwest"
+version = "0.12.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147"
+dependencies = [
+ "base64 0.22.1",
+ "bytes",
+ "cookie",
+ "cookie_store",
+ "encoding_rs",
+ "futures-core",
+ "h2",
+ "http",
+ "http-body",
+ "http-body-util",
+ "hyper",
+ "hyper-rustls",
+ "hyper-util",
+ "js-sys",
+ "log",
+ "mime",
+ "percent-encoding",
+ "pin-project-lite",
+ "quinn",
+ "rustls",
+ "rustls-pki-types",
+ "serde",
+ "serde_json",
+ "serde_urlencoded",
+ "sync_wrapper",
+ "tokio",
+ "tokio-rustls",
+ "tower",
+ "tower-http",
+ "tower-service",
+ "url",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "webpki-roots",
+]
+
[[package]]
name = "reqwest"
version = "0.13.2"
@@ -3069,6 +3330,20 @@ dependencies = [
"web-sys",
]
+[[package]]
+name = "ring"
+version = "0.17.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
+dependencies = [
+ "cc",
+ "cfg-if",
+ "getrandom 0.2.17",
+ "libc",
+ "untrusted",
+ "windows-sys 0.52.0",
+]
+
[[package]]
name = "rkyv"
version = "0.7.46"
@@ -3129,12 +3404,53 @@ dependencies = [
"semver",
]
+[[package]]
+name = "rustls"
+version = "0.23.43"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0283386ce02abc0151e1761d08802dfe86c173b0b494af5cbc086574e453da06"
+dependencies = [
+ "once_cell",
+ "ring",
+ "rustls-pki-types",
+ "rustls-webpki",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "rustls-pki-types"
+version = "1.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96"
+dependencies = [
+ "web-time",
+ "zeroize",
+]
+
+[[package]]
+name = "rustls-webpki"
+version = "0.103.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2"
+dependencies = [
+ "ring",
+ "rustls-pki-types",
+ "untrusted",
+]
+
[[package]]
name = "rustversion"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
+[[package]]
+name = "ryu"
+version = "1.0.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
+
[[package]]
name = "same-file"
version = "1.0.6"
@@ -3349,6 +3665,18 @@ dependencies = [
"serde_core",
]
+[[package]]
+name = "serde_urlencoded"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
+dependencies = [
+ "form_urlencoded",
+ "itoa",
+ "ryu",
+ "serde",
+]
+
[[package]]
name = "serde_with"
version = "3.18.0"
@@ -3428,7 +3756,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [
"cfg-if",
- "cpufeatures",
+ "cpufeatures 0.2.17",
"digest",
]
@@ -3613,6 +3941,12 @@ version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+[[package]]
+name = "subtle"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
+
[[package]]
name = "swift-rs"
version = "1.0.7"
@@ -3666,6 +4000,27 @@ dependencies = [
"syn 2.0.117",
]
+[[package]]
+name = "system-configuration"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b"
+dependencies = [
+ "bitflags 2.11.0",
+ "core-foundation 0.9.4",
+ "system-configuration-sys",
+]
+
+[[package]]
+name = "system-configuration-sys"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
[[package]]
name = "system-deps"
version = "6.2.2"
@@ -3770,7 +4125,7 @@ dependencies = [
"percent-encoding",
"plist",
"raw-window-handle",
- "reqwest",
+ "reqwest 0.13.2",
"serde",
"serde_json",
"serde_repr",
@@ -3871,6 +4226,54 @@ dependencies = [
"walkdir",
]
+[[package]]
+name = "tauri-plugin-fs"
+version = "2.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7ecc274121aca0c036a2b42d1cbe83d368d348f54e0bb8a735c2b1548e8f371"
+dependencies = [
+ "anyhow",
+ "dunce",
+ "glob",
+ "log",
+ "objc2-foundation",
+ "percent-encoding",
+ "schemars 0.8.22",
+ "serde",
+ "serde_json",
+ "serde_repr",
+ "tauri",
+ "tauri-plugin",
+ "tauri-utils",
+ "thiserror 2.0.18",
+ "toml 1.1.0+spec-1.1.0",
+ "url",
+]
+
+[[package]]
+name = "tauri-plugin-http"
+version = "2.5.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5bd512048e1985b7ec78f96d99083e2ddaf7e0d906b2b63c44ce5bb8b894067"
+dependencies = [
+ "bytes",
+ "cookie_store",
+ "data-url",
+ "http",
+ "regex",
+ "reqwest 0.12.28",
+ "schemars 0.8.22",
+ "serde",
+ "serde_json",
+ "tauri",
+ "tauri-plugin",
+ "tauri-plugin-fs",
+ "thiserror 2.0.18",
+ "tokio",
+ "url",
+ "urlpattern",
+]
+
[[package]]
name = "tauri-plugin-log"
version = "2.8.0"
@@ -4148,9 +4551,31 @@ dependencies = [
"mio",
"pin-project-lite",
"socket2",
+ "tokio-macros",
"windows-sys 0.61.2",
]
+[[package]]
+name = "tokio-macros"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "tokio-rustls"
+version = "0.26.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61"
+dependencies = [
+ "rustls",
+ "tokio",
+]
+
[[package]]
name = "tokio-util"
version = "0.7.18"
@@ -4191,6 +4616,21 @@ dependencies = [
"winnow 0.7.15",
]
+[[package]]
+name = "toml"
+version = "1.1.0+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8195ca05e4eb728f4ba94f3e3291661320af739c4e43779cbdfae82ab239fcc"
+dependencies = [
+ "indexmap 2.13.0",
+ "serde_core",
+ "serde_spanned 1.1.0",
+ "toml_datetime 1.1.0+spec-1.1.0",
+ "toml_parser",
+ "toml_writer",
+ "winnow 1.0.0",
+]
+
[[package]]
name = "toml_datetime"
version = "0.6.3"
@@ -4477,6 +4917,12 @@ version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
+[[package]]
+name = "untrusted"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
+
[[package]]
name = "url"
version = "2.5.8"
@@ -4735,6 +5181,16 @@ dependencies = [
"wasm-bindgen",
]
+[[package]]
+name = "web-time"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
[[package]]
name = "web_atoms"
version = "0.2.3"
@@ -4791,6 +5247,15 @@ dependencies = [
"system-deps",
]
+[[package]]
+name = "webpki-roots"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a"
+dependencies = [
+ "rustls-pki-types",
+]
+
[[package]]
name = "webview2-com"
version = "0.38.2"
@@ -5021,6 +5486,17 @@ dependencies = [
"windows-link 0.1.3",
]
+[[package]]
+name = "windows-registry"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720"
+dependencies = [
+ "windows-link 0.2.1",
+ "windows-result 0.4.1",
+ "windows-strings 0.5.1",
+]
+
[[package]]
name = "windows-result"
version = "0.2.0"
@@ -5085,6 +5561,15 @@ dependencies = [
"windows-targets 0.42.2",
]
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
[[package]]
name = "windows-sys"
version = "0.59.0"
@@ -5582,6 +6067,12 @@ dependencies = [
"synstructure",
]
+[[package]]
+name = "zeroize"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e"
+
[[package]]
name = "zerotrie"
version = "0.2.3"
diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml
index a12a358..a9620e0 100644
--- a/src-tauri/Cargo.toml
+++ b/src-tauri/Cargo.toml
@@ -24,3 +24,4 @@ log = "0.4"
tauri = { version = "2.10.3", features = [] }
tauri-plugin-log = "2"
tauri-plugin-tts = "0.1.3"
+tauri-plugin-http = "2"
diff --git a/src-tauri/build.rs b/src-tauri/build.rs
index 795b9b7..d860e1e 100644
--- a/src-tauri/build.rs
+++ b/src-tauri/build.rs
@@ -1,3 +1,3 @@
fn main() {
- tauri_build::build()
+ tauri_build::build()
}
diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json
index 24f0c45..320ccc7 100644
--- a/src-tauri/capabilities/default.json
+++ b/src-tauri/capabilities/default.json
@@ -7,6 +7,10 @@
],
"permissions": [
"core:default",
- "tts:default"
+ "tts:default",
+ {
+ "identifier": "http:default",
+ "allow": [{ "url": "https://api.deepseek.com/chat/completions" }]
+ }
]
-}
+}
\ No newline at end of file
diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs
index f347a73..141c4df 100644
--- a/src-tauri/src/lib.rs
+++ b/src-tauri/src/lib.rs
@@ -1,17 +1,18 @@
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
- tauri::Builder::default()
- .plugin(tauri_plugin_tts::init())
- .setup(|app| {
- if cfg!(debug_assertions) {
- app.handle().plugin(
- tauri_plugin_log::Builder::default()
- .level(log::LevelFilter::Info)
- .build(),
- )?;
- }
- Ok(())
- })
- .run(tauri::generate_context!())
- .expect("error while running tauri application");
+ tauri::Builder::default()
+ .plugin(tauri_plugin_http::init())
+ .plugin(tauri_plugin_tts::init())
+ .setup(|app| {
+ if cfg!(debug_assertions) {
+ app.handle().plugin(
+ tauri_plugin_log::Builder::default()
+ .level(log::LevelFilter::Info)
+ .build(),
+ )?;
+ }
+ Ok(())
+ })
+ .run(tauri::generate_context!())
+ .expect("error while running tauri application");
}
diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs
index ad5fe83..69c3a72 100644
--- a/src-tauri/src/main.rs
+++ b/src-tauri/src/main.rs
@@ -2,5 +2,5 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
fn main() {
- app_lib::run();
+ app_lib::run();
}