refactor: module infrastructure avec comrak_renderer, file_repository, notify_watcher

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-19 14:06:43 +02:00
parent ec239bb42e
commit 7d2f56a364
10 changed files with 164 additions and 106 deletions
@@ -0,0 +1,36 @@
use comrak::plugins::syntect::SyntectAdapterBuilder;
use comrak::{markdown_to_html_with_plugins, Options, Plugins};
use crate::domain::MarkdownRenderer;
pub struct ComrakRenderer {
pub theme: String,
}
pub struct ComrakPreviewRenderer {
pub theme: String,
}
impl MarkdownRenderer for ComrakRenderer {
fn render(&self, content: &str) -> String {
let adapter = SyntectAdapterBuilder::new().theme(&self.theme).build();
let mut options = Options::default();
options.extension.table = true;
options.extension.strikethrough = true;
options.extension.autolink = true;
options.extension.tasklist = true;
let mut plugins = Plugins::default();
plugins.render.codefence_syntax_highlighter = Some(&adapter);
markdown_to_html_with_plugins(content, &options, &plugins)
}
}
impl MarkdownRenderer for ComrakPreviewRenderer {
fn render(&self, content: &str) -> String {
let adapter = SyntectAdapterBuilder::new().theme(&self.theme).build();
let options = Options::default();
let mut plugins = Plugins::default();
plugins.render.codefence_syntax_highlighter = Some(&adapter);
markdown_to_html_with_plugins(content, &options, &plugins)
}
}
@@ -0,0 +1,25 @@
use std::fs;
use std::path::Path;
pub fn collect_md_files(dir: &Path, result: &mut Vec<String>) -> std::io::Result<()> {
for entry in fs::read_dir(dir)? {
let entry = entry?;
let name = entry.file_name();
if name.to_string_lossy().starts_with('.') {
continue;
}
let path = entry.path();
if path.is_dir() {
collect_md_files(&path, result)?;
} else if path.extension().is_some_and(|e| e == "md") {
if let Some(s) = path.to_str() {
result.push(s.to_string());
}
}
}
Ok(())
}
pub fn read_file(path: &str) -> Result<String, String> {
fs::read_to_string(path).map_err(|e| e.to_string())
}
+3
View File
@@ -0,0 +1,3 @@
pub mod comrak_renderer;
pub mod file_repository;
pub mod notify_watcher;
@@ -0,0 +1,66 @@
use notify::EventKind;
use serde::Serialize;
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::sync::mpsc::RecvTimeoutError;
use std::time::{Duration, Instant};
use tauri::{AppHandle, Emitter};
#[derive(Serialize, Clone)]
struct FileChangedPayload {
path: String,
}
pub fn run_debounce_loop(
rx: std::sync::mpsc::Receiver<notify::Result<notify::Event>>,
app: AppHandle,
) {
let mut pending: HashMap<PathBuf, Instant> = HashMap::new();
loop {
match rx.recv_timeout(Duration::from_millis(10)) {
Ok(Ok(event)) => {
if matches!(
event.kind,
EventKind::Modify(_) | EventKind::Create(_) | EventKind::Remove(_)
) {
for p in event.paths {
if is_relevant_path(&p) {
pending.insert(p, Instant::now());
}
}
}
}
Ok(Err(_)) => {}
Err(RecvTimeoutError::Timeout) => {}
Err(RecvTimeoutError::Disconnected) => break,
}
let now = Instant::now();
let ready: Vec<PathBuf> = pending
.iter()
.filter(|(_, t)| now.duration_since(**t) >= Duration::from_millis(80))
.map(|(p, _)| p.clone())
.collect();
for p in ready {
pending.remove(&p);
let _ = app.emit(
"file-changed",
FileChangedPayload {
path: p.to_string_lossy().to_string(),
},
);
}
}
}
fn is_relevant_path(path: &Path) -> bool {
if path
.components()
.any(|c| c.as_os_str().to_string_lossy().starts_with('.'))
{
return false;
}
path.extension().is_some_and(|e| e == "md")
}