yggdrasil/api/
writing_guide.rs1#![allow(clippy::unused_unit, deprecated)]
3
4use dioxus::prelude::*;
5
6pub const SKILL_SOURCE: &str = include_str!("../../public/skills/yggdrasil-writing/SKILL.md");
7pub const SKILL_URL: &str = "/skills/yggdrasil-writing/SKILL.md";
8
9#[derive(Clone, serde::Serialize, serde::Deserialize)]
10pub struct WritingGuideData {
11 pub html: String,
12 pub toc_html: String,
13 pub configs: [String; 3],
14 pub install_prompt: String,
15}
16
17#[cfg(feature = "server")]
19fn render_guide() -> crate::api::markdown::RenderedContent {
20 let body = SKILL_SOURCE
21 .strip_prefix("---\n")
22 .and_then(|text| text.split_once("\n---\n"))
23 .map(|(_, body)| body.trim_start())
24 .unwrap_or(SKILL_SOURCE);
25 let body = body.strip_prefix("# Yggdrasil 文章写作\n").unwrap_or(body);
27 crate::api::markdown::render_markdown_enhanced(body)
28}
29
30#[server]
31pub async fn get_writing_guide() -> Result<WritingGuideData, ServerFnError> {
32 static RENDERED: std::sync::LazyLock<crate::api::markdown::RenderedContent> =
33 std::sync::LazyLock::new(render_guide);
34 let base_url = crate::api::settings::runtime_security_settings()
35 .await
36 .app_base_url;
37 let base_url = if base_url.trim().is_empty() {
38 crate::mcp::config::base_url_from_env()
39 } else {
40 base_url
41 };
42 let configs = crate::mcp::config::generate_client_configs(&base_url, "YOUR_YGGDRASIL_TOKEN");
43 Ok(WritingGuideData {
44 html: RENDERED.html.clone(),
45 toc_html: RENDERED.toc_html.clone(),
46 configs: [configs.codex_toml, configs.omp_json, configs.opencode_json],
47 install_prompt: format!(
48 "请从 {}{SKILL_URL} 下载完整的 SKILL.md,将 yggdrasil-writing 安装为用户全局 skill,保存到 ~/.agents/skills/yggdrasil-writing/SKILL.md(不要安装到项目目录),并确认当前 Agent 能发现该技能。",
49 base_url.trim_end_matches('/')
50 ),
51 })
52}
53
54#[cfg(all(test, feature = "server"))]
55mod tests {
56 use super::*;
57
58 #[test]
59 fn guide_renders_documentation_without_activating_example_blocks() {
60 let rendered = render_guide();
61 assert!(!rendered.html.contains("description:"));
62 assert!(!rendered.html.contains("<h1"));
63 assert!(rendered.html.contains("<h2"));
64 assert!(rendered.html.contains("<table"));
65 assert!(rendered.html.contains("language-markdown"));
66 assert!(!rendered.html.contains("data-runnable=\"true\""));
67 assert!(!rendered.html.contains("class=\"language-mermaid\""));
68 assert!(rendered.toc_html.contains("可运行代码块"));
69 assert!(SKILL_SOURCE.starts_with("---\nname: yggdrasil-writing\n"));
70 }
71}