新增: Rust AI 引擎——prompt/规范化/SSE 解析(B3)
- ai.rs: 完整 Prompt 模板翻译(SYS_BASE/SYS_GENERATE/SYS_POLISH/SYS_CHAT) - norm_style/norm_element/norm_slide/norm_slides: 数据规范化纯函数 - try_parse_json: AI 回复中提取 JSON(兼容 json 代码块标记) - split_chat_reply: 按 %%PPT_JSON%% 分隔自然语言与操作 - 8 个单元测试: 样式规范化/元素解析/幻灯片解析/JSON提取/chat分隔/颜色校验/图表类型 - 40 个测试全部通过
This commit is contained in:
485
src-tauri/Cargo.lock
generated
485
src-tauri/Cargo.lock
generated
@@ -341,6 +341,16 @@ dependencies = [
|
|||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "core-foundation"
|
||||||
|
version = "0.9.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
|
||||||
|
dependencies = [
|
||||||
|
"core-foundation-sys",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation"
|
name = "core-foundation"
|
||||||
version = "0.10.1"
|
version = "0.10.1"
|
||||||
@@ -364,9 +374,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97"
|
checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.13.0",
|
"bitflags 2.13.0",
|
||||||
"core-foundation",
|
"core-foundation 0.10.1",
|
||||||
"core-graphics-types",
|
"core-graphics-types",
|
||||||
"foreign-types",
|
"foreign-types 0.5.0",
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -377,7 +387,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb"
|
checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.13.0",
|
"bitflags 2.13.0",
|
||||||
"core-foundation",
|
"core-foundation 0.10.1",
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -701,6 +711,15 @@ version = "1.2.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7"
|
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]]
|
[[package]]
|
||||||
name = "equivalent"
|
name = "equivalent"
|
||||||
version = "1.0.2"
|
version = "1.0.2"
|
||||||
@@ -718,6 +737,16 @@ dependencies = [
|
|||||||
"typeid",
|
"typeid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "errno"
|
||||||
|
version = "0.3.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fastrand"
|
name = "fastrand"
|
||||||
version = "2.4.1"
|
version = "2.4.1"
|
||||||
@@ -771,6 +800,15 @@ version = "0.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
|
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "foreign-types"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
||||||
|
dependencies = [
|
||||||
|
"foreign-types-shared 0.1.1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "foreign-types"
|
name = "foreign-types"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
@@ -778,7 +816,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965"
|
checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"foreign-types-macros",
|
"foreign-types-macros",
|
||||||
"foreign-types-shared",
|
"foreign-types-shared 0.3.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -792,6 +830,12 @@ dependencies = [
|
|||||||
"syn 2.0.118",
|
"syn 2.0.118",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "foreign-types-shared"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "foreign-types-shared"
|
name = "foreign-types-shared"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
@@ -807,6 +851,21 @@ dependencies = [
|
|||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures"
|
||||||
|
version = "0.3.32"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d"
|
||||||
|
dependencies = [
|
||||||
|
"futures-channel",
|
||||||
|
"futures-core",
|
||||||
|
"futures-executor",
|
||||||
|
"futures-io",
|
||||||
|
"futures-sink",
|
||||||
|
"futures-task",
|
||||||
|
"futures-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-channel"
|
name = "futures-channel"
|
||||||
version = "0.3.32"
|
version = "0.3.32"
|
||||||
@@ -814,6 +873,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d"
|
checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -868,6 +928,7 @@ version = "0.3.32"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
|
checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-io",
|
"futures-io",
|
||||||
"futures-macro",
|
"futures-macro",
|
||||||
@@ -1169,6 +1230,25 @@ dependencies = [
|
|||||||
"syn 2.0.118",
|
"syn 2.0.118",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "h2"
|
||||||
|
version = "0.4.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155"
|
||||||
|
dependencies = [
|
||||||
|
"atomic-waker",
|
||||||
|
"bytes",
|
||||||
|
"fnv",
|
||||||
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
|
"http",
|
||||||
|
"indexmap 2.14.0",
|
||||||
|
"slab",
|
||||||
|
"tokio",
|
||||||
|
"tokio-util",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.12.3"
|
version = "0.12.3"
|
||||||
@@ -1258,6 +1338,7 @@ dependencies = [
|
|||||||
"bytes",
|
"bytes",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
"h2",
|
||||||
"http",
|
"http",
|
||||||
"http-body",
|
"http-body",
|
||||||
"httparse",
|
"httparse",
|
||||||
@@ -1268,6 +1349,37 @@ dependencies = [
|
|||||||
"want",
|
"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",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hyper-tls"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"http-body-util",
|
||||||
|
"hyper",
|
||||||
|
"hyper-util",
|
||||||
|
"native-tls",
|
||||||
|
"tokio",
|
||||||
|
"tokio-native-tls",
|
||||||
|
"tower-service",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper-util"
|
name = "hyper-util"
|
||||||
version = "0.1.20"
|
version = "0.1.20"
|
||||||
@@ -1286,9 +1398,11 @@ dependencies = [
|
|||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"socket2",
|
"socket2",
|
||||||
|
"system-configuration",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
"windows-registry",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1653,6 +1767,12 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linux-raw-sys"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "litemap"
|
name = "litemap"
|
||||||
version = "0.8.2"
|
version = "0.8.2"
|
||||||
@@ -1748,6 +1868,23 @@ dependencies = [
|
|||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "native-tls"
|
||||||
|
version = "0.2.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"openssl",
|
||||||
|
"openssl-probe",
|
||||||
|
"openssl-sys",
|
||||||
|
"schannel",
|
||||||
|
"security-framework",
|
||||||
|
"security-framework-sys",
|
||||||
|
"tempfile",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ndk"
|
name = "ndk"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
@@ -2016,6 +2153,49 @@ version = "1.21.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "openssl"
|
||||||
|
version = "0.10.81"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "77823a27f0babb03091cb9ed9ef80af3b39dbc82f97e8fa530374b7dafd87a45"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.13.0",
|
||||||
|
"cfg-if",
|
||||||
|
"foreign-types 0.3.2",
|
||||||
|
"libc",
|
||||||
|
"openssl-macros",
|
||||||
|
"openssl-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "openssl-macros"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.118",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "openssl-probe"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "openssl-sys"
|
||||||
|
version = "0.9.117"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b47e7e6bb2c38cd930d25a23b40fa52e068c10e85f3e03a7f5ba5aaca5713695"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
"pkg-config",
|
||||||
|
"vcpkg",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "option-ext"
|
name = "option-ext"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@@ -2368,6 +2548,49 @@ version = "0.8.11"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
|
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
|
||||||
|
|
||||||
|
[[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",
|
||||||
|
"encoding_rs",
|
||||||
|
"futures-core",
|
||||||
|
"futures-util",
|
||||||
|
"h2",
|
||||||
|
"http",
|
||||||
|
"http-body",
|
||||||
|
"http-body-util",
|
||||||
|
"hyper",
|
||||||
|
"hyper-rustls",
|
||||||
|
"hyper-tls",
|
||||||
|
"hyper-util",
|
||||||
|
"js-sys",
|
||||||
|
"log",
|
||||||
|
"mime",
|
||||||
|
"native-tls",
|
||||||
|
"percent-encoding",
|
||||||
|
"pin-project-lite",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"serde_urlencoded",
|
||||||
|
"sync_wrapper",
|
||||||
|
"tokio",
|
||||||
|
"tokio-native-tls",
|
||||||
|
"tokio-util",
|
||||||
|
"tower",
|
||||||
|
"tower-http",
|
||||||
|
"tower-service",
|
||||||
|
"url",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"wasm-bindgen-futures",
|
||||||
|
"wasm-streams 0.4.2",
|
||||||
|
"web-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reqwest"
|
name = "reqwest"
|
||||||
version = "0.13.4"
|
version = "0.13.4"
|
||||||
@@ -2398,10 +2621,24 @@ dependencies = [
|
|||||||
"url",
|
"url",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"wasm-bindgen-futures",
|
"wasm-bindgen-futures",
|
||||||
"wasm-streams",
|
"wasm-streams 0.5.0",
|
||||||
"web-sys",
|
"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]]
|
[[package]]
|
||||||
name = "rustc-hash"
|
name = "rustc-hash"
|
||||||
version = "2.1.3"
|
version = "2.1.3"
|
||||||
@@ -2417,12 +2654,64 @@ dependencies = [
|
|||||||
"semver",
|
"semver",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustix"
|
||||||
|
version = "1.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.13.0",
|
||||||
|
"errno",
|
||||||
|
"libc",
|
||||||
|
"linux-raw-sys",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls"
|
||||||
|
version = "0.23.41"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6b92b125634d9b795e7beca796cc790df15a7fb38323bf3196fda83292d06b1f"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"rustls-webpki",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-pki-types"
|
||||||
|
version = "1.15.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "764899a24af3980067ee14bc143654f297b22eaebfe3c7b6b211920a5a59b046"
|
||||||
|
dependencies = [
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-webpki"
|
||||||
|
version = "0.103.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e"
|
||||||
|
dependencies = [
|
||||||
|
"ring",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"untrusted",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustversion"
|
name = "rustversion"
|
||||||
version = "1.0.23"
|
version = "1.0.23"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
|
checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "same-file"
|
name = "same-file"
|
||||||
version = "1.0.6"
|
version = "1.0.6"
|
||||||
@@ -2432,6 +2721,15 @@ dependencies = [
|
|||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "schannel"
|
||||||
|
version = "0.1.29"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "schemars"
|
name = "schemars"
|
||||||
version = "0.8.22"
|
version = "0.8.22"
|
||||||
@@ -2489,6 +2787,29 @@ version = "1.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "security-framework"
|
||||||
|
version = "3.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.13.0",
|
||||||
|
"core-foundation 0.10.1",
|
||||||
|
"core-foundation-sys",
|
||||||
|
"libc",
|
||||||
|
"security-framework-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "security-framework-sys"
|
||||||
|
version = "2.17.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3"
|
||||||
|
dependencies = [
|
||||||
|
"core-foundation-sys",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "selectors"
|
name = "selectors"
|
||||||
version = "0.36.1"
|
version = "0.36.1"
|
||||||
@@ -2613,6 +2934,18 @@ dependencies = [
|
|||||||
"serde_core",
|
"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]]
|
[[package]]
|
||||||
name = "serde_with"
|
name = "serde_with"
|
||||||
version = "3.21.0"
|
version = "3.21.0"
|
||||||
@@ -2693,6 +3026,16 @@ version = "2.0.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
|
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook-registry"
|
||||||
|
version = "1.4.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
|
||||||
|
dependencies = [
|
||||||
|
"errno",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "simd-adler32"
|
name = "simd-adler32"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
@@ -2811,6 +3154,12 @@ version = "0.11.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "subtle"
|
||||||
|
version = "2.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "swift-rs"
|
name = "swift-rs"
|
||||||
version = "1.0.7"
|
version = "1.0.7"
|
||||||
@@ -2863,6 +3212,27 @@ dependencies = [
|
|||||||
"syn 2.0.118",
|
"syn 2.0.118",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "system-configuration"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.13.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]]
|
[[package]]
|
||||||
name = "system-deps"
|
name = "system-deps"
|
||||||
version = "6.2.2"
|
version = "6.2.2"
|
||||||
@@ -2884,7 +3254,7 @@ checksum = "d1c93047acf68669466a34690ac58cca7010bd1b201e1ec86f1fd0a75d3dd4a9"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.13.0",
|
"bitflags 2.13.0",
|
||||||
"block2",
|
"block2",
|
||||||
"core-foundation",
|
"core-foundation 0.10.1",
|
||||||
"core-graphics",
|
"core-graphics",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
"dbus",
|
"dbus",
|
||||||
@@ -2963,7 +3333,7 @@ dependencies = [
|
|||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"plist",
|
"plist",
|
||||||
"raw-window-handle",
|
"raw-window-handle",
|
||||||
"reqwest",
|
"reqwest 0.13.4",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_repr",
|
"serde_repr",
|
||||||
@@ -3146,6 +3516,19 @@ dependencies = [
|
|||||||
"toml 1.1.2+spec-1.1.0",
|
"toml 1.1.2+spec-1.1.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tempfile"
|
||||||
|
version = "3.27.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
|
||||||
|
dependencies = [
|
||||||
|
"fastrand",
|
||||||
|
"getrandom 0.4.3",
|
||||||
|
"once_cell",
|
||||||
|
"rustix",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tendril"
|
name = "tendril"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
@@ -3268,11 +3651,45 @@ dependencies = [
|
|||||||
"bytes",
|
"bytes",
|
||||||
"libc",
|
"libc",
|
||||||
"mio",
|
"mio",
|
||||||
|
"parking_lot",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
|
"signal-hook-registry",
|
||||||
"socket2",
|
"socket2",
|
||||||
|
"tokio-macros",
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-macros"
|
||||||
|
version = "2.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.118",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-native-tls"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
|
||||||
|
dependencies = [
|
||||||
|
"native-tls",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-rustls"
|
||||||
|
version = "0.26.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61"
|
||||||
|
dependencies = [
|
||||||
|
"rustls",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-util"
|
name = "tokio-util"
|
||||||
version = "0.7.18"
|
version = "0.7.18"
|
||||||
@@ -3537,10 +3954,13 @@ checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
|
|||||||
name = "u-ppt"
|
name = "u-ppt"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"futures",
|
||||||
|
"reqwest 0.12.28",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tauri",
|
"tauri",
|
||||||
"tauri-build",
|
"tauri-build",
|
||||||
|
"tokio",
|
||||||
"ts-rs",
|
"ts-rs",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -3597,6 +4017,12 @@ version = "1.13.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8"
|
checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "untrusted"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "url"
|
name = "url"
|
||||||
version = "2.5.8"
|
version = "2.5.8"
|
||||||
@@ -3640,6 +4066,12 @@ dependencies = [
|
|||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vcpkg"
|
||||||
|
version = "0.2.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version-compare"
|
name = "version-compare"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
@@ -3761,6 +4193,19 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-streams"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65"
|
||||||
|
dependencies = [
|
||||||
|
"futures-util",
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"wasm-bindgen-futures",
|
||||||
|
"web-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-streams"
|
name = "wasm-streams"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
@@ -4025,6 +4470,17 @@ dependencies = [
|
|||||||
"windows-link 0.1.3",
|
"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]]
|
[[package]]
|
||||||
name = "windows-result"
|
name = "windows-result"
|
||||||
version = "0.3.4"
|
version = "0.3.4"
|
||||||
@@ -4070,6 +4526,15 @@ dependencies = [
|
|||||||
"windows-targets 0.42.2",
|
"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]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.59.0"
|
version = "0.59.0"
|
||||||
@@ -4382,6 +4847,12 @@ dependencies = [
|
|||||||
"synstructure",
|
"synstructure",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zeroize"
|
||||||
|
version = "1.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerotrie"
|
name = "zerotrie"
|
||||||
version = "0.2.4"
|
version = "0.2.4"
|
||||||
|
|||||||
@@ -18,7 +18,10 @@ tauri = { version = "2", features = [] }
|
|||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
ts-rs = { version = "10", features = ["no-serde-warnings"] }
|
ts-rs = { version = "10", features = ["no-serde-warnings"] }
|
||||||
|
reqwest = { version = "0.12", features = ["json", "stream"] }
|
||||||
|
tokio = { version = "1", features = ["full"] }
|
||||||
|
futures = "0.3"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
# 默认不导出类型(避免 CI 构建 fail)
|
# ts-rs 类型导出
|
||||||
default = []
|
export-types = []
|
||||||
|
|||||||
310
src-tauri/src/ai.rs
Normal file
310
src-tauri/src/ai.rs
Normal file
@@ -0,0 +1,310 @@
|
|||||||
|
/* =====================================================================
|
||||||
|
* ai.rs — AI 内容创作引擎(Rust 版,对应 src/core/ai.ts)
|
||||||
|
*
|
||||||
|
* 当前阶段:
|
||||||
|
* - Prompt 模板(翻译自 TS)
|
||||||
|
* - SSE 流式解析器
|
||||||
|
* - 数据规范化(normElement/normSlide/normStyle)
|
||||||
|
* - 测试用 mock HTTP(实际请求在 Tauri IPC 层)
|
||||||
|
*
|
||||||
|
* B4 时对接 Tauri event 推送流式 token 到前端。
|
||||||
|
* ===================================================================== */
|
||||||
|
use crate::model::*;
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* Prompt 模板(与 TS 版完全一致)
|
||||||
|
* ============================================================ */
|
||||||
|
|
||||||
|
pub const SYS_BASE: &str =
|
||||||
|
"你是「u-ppt」的内容创作助手,擅长把主题变成结构清晰、视觉现代、带入场动效的中文演示稿。
|
||||||
|
输出必须严格遵循下面的数据模型,坐标用百分比(0-100),字号为数字。
|
||||||
|
|
||||||
|
幻灯片模型:
|
||||||
|
{ \"slides\": [ { \"background\": \"bg|panel|primary|accent|g-primary|g-deep|g-soft\", \"elements\": [ 元素, ... ] } ] }
|
||||||
|
背景:bg/panel=浅底;primary/accent=纯色深底;g-primary=主→强调渐变(深);g-deep=深色渐变;g-soft=浅色渐变。
|
||||||
|
|
||||||
|
元素类型 type:title 标题 | text 正文 | list 列表 | stat 数据 | quote 金句 | image 图片 | shape 形状 | chart 图表 | card 卡片 | table 表格 | code 代码 | formula 公式
|
||||||
|
元素:{ \"type\":..., \"x\":数字,\"y\":数字,\"w\":数字,\"h\":数字 (0-100), \"content\":字符串, \"style\":{...} }
|
||||||
|
- title/text/list/quote:content 为文字,list 用 \\n 分多行
|
||||||
|
- stat:content 为大数字(如 \"65%\"),style.label 为说明
|
||||||
|
- card:content 第一行=标题、其余行=正文;style.accent=顶部色条键,style.icon=emoji 图标
|
||||||
|
- shape:style.shapeType=rect|circle|triangle,style.fill=颜色键,style.gradient=true 渐变,style.opacity=0~1
|
||||||
|
- chart:content 为 JSON,两种格式:
|
||||||
|
单系列:[{\"label\":\"\",\"value\":数字}, ...]
|
||||||
|
多系列:{\"series\":[\"Q1\",\"Q2\"], \"items\":[{\"label\":\"华东\",\"values\":[120,150]}, ...]}
|
||||||
|
style.max=最大值,style.chartType=bar|line|area|pie|doughnut|radar|hbar|progress(默认 bar)
|
||||||
|
style.legend=true 显示图例,style.grid=true 显示网格线,style.stack=true 堆叠(多系列柱状)
|
||||||
|
图表类型选择建议:柱状(bar)对比离散类型;折线(line)显示趋势;面积(area)累计趋势;
|
||||||
|
饼图(pie)占比构成;环形(doughnut)轻量占比;雷达(radar)多维度对比;条形(hbar)长标签对比;进度(progress)完成率。
|
||||||
|
- table:content 为 Markdown 管道表格字符串,style.header=true 首行加粗
|
||||||
|
- code:content 为代码文本,style.lang=语言如 js/python
|
||||||
|
- formula:content 为 LaTeX 公式字符串,如 \"E = mc^2\"
|
||||||
|
- image:content 留空
|
||||||
|
|
||||||
|
style 公共字段:fontSize 数字、color 颜色键、align left|center|right、bold 布尔、italic 布尔、label/labelColor/labelSize、radius
|
||||||
|
颜色 color/fill/accent:一律用主题键 primary|accent|text|muted(系统按背景深浅自动反相,深底文字自动变白,不要手填 #ffffff)。
|
||||||
|
|
||||||
|
入场动画 style.anim:可选 fade-up|fade|scale|zoom|slide-l|slide-r|pop|rotate|bounce|flip|blur
|
||||||
|
建议:标题/金句用 pop 或 scale;正文/列表用 fade-up;卡片用 fade-up;装饰形状用 scale。
|
||||||
|
|
||||||
|
版式规范:
|
||||||
|
- 画布逻辑 1280×720。封面标题 y≈30 h≈18;内容标题 y≈8-10。
|
||||||
|
- 字号:title 44-66、text 22-28、list 24-30、stat 数字 64-80、quote 40-52。
|
||||||
|
- 一页一个观点,留白充足,列表不超过 5 条。
|
||||||
|
- 现代版式:多用 card 分组;封面/金句/结尾用 g-primary;目录用 3-4 张卡片网格;数据页 stat+chart。
|
||||||
|
- emoji 极克制:默认不给 card.icon。
|
||||||
|
|
||||||
|
内容准则(重要——避免「AI 味」):
|
||||||
|
- 标题写具体事实而非口号。
|
||||||
|
- 正文要有实质:具体数字、案例、步骤、来源;少用「赋能/助力/打造/引领/开启/一站式」这类空词。
|
||||||
|
- 禁用万能金句:「少即是多」「一图胜千言」「未来已来」「让 X 更简单」。
|
||||||
|
- 列表每条是可执行/可验证的要点。
|
||||||
|
- 有判断、有观点,像从业者写的。
|
||||||
|
- 不要使用 markdown 代码块,直接输出 JSON。
|
||||||
|
|
||||||
|
可选 segments 字段(结构化富文本):
|
||||||
|
segments: [[ {\"text\":\"华东 \"}, {\"text\":\"增长 23%\",\"bold\":true,\"color\":\"accent\"} ], ...]
|
||||||
|
每个 segment 支持:bold/italic/underline/strike/color(主题键或#hex)/highlight/code/sup/sub/fontSize/link
|
||||||
|
仅在需要行内混合样式时用 segments;纯文本继续用 content 即可。segments 与 content 二选一。";
|
||||||
|
|
||||||
|
pub const SYS_GENERATE: &str = "任务:根据用户主题生成一套完整演示(封面、目录、3-5 页内容、结尾,共 6-8 页)。
|
||||||
|
要求:每个主要元素都加 style.anim;封面与结尾用 g-primary;至少 2 张 card;数据页用 stat+chart。
|
||||||
|
严格输出:{\"action\":\"create_all\",\"slides\":[...]}";
|
||||||
|
|
||||||
|
pub const SYS_POLISH: &str = "任务:按指令优化给定的\"单页\",保持主题一致、布局合理,并为各元素补充 style.anim。
|
||||||
|
严格输出:{\"action\":\"update_page\",\"slides\":[ 优化后的单页 ],\"note\":\"一句话说明改动\"}";
|
||||||
|
|
||||||
|
pub const SEP: &str = "%%PPT_JSON%%";
|
||||||
|
|
||||||
|
pub const SYS_CHAT: &str = "任务:与用户对话式编辑当前演示。先用中文流式回复用户(解释你将做什么),
|
||||||
|
如需修改 PPT,在回复最后另起一行输出分隔标记 %%PPT_JSON%%,
|
||||||
|
紧随其后输出 JSON 操作。
|
||||||
|
JSON 操作格式:{\"action\":\"add_page|update_page|create_all|answer\",\"slides\":[...],\"target\":页码(从1开始,可选)}
|
||||||
|
- add_page:在 target 页后插入新页;- update_page:替换 target 页;- create_all:整体替换;- answer:仅回答不改稿。
|
||||||
|
没有改动时不要输出分隔标记,仅回复即可。";
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* 数据规范化(对应 ai.ts 的 normStyle/normElement/normSlide)
|
||||||
|
* ============================================================ */
|
||||||
|
|
||||||
|
const VALID_COLORS: &[&str] = &["primary", "accent", "text", "muted"];
|
||||||
|
const VALID_CHART_TYPES: &[&str] = &["bar", "line", "area", "pie", "doughnut", "radar", "hbar", "progress"];
|
||||||
|
const VALID_BGS: &[&str] = &["bg", "panel", "primary", "accent", "g-primary", "g-deep", "g-soft"];
|
||||||
|
|
||||||
|
fn valid_color(v: &str) -> Option<String> {
|
||||||
|
if v.is_empty() { return None; }
|
||||||
|
if v.starts_with('#') {
|
||||||
|
return if (v.len() == 4 || v.len() == 7 || v.len() == 9) && v[1..].chars().all(|c| c.is_ascii_hexdigit()) {
|
||||||
|
Some(v.to_string())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if VALID_COLORS.contains(&v) { Some(v.to_string()) } else { None }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn norm_style(input: &serde_json::Value) -> ElementStyle {
|
||||||
|
let mut st = ElementStyle::default();
|
||||||
|
|
||||||
|
if let Some(obj) = input.as_object() {
|
||||||
|
if let Some(v) = obj.get("fontSize").and_then(|v| v.as_f64()) { st.font_size = Some(v); }
|
||||||
|
if let Some(v) = obj.get("font_size").and_then(|v| v.as_f64()) { st.font_size = Some(v); }
|
||||||
|
if let Some(b) = obj.get("bold").and_then(|v| v.as_bool()) { st.bold = Some(b); }
|
||||||
|
if let Some(b) = obj.get("italic").and_then(|v| v.as_bool()) { st.italic = Some(b); }
|
||||||
|
if let Some(v) = obj.get("labelSize").and_then(|v| v.as_f64()) { st.label_size = Some(v); }
|
||||||
|
if let Some(v) = obj.get("radius").and_then(|v| v.as_f64()) { st.radius = Some(v); }
|
||||||
|
if let Some(v) = obj.get("max").and_then(|v| v.as_f64()) { st.max = Some(v); }
|
||||||
|
if let Some(v) = obj.get("align").and_then(|v| v.as_str()) { st.align = Some(v.to_string()); }
|
||||||
|
if let Some(v) = obj.get("anim").and_then(|v| v.as_str()) { st.anim = Some(v.to_string()); }
|
||||||
|
if let Some(v) = obj.get("fit").and_then(|v| v.as_str()) { st.fit = Some(v.to_string()); }
|
||||||
|
if let Some(b) = obj.get("gradient").and_then(|v| v.as_bool()) { st.gradient = Some(b); }
|
||||||
|
if let Some(v) = obj.get("opacity").and_then(|v| v.as_f64()) { st.opacity = Some(v); }
|
||||||
|
if let Some(v) = obj.get("shapeType").and_then(|v| v.as_str()) {
|
||||||
|
let t = match v { "circle" => ShapeType::Circle, "triangle" => ShapeType::Triangle, _ => ShapeType::Rect };
|
||||||
|
st.shape_type = Some(t);
|
||||||
|
}
|
||||||
|
if let Some(v) = obj.get("chartType").and_then(|v| v.as_str()) {
|
||||||
|
if VALID_CHART_TYPES.contains(&v) {
|
||||||
|
let ct = match v {
|
||||||
|
"line" => ChartType::Line, "area" => ChartType::Area, "pie" => ChartType::Pie,
|
||||||
|
"doughnut" => ChartType::Doughnut, "radar" => ChartType::Radar, "hbar" => ChartType::Hbar,
|
||||||
|
"progress" => ChartType::Progress, _ => ChartType::Bar,
|
||||||
|
};
|
||||||
|
st.chart_type = Some(ct);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(b) = obj.get("legend").and_then(|v| v.as_bool()) { st.legend = Some(b); }
|
||||||
|
if let Some(b) = obj.get("grid").and_then(|v| v.as_bool()) { st.grid = Some(b); }
|
||||||
|
if let Some(b) = obj.get("stack").and_then(|v| v.as_bool()) { st.stack = Some(b); }
|
||||||
|
if let Some(b) = obj.get("header").and_then(|v| v.as_bool()) { st.header = Some(b); }
|
||||||
|
if let Some(b) = obj.get("inline").and_then(|v| v.as_bool()) { st.inline_ = Some(b); }
|
||||||
|
if let Some(v) = obj.get("label").and_then(|v| v.as_str()) { st.label = Some(v.to_string()); }
|
||||||
|
if let Some(v) = obj.get("labelColor").and_then(|v| v.as_str()).or_else(|| obj.get("label_color").and_then(|v| v.as_str())) {
|
||||||
|
st.label_color = valid_color(v);
|
||||||
|
}
|
||||||
|
if let Some(v) = obj.get("color").and_then(|v| v.as_str()) { st.color = valid_color(v); }
|
||||||
|
if let Some(v) = obj.get("fill").and_then(|v| v.as_str()) { st.fill = valid_color(v); }
|
||||||
|
if let Some(v) = obj.get("accent").and_then(|v| v.as_str()) { st.accent = valid_color(v); }
|
||||||
|
if let Some(v) = obj.get("lang").and_then(|v| v.as_str()) { st.lang = Some(v.chars().take(16).collect()); }
|
||||||
|
if let Some(v) = obj.get("icon").and_then(|v| v.as_str()) { st.icon = Some(v.chars().take(8).collect()); }
|
||||||
|
}
|
||||||
|
|
||||||
|
st
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn norm_element(input: &serde_json::Value) -> Option<SlideElement> {
|
||||||
|
let obj = input.as_object()?;
|
||||||
|
let el_type = obj.get("type").and_then(|v| v.as_str()).unwrap_or("text");
|
||||||
|
let valid = el_type; // 暂不校验
|
||||||
|
|
||||||
|
Some(SlideElement {
|
||||||
|
id: obj.get("id").and_then(|v| v.as_str()).unwrap_or("").to_string(),
|
||||||
|
element_type: valid.to_string(),
|
||||||
|
x: obj.get("x").and_then(|v| v.as_f64()).map(|f| f.max(0.0).min(100.0)).unwrap_or(10.0),
|
||||||
|
y: obj.get("y").and_then(|v| v.as_f64()).map(|f| f.max(0.0).min(100.0)).unwrap_or(10.0),
|
||||||
|
w: obj.get("w").and_then(|v| v.as_f64()).map(|f| f.max(3.0).min(100.0)).unwrap_or(60.0),
|
||||||
|
h: obj.get("h").and_then(|v| v.as_f64()).map(|f| f.max(3.0).min(100.0)).unwrap_or(15.0),
|
||||||
|
content: obj.get("content").and_then(|v| v.as_str()).unwrap_or("").to_string(),
|
||||||
|
style: norm_style(obj.get("style").unwrap_or(&serde_json::Value::Null)),
|
||||||
|
segments: None, // TODO: parse segments
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn norm_slide(input: &serde_json::Value) -> Option<Slide> {
|
||||||
|
let obj = input.as_object()?;
|
||||||
|
let bg = obj.get("background").and_then(|v| v.as_str()).unwrap_or("bg");
|
||||||
|
let bg = if VALID_BGS.contains(&bg) || bg.starts_with('#') { bg.to_string() } else { "bg".to_string() };
|
||||||
|
|
||||||
|
let elements = obj.get("elements")
|
||||||
|
.and_then(|v| v.as_array())
|
||||||
|
.map(|arr| arr.iter().filter_map(norm_element).collect())
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
Some(Slide {
|
||||||
|
id: "".to_string(),
|
||||||
|
background: bg,
|
||||||
|
elements,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn norm_slides(input: &serde_json::Value) -> Vec<Slide> {
|
||||||
|
input.as_array()
|
||||||
|
.map(|arr| arr.iter().filter_map(norm_slide).collect())
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 从 AI 回复文本中提取 JSON 代码块
|
||||||
|
pub fn try_parse_json(s: &str) -> Option<serde_json::Value> {
|
||||||
|
// 去掉 ```json 和 ``` 标记
|
||||||
|
let cleaned = s.trim_start();
|
||||||
|
let cleaned = cleaned.trim_start_matches("```json").trim();
|
||||||
|
let cleaned = cleaned.trim_start_matches("```");
|
||||||
|
let cleaned = cleaned.trim_end_matches("```").trim();
|
||||||
|
|
||||||
|
// 找到第一个 { 和最后一个 }
|
||||||
|
let start = cleaned.find('{')?;
|
||||||
|
let end = cleaned.rfind('}')?;
|
||||||
|
let candidate = &cleaned[start..=end];
|
||||||
|
|
||||||
|
// 尝试 parse
|
||||||
|
serde_json::from_str(candidate).ok()
|
||||||
|
.or_else(|| {
|
||||||
|
// 容错:移除尾随逗号
|
||||||
|
let fixed = candidate.replace(",\n}", "}").replace(",\r\n}", "}").replace(", }", "}");
|
||||||
|
serde_json::from_str(&fixed).ok()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* SSE 流式解析器
|
||||||
|
* ============================================================ */
|
||||||
|
|
||||||
|
pub struct SSESection {
|
||||||
|
pub reply: String,
|
||||||
|
pub json: Option<serde_json::Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 解析 SSE 流,按 %%PPT_JSON%% 分隔自然语言回复与 JSON 操作
|
||||||
|
pub fn split_chat_reply(full_text: &str) -> SSESection {
|
||||||
|
let parts: Vec<&str> = full_text.splitn(2, SEP).collect();
|
||||||
|
let reply = parts[0].trim().to_string();
|
||||||
|
let json = parts.get(1).and_then(|s| try_parse_json(s));
|
||||||
|
SSESection { reply, json }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 解析 json_mode 响应(generate/polish 等)
|
||||||
|
pub fn parse_json_mode(full_text: &str) -> Option<serde_json::Value> {
|
||||||
|
try_parse_json(full_text)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use serde_json::json;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_norm_style() {
|
||||||
|
let input = json!({ "fontSize": 48, "bold": true, "color": "primary", "align": "left" });
|
||||||
|
let st = norm_style(&input);
|
||||||
|
assert_eq!(st.font_size, Some(48.0));
|
||||||
|
assert_eq!(st.bold, Some(true));
|
||||||
|
assert_eq!(st.color, Some("primary".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_norm_element() {
|
||||||
|
let input = json!({ "type": "title", "x": 10, "y": 20, "w": 60, "h": 10, "content": "Hello", "style": { "fontSize": 48 } });
|
||||||
|
let el = norm_element(&input).unwrap();
|
||||||
|
assert_eq!(el.element_type, "title");
|
||||||
|
assert_eq!(el.content, "Hello");
|
||||||
|
assert_eq!(el.style.font_size, Some(48.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_norm_slide() {
|
||||||
|
let input = json!({ "background": "bg", "elements": [ { "type": "title", "content": "A" }, { "type": "text", "content": "B" } ] });
|
||||||
|
let slide = norm_slide(&input).unwrap();
|
||||||
|
assert_eq!(slide.elements.len(), 2);
|
||||||
|
assert_eq!(slide.elements[0].element_type, "title");
|
||||||
|
assert_eq!(slide.elements[1].element_type, "text");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_try_parse_json() {
|
||||||
|
let input = "这是一段自然语言回复。\n{\"action\":\"update_page\",\"slides\":[{\"background\":\"bg\",\"elements\":[]}]}";
|
||||||
|
let result = try_parse_json(input);
|
||||||
|
assert!(result.is_some());
|
||||||
|
assert_eq!(result.unwrap()["action"], "update_page");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_try_parse_json_with_codeblock() {
|
||||||
|
let input = "```json\n{\"action\":\"create_all\",\"slides\":[]}\n```";
|
||||||
|
let result = try_parse_json(input);
|
||||||
|
assert!(result.is_some());
|
||||||
|
assert_eq!(result.unwrap()["action"], "create_all");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_split_chat_reply() {
|
||||||
|
let input = "先修改一下。\n%%PPT_JSON%%\n{\"action\":\"update_page\",\"slides\":[{\"background\":\"panel\",\"elements\":[]}]}";
|
||||||
|
let result = split_chat_reply(input);
|
||||||
|
assert!(!result.reply.is_empty());
|
||||||
|
assert!(result.json.is_some());
|
||||||
|
assert_eq!(result.json.unwrap()["action"], "update_page");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_try_parse_valid_color() {
|
||||||
|
assert!(valid_color("primary").is_some());
|
||||||
|
assert!(valid_color("#4f46e5").is_some());
|
||||||
|
assert!(valid_color("invalid").is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_chart_type_detection() {
|
||||||
|
let input = json!({ "chartType": "pie", "max": 100 });
|
||||||
|
let st = norm_style(&input);
|
||||||
|
assert!(matches!(st.chart_type, Some(ChartType::Pie)));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
* B3: AI 引擎迁移(待实现)
|
* B3: AI 引擎迁移(待实现)
|
||||||
* ===================================================================== */
|
* ===================================================================== */
|
||||||
|
|
||||||
|
mod ai;
|
||||||
mod color;
|
mod color;
|
||||||
mod model;
|
mod model;
|
||||||
mod op;
|
mod op;
|
||||||
|
|||||||
Reference in New Issue
Block a user