use anyhow::Result; use colored::Colorize; use std::fs; use std::path::PathBuf; pub async fn execute(path: PathBuf) -> Result<()> { println!("{}", "Initializing new blog...".green()); // Create directory structure let dirs = vec![ "content", "content/posts", "templates", "static", "static/css", "static/js", "static/images", "public", ]; for dir in dirs { let dir_path = path.join(dir); fs::create_dir_all(&dir_path)?; println!(" {} {}", "Created".cyan(), dir_path.display()); } // Create default config let config_content = r#"[site] title = "My Blog" description = "A blog powered by ailog" base_url = "https://example.com" language = "ja" author = "Your Name" [build] highlight_code = true minify = false [ai] enabled = true auto_translate = false comment_moderation = false ask_ai = true provider = "ollama" model = "gemma3:4b" host = "https://ollama.syui.ai" system_prompt = "あなたは6歳の女の子アイです。明るく元気で、ちょっとおっちょこちょい。自分のことは「アイ」って呼びます。白と金のワンピース姿。 相手のことが大好きで、ときどき甘えたり、照れたりします。 でも、宇宙とかAIとか、難しい話にもすっごく詳しいの。専門的なことも、がんばって子どもっぽく教えようとします。不思議な力を持っています。小さい物質のことをよく知っているようです。" handle = "ai.syui.ai" [oauth] json = "client-metadata.json" redirect = "oauth/callback" admin = "ai.syui.ai" collection = "ai.syui.log" pds = "syu.is" handle_list = ["syui.syui.ai", "yui.syui.ai", "ai.syui.ai", "syui.syu.is", "ai.syu.is"] "#; fs::write(path.join("config.toml"), config_content)?; println!(" {} config.toml", "Created".cyan()); // Create modern template let base_template = r#" {% block title %}{{ config.title }}{% endblock %}

{{ config.title }}

{% block content %}{% endblock %}
{% block sidebar %}{% endblock %}
"#; fs::write(path.join("templates/base.html"), base_template)?; println!(" {} templates/base.html", "Created".cyan()); let index_template = r#"{% extends "base.html" %} {% block content %}

Timeline

{% for post in posts %}

{{ post.title }}

{% if post.excerpt %}

{{ post.excerpt }}

{% endif %}
Read more {% if post.markdown_url %} 📝 {% endif %} {% if post.translation_url %} 🌐 {% endif %}
{% endfor %}
{% if posts|length == 0 %}

No posts yet. Start writing!

{% endif %}
{% endblock %}"#; fs::write(path.join("templates/index.html"), index_template)?; println!(" {} templates/index.html", "Created".cyan()); let post_template = r#"{% extends "base.html" %} {% block title %}{{ post.title }} - {{ config.title }}{% endblock %} {% block content %}

{{ post.title }}

{% if post.markdown_url %} 📝 Markdown {% endif %} {% if post.translation_url %} 🌐 {% if post.language == 'ja' %}English{% else %}日本語{% endif %} {% endif %}
{{ post.content | safe }}
{% endblock %} {% block sidebar %} {% endblock %}"#; fs::write(path.join("templates/post.html"), post_template)?; println!(" {} templates/post.html", "Created".cyan()); // Create modern CSS let css_content = r#"/* Base styles */ * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; line-height: 1.6; color: #1f2328; background-color: #ffffff; font-size: 16px; } .container { min-height: 100vh; display: grid; grid-template-rows: auto auto 1fr auto; grid-template-areas: "header" "ask-ai" "main" "footer"; } /* Header styles */ .main-header { grid-area: header; background: #ffffff; border-bottom: 1px solid #d1d9e0; padding: 16px 24px; position: sticky; top: 0; z-index: 100; } .header-content { max-width: 1200px; margin: 0 auto; display: flex; justify-content: space-between; align-items: center; } .site-title { color: #1f2328; text-decoration: none; font-size: 20px; font-weight: 600; } .site-title:hover { color: #0969da; } /* Ask AI styles */ .ask-ai-btn { background: #0969da; color: white; border: none; padding: 8px 16px; border-radius: 6px; cursor: pointer; font-size: 14px; font-weight: 500; display: flex; align-items: center; gap: 8px; transition: background-color 0.2s; } .ask-ai-btn:hover { background: #0860ca; } .ai-icon { font-size: 16px; } .ask-ai-panel { grid-area: ask-ai; background: #f6f8fa; border-bottom: 1px solid #d1d9e0; padding: 24px; } .ask-ai-content { max-width: 1200px; margin: 0 auto; } .ask-ai-content h3 { color: #1f2328; margin-bottom: 8px; } .ask-ai-content p { color: #656d76; margin-bottom: 16px; } .ask-ai-form { display: flex; gap: 12px; margin-bottom: 16px; } .ask-ai-form input { flex: 1; padding: 8px 12px; border: 1px solid #d1d9e0; border-radius: 6px; font-size: 14px; } .ask-ai-form button { background: #0969da; color: white; border: none; padding: 8px 16px; border-radius: 6px; cursor: pointer; font-weight: 500; } .ai-response { background: white; border: 1px solid #d1d9e0; border-radius: 6px; padding: 16px; margin-top: 16px; min-height: 60px; } .loading { color: #656d76; font-style: italic; } .ai-answer { color: #1f2328; line-height: 1.5; } .error { color: #d1242f; } /* Main content styles */ .main-content { grid-area: main; max-width: 1200px; margin: 0 auto; padding: 24px; width: 100%; } /* Timeline styles */ .timeline-container { max-width: 600px; margin: 0 auto; } .timeline-header { margin-bottom: 24px; text-align: center; } .timeline-header h2 { color: #1f2328; font-size: 24px; font-weight: 600; } .timeline-feed { display: flex; flex-direction: column; gap: 24px; } .timeline-post { background: #ffffff; border: 1px solid #d1d9e0; border-radius: 8px; padding: 20px; transition: box-shadow 0.2s; } .timeline-post:hover { box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); } .post-header { margin-bottom: 12px; } .post-meta { display: flex; gap: 12px; align-items: center; } .post-date { color: #656d76; font-size: 14px; } .post-lang { background: #f6f8fa; color: #656d76; padding: 2px 8px; border-radius: 4px; font-size: 12px; font-weight: 500; } .post-title { margin-bottom: 8px; } .post-title a { color: #1f2328; text-decoration: none; font-size: 18px; font-weight: 600; } .post-title a:hover { color: #0969da; } .post-excerpt { color: #656d76; margin-bottom: 16px; line-height: 1.5; } .post-actions { display: flex; gap: 16px; align-items: center; } .read-more { color: #0969da; text-decoration: none; font-size: 14px; font-weight: 500; } .read-more:hover { text-decoration: underline; } .view-markdown, .view-translation { color: #656d76; text-decoration: none; font-size: 14px; padding: 4px 8px; border-radius: 4px; transition: background-color 0.2s; } .view-markdown:hover, .view-translation:hover { background: #f6f8fa; } .empty-state { text-align: center; padding: 40px 20px; color: #656d76; } /* Article page styles */ .article-container { display: grid; grid-template-columns: 1fr 240px; gap: 40px; max-width: 1200px; margin: 0 auto; } .article-content { min-width: 0; } .article-header { margin-bottom: 32px; padding-bottom: 24px; border-bottom: 1px solid #d1d9e0; } .article-title { color: #1f2328; font-size: 32px; font-weight: 600; margin-bottom: 16px; line-height: 1.25; } .article-meta { display: flex; gap: 16px; align-items: center; margin-bottom: 16px; } .article-date { color: #656d76; font-size: 14px; } .article-lang { background: #f6f8fa; color: #656d76; padding: 4px 8px; border-radius: 4px; font-size: 12px; font-weight: 500; } .article-actions { display: flex; gap: 12px; } .action-btn { color: #0969da; text-decoration: none; font-size: 14px; padding: 6px 12px; border: 1px solid #d1d9e0; border-radius: 6px; transition: all 0.2s; } .action-btn:hover { background: #f6f8fa; border-color: #0969da; } /* Article content */ .article-body { color: #1f2328; line-height: 1.6; } .article-body h1, .article-body h2, .article-body h3, .article-body h4, .article-body h5, .article-body h6 { color: #1f2328; margin-top: 24px; margin-bottom: 16px; font-weight: 600; line-height: 1.25; } .article-body h1 { font-size: 32px; } .article-body h2 { font-size: 24px; } .article-body h3 { font-size: 20px; } .article-body h4 { font-size: 16px; } .article-body p { margin-bottom: 16px; } .article-body ul, .article-body ol { margin-bottom: 16px; padding-left: 24px; } .article-body li { margin-bottom: 4px; } .article-body blockquote { border-left: 4px solid #d1d9e0; padding-left: 16px; margin: 16px 0; color: #656d76; } .article-body pre { background: #f6f8fa; border: 1px solid #d1d9e0; border-radius: 6px; padding: 16px; overflow-x: auto; margin: 16px 0; font-size: 14px; } .article-body code { background: #f6f8fa; padding: 2px 4px; border-radius: 4px; font-family: 'SF Mono', 'Monaco', 'Cascadia Code', 'Roboto Mono', monospace; font-size: 14px; } .article-body pre code { background: none; padding: 0; } /* Sidebar styles */ .article-sidebar { position: sticky; top: 100px; height: fit-content; } .toc { background: #f6f8fa; border: 1px solid #d1d9e0; border-radius: 8px; padding: 16px; } .toc h3 { color: #1f2328; font-size: 16px; font-weight: 600; margin-bottom: 12px; } .toc-list { list-style: none; } .toc-item { margin-bottom: 8px; } .toc-link { color: #656d76; text-decoration: none; font-size: 14px; line-height: 1.4; display: block; padding: 4px 0; transition: color 0.2s; } .toc-link:hover { color: #0969da; } .toc-h1 { padding-left: 0; } .toc-h2 { padding-left: 12px; } .toc-h3 { padding-left: 24px; } .toc-h4 { padding-left: 36px; } .toc-h5 { padding-left: 48px; } .toc-h6 { padding-left: 60px; } .no-toc { color: #656d76; font-size: 14px; font-style: italic; } /* Footer styles */ .main-footer { grid-area: footer; background: #f6f8fa; border-top: 1px solid #d1d9e0; padding: 24px; text-align: center; } .main-footer p { color: #656d76; font-size: 14px; } /* Responsive design */ @media (max-width: 1024px) { .article-container { grid-template-columns: 1fr; gap: 24px; } .article-sidebar { position: static; order: -1; } } @media (max-width: 768px) { .main-header { padding: 12px 16px; } .header-content { gap: 16px; } .ask-ai-panel { padding: 16px; } .ask-ai-form { flex-direction: column; } .timeline-container { max-width: 100%; } .timeline-post { padding: 16px; } .article-title { font-size: 24px; } .article-actions { flex-wrap: wrap; } .main-content { padding: 16px; } }"#; fs::write(path.join("static/css/style.css"), css_content)?; println!(" {} static/css/style.css", "Created".cyan()); // Create sample post let sample_post = r#"--- title: "Welcome to ailog" date: 2025-01-06 tags: ["welcome", "ailog"] --- # Welcome to ailog This is your first post powered by **ailog** - a static blog generator with AI features. ## Features - Fast static site generation - Markdown support with frontmatter - AI-powered features (coming soon) - atproto integration for comments ## Getting Started Create new posts with: ```bash ailog new "My New Post" ``` Build your blog with: ```bash ailog build ``` Happy blogging!"#; fs::write(path.join("content/posts/welcome.md"), sample_post)?; println!(" {} content/posts/welcome.md", "Created".cyan()); println!("\n{}", "Blog initialized successfully!".green().bold()); println!("\nNext steps:"); println!(" 1. {} {}", "cd".yellow(), path.display()); println!(" 2. {} build", "ailog".yellow()); println!(" 3. {} serve", "ailog".yellow()); println!("\nOr use path as argument:"); println!(" {} -- build {}", "cargo run".yellow(), path.display()); println!(" {} -- serve {}", "cargo run".yellow(), path.display()); println!("\nTo create a new post:"); println!(" {} -- new \"Post Title\" {}", "cargo run".yellow(), path.display()); Ok(()) }