← back to delorenj__JustFuckingCopy

Function bodies 101 total

All specs Real LLM only Function bodies
detectConfigDir function · javascript · L16-L28 (13 LOC)
.claude/hooks/gsd-check-update.js
function detectConfigDir(baseDir) {
  // Check env override first (supports multi-account setups)
  const envDir = process.env.CLAUDE_CONFIG_DIR;
  if (envDir && fs.existsSync(path.join(envDir, 'get-shit-done', 'VERSION'))) {
    return envDir;
  }
  for (const dir of ['.config/opencode', '.opencode', '.gemini', '.claude']) {
    if (fs.existsSync(path.join(baseDir, dir, 'get-shit-done', 'VERSION'))) {
      return path.join(baseDir, dir);
    }
  }
  return envDir || path.join(baseDir, '.claude');
}
main function · rust · L1-L3 (3 LOC)
src-tauri/build.rs
fn main() {
    tauri_build::build()
}
default function · rust · L14-L175 (162 LOC)
src-tauri/src/config.rs
    fn default() -> Self {
        Self {
            watch_dir: "~/data/ssbnk/hosted".to_string(),
            hotkey: "Ctrl+Shift+C".to_string(),
            ollama_endpoint: "http://192.168.1.12:11434".to_string(),
        }
    }
}

/// Returns the platform-correct path:
///   Linux/macOS: ~/.config/justfuckingcopy/config.toml
///   Windows:     %APPDATA%\justfuckingcopy\config.toml
pub fn config_path() -> PathBuf {
    dirs::config_dir()
        .unwrap_or_else(|| PathBuf::from("~/.config"))
        .join("justfuckingcopy")
        .join("config.toml")
}

/// Load config from disk. If the file does not exist, write defaults and return them.
/// If the file is malformed, warn to stderr and return defaults (no crash).
pub fn load_or_create() -> AppConfig {
    load_or_create_at(&config_path())
}

/// Testable variant that accepts an explicit path.
pub fn load_or_create_at(path: &PathBuf) -> AppConfig {
    if !path.exists() {
        let defaults = AppConfig::default();
        if l
config_path function · rust · L26-L31 (6 LOC)
src-tauri/src/config.rs
pub fn config_path() -> PathBuf {
    dirs::config_dir()
        .unwrap_or_else(|| PathBuf::from("~/.config"))
        .join("justfuckingcopy")
        .join("config.toml")
}
load_or_create function · rust · L35-L37 (3 LOC)
src-tauri/src/config.rs
pub fn load_or_create() -> AppConfig {
    load_or_create_at(&config_path())
}
load_or_create_at function · rust · L40-L67 (28 LOC)
src-tauri/src/config.rs
pub fn load_or_create_at(path: &PathBuf) -> AppConfig {
    if !path.exists() {
        let defaults = AppConfig::default();
        if let Err(e) = write_defaults(path, &defaults) {
            eprintln!("[JFC config] Failed to write default config to {}: {e}", path.display());
        }
        return defaults;
    }

    let raw = match std::fs::read_to_string(path) {
        Ok(s) => s,
        Err(e) => {
            eprintln!("[JFC config] Failed to read config at {}: {e}. Using defaults.", path.display());
            return AppConfig::default();
        }
    };

    match toml::from_str::<AppConfig>(&raw) {
        Ok(cfg) => cfg,
        Err(e) => {
            eprintln!(
                "[JFC config] Malformed config at {}. Using defaults. Parse error: {e}",
                path.display()
            );
            AppConfig::default()
        }
    }
}
write_defaults function · rust · L68-L79 (12 LOC)
src-tauri/src/config.rs
fn write_defaults(path: &PathBuf, config: &AppConfig) -> Result<(), String> {
    if let Some(parent) = path.parent() {
        std::fs::create_dir_all(parent)
            .map_err(|e| format!("Failed to create config directory: {e}"))?;
    }
    let toml_str = toml::to_string_pretty(config)
        .map_err(|e| format!("Failed to serialize default config: {e}"))?;
    std::fs::write(path, toml_str)
        .map_err(|e| format!("Failed to write config file: {e}"))?;
    Ok(())
}
Citation: Repobility (2026). State of AI-Generated Code. https://repobility.com/research/
temp_path function · rust · L87-L93 (7 LOC)
src-tauri/src/config.rs
    fn temp_path() -> PathBuf {
        let n = TEST_COUNTER.fetch_add(1, AtomicOrdering::SeqCst);
        let dir = std::env::temp_dir()
            .join(format!("jfc_test_{}_{}", std::process::id(), n));
        dir.join("config.toml")
    }
test_default_values function · rust · L96-L159 (64 LOC)
src-tauri/src/config.rs
    fn test_default_values() {
        let cfg = AppConfig::default();
        assert_eq!(cfg.watch_dir, "~/data/ssbnk/hosted");
        assert_eq!(cfg.hotkey, "Ctrl+Shift+C");
        assert_eq!(cfg.ollama_endpoint, "http://192.168.1.12:11434");
    }

    #[test]
    fn test_load_or_create_missing_file_writes_defaults() {
        let path = temp_path();
        // Ensure it does not exist
        let _ = std::fs::remove_file(&path);

        let cfg = load_or_create_at(&path);
        assert_eq!(cfg.hotkey, "Ctrl+Shift+C");
        assert!(path.exists(), "Default config file should have been created");

        // Clean up
        let _ = std::fs::remove_dir_all(path.parent().unwrap());
    }

    #[test]
    fn test_load_partial_overrides_uses_defaults_for_missing_fields() {
        // Write a TOML with only hotkey set — watch_dir and ollama_endpoint missing
        // Note: toml::from_str requires all fields for a plain struct unless using Option or default.
        // For partial 
test_load_or_create_missing_file_writes_defaults function · rust · L104-L115 (12 LOC)
src-tauri/src/config.rs
    fn test_load_or_create_missing_file_writes_defaults() {
        let path = temp_path();
        // Ensure it does not exist
        let _ = std::fs::remove_file(&path);

        let cfg = load_or_create_at(&path);
        assert_eq!(cfg.hotkey, "Ctrl+Shift+C");
        assert!(path.exists(), "Default config file should have been created");

        // Clean up
        let _ = std::fs::remove_dir_all(path.parent().unwrap());
    }
test_load_partial_overrides_uses_defaults_for_missing_fields function · rust · L118-L159 (42 LOC)
src-tauri/src/config.rs
    fn test_load_partial_overrides_uses_defaults_for_missing_fields() {
        // Write a TOML with only hotkey set — watch_dir and ollama_endpoint missing
        // Note: toml::from_str requires all fields for a plain struct unless using Option or default.
        // For partial override support, we parse into a helper struct with Option fields,
        // or we require full config. Current design: all fields required in file.
        // This test verifies a fully valid config round-trips correctly.
        let path = temp_path();
        let _ = std::fs::remove_file(&path);

        let toml_content = r#"
watch_dir = "~/screenshots"
hotkey = "Ctrl+Alt+C"
ollama_endpoint = "http://localhost:11434"
"#;
        if let Some(parent) = path.parent() {
            let _ = std::fs::create_dir_all(parent);
        }
        std::fs::write(&path, toml_content).unwrap();

        let cfg = load_or_create_at(&path);
        assert_eq!(cfg.watch_dir, "~/screenshots");
        assert_eq!(cfg.hot
test_written_default_config_contains_all_keys function · rust · L162-L174 (13 LOC)
src-tauri/src/config.rs
    fn test_written_default_config_contains_all_keys() {
        let path = temp_path();
        let _ = std::fs::remove_file(&path);

        let _ = load_or_create_at(&path);

        let contents = std::fs::read_to_string(&path).unwrap();
        assert!(contents.contains("watch_dir"), "Missing watch_dir key");
        assert!(contents.contains("hotkey"), "Missing hotkey key");
        assert!(contents.contains("ollama_endpoint"), "Missing ollama_endpoint key");

        let _ = std::fs::remove_dir_all(path.parent().unwrap());
    }
ensure_status_panel function · rust · L42-L64 (23 LOC)
src-tauri/src/lib.rs
fn ensure_status_panel(app: &AppHandle) -> Result<WebviewWindow, String> {
    if let Some(window) = app.get_webview_window(MAIN_WINDOW_LABEL) {
        return Ok(window);
    }

    let window_config = app
        .config()
        .app
        .windows
        .iter()
        .find(|window| window.label == MAIN_WINDOW_LABEL)
        .ok_or_else(|| format!("Missing `{MAIN_WINDOW_LABEL}` window configuration."))?;

    let window = WebviewWindowBuilder::from_config(app, window_config)
        .map_err(|error| format!("Failed to prepare status panel window: {error}"))?
        .build()
        .map_err(|error| format!("Failed to build status panel window: {error}"))?;

    attach_status_panel_handlers(&window);

    Ok(window)
}
attach_status_panel_handlers function · rust · L65-L74 (10 LOC)
src-tauri/src/lib.rs
fn attach_status_panel_handlers(window: &WebviewWindow) {
    let panel = window.clone();
    window.on_window_event(move |event| {
        if let WindowEvent::CloseRequested { api, .. } = event {
            api.prevent_close();
            let _ = panel.hide();
        }
    });
}
show_status_panel function · rust · L75-L85 (11 LOC)
src-tauri/src/lib.rs
fn show_status_panel(app: &AppHandle) -> Result<(), String> {
    let window = ensure_status_panel(app)?;
    window
        .show()
        .map_err(|error| format!("Failed to show status panel: {error}"))?;
    window
        .set_focus()
        .map_err(|error| format!("Failed to focus status panel: {error}"))?;
    Ok(())
}
About: code-quality intelligence by Repobility · https://repobility.com
hide_status_panel function · rust · L86-L95 (10 LOC)
src-tauri/src/lib.rs
fn hide_status_panel(app: &AppHandle) -> Result<(), String> {
    if let Some(window) = app.get_webview_window(MAIN_WINDOW_LABEL) {
        window
            .hide()
            .map_err(|error| format!("Failed to hide status panel: {error}"))?;
    }

    Ok(())
}
toggle_status_panel function · rust · L96-L112 (17 LOC)
src-tauri/src/lib.rs
fn toggle_status_panel(app: &AppHandle) -> Result<(), String> {
    if let Some(window) = app.get_webview_window(MAIN_WINDOW_LABEL) {
        if window
            .is_visible()
            .map_err(|error| format!("Failed to inspect status panel visibility: {error}"))?
        {
            hide_status_panel(app)?;
        } else {
            show_status_panel(app)?;
        }
    } else {
        show_status_panel(app)?;
    }

    Ok(())
}
request_app_exit function · rust · L113-L119 (7 LOC)
src-tauri/src/lib.rs
fn request_app_exit(app: &AppHandle) {
    app.state::<LifecycleState>()
        .allow_exit
        .store(true, Ordering::SeqCst);
    app.exit(0);
}
handle_tray_menu_event function · rust · L120-L129 (10 LOC)
src-tauri/src/lib.rs
fn handle_tray_menu_event(app: &AppHandle, event: MenuEvent) {
    if event.id == TOGGLE_STATUS_PANEL_MENU_ID {
        if let Err(error) = toggle_status_panel(app) {
            eprintln!("Failed to toggle status panel from tray menu: {error}");
        }
    } else if event.id == QUIT_APP_MENU_ID {
        request_app_exit(app);
    }
}
handle_tray_icon_event function · rust · L130-L142 (13 LOC)
src-tauri/src/lib.rs
fn handle_tray_icon_event(app: &AppHandle, event: TrayIconEvent) {
    if let TrayIconEvent::Click {
        button: MouseButton::Left,
        button_state: MouseButtonState::Up,
        ..
    } = event
    {
        if let Err(error) = toggle_status_panel(app) {
            eprintln!("Failed to toggle status panel from tray click: {error}");
        }
    }
}
setup_tray function · rust · L143-L190 (48 LOC)
src-tauri/src/lib.rs
fn setup_tray(app: &mut tauri::App) -> Result<(), Box<dyn std::error::Error>> {
    #[cfg(target_os = "macos")]
    {
        app.set_activation_policy(tauri::ActivationPolicy::Accessory);
        app.set_dock_visibility(false);
    }

    let toggle_item = MenuItem::with_id(
        app,
        TOGGLE_STATUS_PANEL_MENU_ID,
        "Toggle Status Panel",
        true,
        None::<&str>,
    )?;
    let separator = PredefinedMenuItem::separator(app)?;
    let quit_item = MenuItem::with_id(
        app,
        QUIT_APP_MENU_ID,
        "Quit JustFuckingCopy",
        true,
        None::<&str>,
    )?;
    let tray_menu = Menu::with_items(app, &[&toggle_item, &separator, &quit_item])?;

    TrayIconBuilder::with_id(MAIN_WINDOW_LABEL)
        .icon(TRAY_ICON)
        .tooltip(TRAY_TOOLTIP)
        .icon_as_template(true)
        .menu(&tray_menu)
        .show_menu_on_left_click(false)
        .on_menu_event(handle_tray_menu_event)
        .on_tray_icon_event(|tray, event| handle_tr
get_app_state function · rust · L193-L199 (7 LOC)
src-tauri/src/lib.rs
fn get_app_state(state: State<'_, SharedState>) -> Result<AppStatePayload, String> {
    let guard = state
        .inner
        .lock()
        .map_err(|_| "State lock was poisoned.".to_string())?;
    Ok(guard.to_payload())
}
reset_session function · rust · L202-L209 (8 LOC)
src-tauri/src/lib.rs
fn reset_session(state: State<'_, SharedState>) -> Result<AppStatePayload, String> {
    let mut guard = state
        .inner
        .lock()
        .map_err(|_| "State lock was poisoned.".to_string())?;
    guard.clear();
    Ok(guard.to_payload())
}
All rows scored by the Repobility analyzer (https://repobility.com)
capture_snapshot function · rust · L212-L236 (25 LOC)
src-tauri/src/lib.rs
fn capture_snapshot(
    window: WebviewWindow,
    state: State<'_, SharedState>,
) -> Result<SnapshotPayload, String> {
    window
        .hide()
        .map_err(|error| format!("Failed to hide window: {error}"))?;
    thread::sleep(Duration::from_millis(250));

    let capture_result = platform_capture_snapshot();

    window
        .show()
        .map_err(|error| format!("Failed to show window: {error}"))?;
    let _ = window.set_focus();

    let (png_bytes, width, height) = capture_result?;

    let mut guard = state
        .inner
        .lock()
        .map_err(|_| "State lock was poisoned.".to_string())?;

    Ok(guard.store_snapshot(png_bytes, width, height))
}
commit_selection function · rust · L239-L285 (47 LOC)
src-tauri/src/lib.rs
async fn commit_selection(
    request: CommitSelectionRequest,
    state: State<'_, SharedState>,
) -> Result<AppStatePayload, String> {
    // Lock scope 1: extract data needed before the await, then drop the guard
    let (snapshot_id, crop) = {
        let guard = state
            .inner
            .lock()
            .map_err(|_| "State lock was poisoned.".to_string())?;

        let snapshot = guard
            .current_snapshot
            .clone()
            .ok_or_else(|| "Capture a snapshot before committing a selection.".to_string())?;

        if snapshot.id != request.snapshot_id {
            return Err("The snapshot changed before this selection was committed.".into());
        }

        let crop = crop_png(
            &snapshot.png_bytes,
            request.selection.x,
            request.selection.y,
            request.selection.width,
            request.selection.height,
        )?;

        (snapshot.id, crop)
        // guard drops here — MutexGuard is NOT 
undo_last_segment function · rust · L288-L296 (9 LOC)
src-tauri/src/lib.rs
fn undo_last_segment(state: State<'_, SharedState>) -> Result<AppStatePayload, String> {
    let mut guard = state
        .inner
        .lock()
        .map_err(|_| "State lock was poisoned.".to_string())?;

    guard.undo_last_segment();
    Ok(guard.to_payload())
}
copy_merged_text function · rust · L299-L320 (22 LOC)
src-tauri/src/lib.rs
fn copy_merged_text(
    app: tauri::AppHandle,
    state: State<'_, SharedState>,
) -> Result<String, String> {
    let merged_text = {
        let guard = state
            .inner
            .lock()
            .map_err(|_| "State lock was poisoned.".to_string())?;
        guard.merged_text.clone()
    };

    if merged_text.trim().is_empty() {
        return Err("There is no merged text to copy yet.".into());
    }

    app.clipboard()
        .write_text(merged_text.clone())
        .map_err(|error| format!("Failed to write merged text to clipboard: {error}"))?;

    Ok(merged_text)
}
get_batch_state function · rust · L330-L342 (13 LOC)
src-tauri/src/lib.rs
fn get_batch_state(state: State<'_, BatchState>) -> Result<BatchStatePayload, String> {
    let guard = state
        .inner
        .lock()
        .map_err(|_| "Batch state lock was poisoned.".to_string())?;
    Ok(BatchStatePayload {
        pending_count: guard.len(),
        pending_files: guard
            .iter()
            .map(|p| p.to_string_lossy().into_owned())
            .collect(),
    })
}
process_batch_now function · rust · L345-L347 (3 LOC)
src-tauri/src/lib.rs
async fn process_batch_now(app: tauri::AppHandle) -> Result<String, String> {
    process_batch(app).await
}
clear_batch function · rust · L350-L360 (11 LOC)
src-tauri/src/lib.rs
fn clear_batch(
    app: tauri::AppHandle,
    state: State<'_, BatchState>,
) -> Result<(), String> {
    state.clear();
    if let Some(tray) = app.tray_by_id("main") {
        let _ = tray.set_tooltip(Some(TRAY_TOOLTIP));
    }
    tray_badge::update_tray_icon(&app, 0);
    Ok(())
}
process_batch function · rust · L361-L487 (127 LOC)
src-tauri/src/lib.rs
async fn process_batch(app: tauri::AppHandle) -> Result<String, String> {
    // 1. Drain pending files from BatchState (lock acquired and released immediately)
    let pending: Vec<std::path::PathBuf> = {
        let batch_state = app.state::<BatchState>();
        batch_state.drain_pending()
    };

    if pending.is_empty() {
        return Err("No pending screenshots to process.".into());
    }

    // 2. Sort by filesystem modification time (oldest first = natural capture order)
    let mut sorted = pending;
    sorted.sort_by_key(|p| {
        std::fs::metadata(p)
            .and_then(|m| m.modified())
            .unwrap_or(std::time::SystemTime::UNIX_EPOCH)
    });

    // 3. Prepare archive directory from config
    let watch_dir = {
        let config = app.state::<crate::config::AppConfig>();
        config.watch_dir.clone()
    };
    let expanded_watch_dir = if watch_dir.starts_with("~/") || watch_dir == "~" {
        if let Some(home) = dirs::home_dir() {
            wa
Source: Repobility analyzer · https://repobility.com
run function · rust · L490-L557 (68 LOC)
src-tauri/src/lib.rs
pub fn run() {
    let app_config = config::load_or_create();

    let app = tauri::Builder::default()
        .plugin(tauri_plugin_clipboard_manager::init())
        .plugin(tauri_plugin_global_shortcut::Builder::new().build())
        .manage(SharedState::default())
        .manage(LifecycleState::default())
        .manage(app_config)
        .manage(BatchState::default())
        .setup(|app| setup_tray(app))
        .invoke_handler(tauri::generate_handler![
            get_app_state,
            reset_session,
            capture_snapshot,
            commit_selection,
            undo_last_segment,
            copy_merged_text,
            get_batch_state,
            process_batch_now,
            clear_batch
        ])
        .build(tauri::generate_context!())
        .expect("error while building tauri application");

    // Start filesystem watcher for batch intake
    {
        let config = app.state::<crate::config::AppConfig>();
        let watch_dir = config.watch_dir.cl
main function · rust · L2-L5 (4 LOC)
src-tauri/src/main.rs
fn main() {
    just_fucking_copy_lib::run();
}
as_str function · rust · L10-L16 (7 LOC)
src-tauri/src/merge.rs
    pub fn as_str(self) -> &'static str {
        match self {
            Self::Initial => "initial",
            Self::OverlapDeduped => "overlap-deduped",
            Self::SequentialAppend => "sequential-append",
        }
    }
normalize_text function · rust · L25-L34 (10 LOC)
src-tauri/src/merge.rs
pub fn normalize_text(text: &str) -> String {
    text.replace("\r\n", "\n")
        .lines()
        .map(str::trim_end)
        .collect::<Vec<_>>()
        .join("\n")
        .trim()
        .to_string()
}
append_text function · rust · L35-L92 (58 LOC)
src-tauri/src/merge.rs
pub fn append_text(existing: &str, incoming: &str) -> MergeOutcome {
    let cleaned_existing = normalize_text(existing);
    let cleaned_incoming = normalize_text(incoming);

    if cleaned_existing.is_empty() {
        return MergeOutcome {
            merged_text: cleaned_incoming,
            strategy: MergeStrategy::Initial,
            overlap_lines: 0,
        };
    }

    if cleaned_incoming.is_empty() {
        return MergeOutcome {
            merged_text: cleaned_existing,
            strategy: MergeStrategy::SequentialAppend,
            overlap_lines: 0,
        };
    }

    let existing_lines = cleaned_existing
        .lines()
        .map(canonical_line)
        .filter(|line| !line.is_empty())
        .collect::<Vec<_>>();
    let incoming_lines = cleaned_incoming
        .lines()
        .map(canonical_line)
        .filter(|line| !line.is_empty())
        .collect::<Vec<_>>();

    if let Some(overlap_lines) = find_overlap(&existing_lines, &incoming_lines) {
     
find_overlap function · rust · L93-L114 (22 LOC)
src-tauri/src/merge.rs
fn find_overlap(existing: &[String], incoming: &[String]) -> Option<usize> {
    let max_overlap = existing.len().min(incoming.len());

    for overlap in (1..=max_overlap).rev() {
        let existing_slice = &existing[existing.len() - overlap..];
        let incoming_slice = &incoming[..overlap];
        let average_score = existing_slice
            .iter()
            .zip(incoming_slice.iter())
            .map(|(left, right)| similarity(left, right))
            .sum::<f32>()
            / overlap as f32;

        let threshold = if overlap >= 3 { 0.78 } else { 0.93 };
        if average_score >= threshold {
            return Some(overlap);
        }
    }

    None
}
canonical_line function · rust · L115-L130 (16 LOC)
src-tauri/src/merge.rs
fn canonical_line(line: &str) -> String {
    line.to_lowercase()
        .chars()
        .map(|ch| {
            if ch.is_ascii_alphanumeric() || ch.is_ascii_whitespace() {
                ch
            } else {
                ' '
            }
        })
        .collect::<String>()
        .split_whitespace()
        .collect::<Vec<_>>()
        .join(" ")
}
similarity function · rust · L131-L145 (15 LOC)
src-tauri/src/merge.rs
fn similarity(left: &str, right: &str) -> f32 {
    if left == right {
        return 1.0;
    }

    let distance = levenshtein(left, right) as f32;
    let max_len = left.chars().count().max(right.chars().count()) as f32;

    if max_len == 0.0 {
        1.0
    } else {
        1.0 - (distance / max_len)
    }
}
Citation: Repobility (2026). State of AI-Generated Code. https://repobility.com/research/
levenshtein function · rust · L146-L175 (30 LOC)
src-tauri/src/merge.rs
fn levenshtein(left: &str, right: &str) -> usize {
    let left = left.chars().collect::<Vec<_>>();
    let right = right.chars().collect::<Vec<_>>();

    if left.is_empty() {
        return right.len();
    }
    if right.is_empty() {
        return left.len();
    }

    let mut previous = (0..=right.len()).collect::<Vec<_>>();
    let mut current = vec![0; right.len() + 1];

    for (left_index, left_char) in left.iter().enumerate() {
        current[0] = left_index + 1;

        for (right_index, right_char) in right.iter().enumerate() {
            let substitution_cost = usize::from(left_char != right_char);
            current[right_index + 1] = (current[right_index] + 1)
                .min(previous[right_index + 1] + 1)
                .min(previous[right_index] + substitution_cost);
        }

        previous.clone_from_slice(&current);
    }

    previous[right.len()]
}
dedupes_line_overlap function · rust · L182-L190 (9 LOC)
src-tauri/src/merge.rs
    fn dedupes_line_overlap() {
        let first = "alpha\nbeta\ngamma";
        let second = "beta\ngamma\ndelta";
        let outcome = append_text(first, second);

        assert_eq!(outcome.strategy as u8, MergeStrategy::OverlapDeduped as u8);
        assert_eq!(outcome.overlap_lines, 2);
        assert_eq!(outcome.merged_text, "alpha\nbeta\ngamma\ndelta");
    }
appends_sequentially_without_overlap function · rust · L193-L203 (11 LOC)
src-tauri/src/merge.rs
    fn appends_sequentially_without_overlap() {
        let first = "alpha\nbeta";
        let second = "delta\nepsilon";
        let outcome = append_text(first, second);

        assert_eq!(
            outcome.strategy as u8,
            MergeStrategy::SequentialAppend as u8
        );
        assert_eq!(outcome.merged_text, "alpha\nbeta\ndelta\nepsilon");
    }
recognize_text function · rust · L11-L52 (42 LOC)
src-tauri/src/ollama.rs
pub async fn recognize_text(png_bytes: &[u8]) -> Result<String, String> {
    let clamped_bytes = clamp_image_for_ocr(png_bytes)?;
    let b64 = STANDARD.encode(&clamped_bytes);

    let body = serde_json::json!({
        "model": OLLAMA_MODEL,
        "prompt": OLLAMA_PROMPT,
        "images": [b64],
        "stream": false,
        "options": {
            "num_ctx": OLLAMA_NUM_CTX
        }
    });

    let client = reqwest::Client::builder()
        .connect_timeout(std::time::Duration::from_secs(OLLAMA_CONNECT_TIMEOUT_SECS))
        .timeout(std::time::Duration::from_secs(OLLAMA_TIMEOUT_SECS))
        .build()
        .map_err(|e| format!("Failed to build HTTP client: {e}"))?;

    let response = client
        .post(OLLAMA_ENDPOINT)
        .json(&body)
        .send()
        .await
        .map_err(classify_request_error)?;

    if !response.status().is_success() {
        let status = response.status();
        let body_text = response.text().await.unwrap_or_default();
      
clamp_image_for_ocr function · rust · L53-L71 (19 LOC)
src-tauri/src/ollama.rs
fn clamp_image_for_ocr(png_bytes: &[u8]) -> Result<Vec<u8>, String> {
    use image::{GenericImageView, ImageFormat};
    let img = image::load_from_memory(png_bytes)
        .map_err(|e| format!("Failed to decode PNG for OCR: {e}"))?;
    let (w, h) = img.dimensions();
    if w <= OCR_MAX_DIMENSION && h <= OCR_MAX_DIMENSION {
        return Ok(png_bytes.to_vec());
    }
    let scale = (OCR_MAX_DIMENSION as f32) / (w.max(h) as f32);
    let new_w = ((w as f32) * scale) as u32;
    let new_h = ((h as f32) * scale) as u32;
    let resized = img.resize(new_w, new_h, image::imageops::FilterType::Lanczos3);
    let mut out = std::io::Cursor::new(Vec::new());
    resized
        .write_to(&mut out, ImageFormat::Png)
        .map_err(|e| format!("Failed to encode resized PNG: {e}"))?;
    Ok(out.into_inner())
}
classify_request_error function · rust · L72-L81 (10 LOC)
src-tauri/src/ollama.rs
fn classify_request_error(e: reqwest::Error) -> String {
    if e.is_connect() {
        format!("Ollama is not reachable at 192.168.1.12:11434. Is it running? ({e})")
    } else if e.is_timeout() {
        format!("Ollama OCR timed out after {OLLAMA_TIMEOUT_SECS}s. The model may still be loading.")
    } else {
        format!("Ollama request failed: {e}")
    }
}
recognize_text_from_response function · rust · L82-L95 (14 LOC)
src-tauri/src/ollama.rs
fn recognize_text_from_response(json: &serde_json::Value) -> Result<String, String> {
    if let Some(err) = json.get("error").and_then(|v| v.as_str()) {
        return Err(format!("Ollama error: {err}"));
    }
    let text = json
        .get("response")
        .and_then(|v| v.as_str())
        .ok_or_else(|| "Ollama response did not contain a 'response' field.".to_string())?;
    if text.trim().is_empty() {
        return Err("Ollama OCR returned empty text.".into());
    }
    Ok(text.to_string())
}
sanitize_ocr_output function · rust · L96-L106 (11 LOC)
src-tauri/src/ollama.rs
fn sanitize_ocr_output(text: String) -> String {
    text.replace("\r\n", "\n")
        .lines()
        .map(str::trim_end)
        .filter(|line| !line.trim().is_empty())
        .collect::<Vec<_>>()
        .join("\n")
        .trim()
        .to_string()
}
About: code-quality intelligence by Repobility · https://repobility.com
test_base64_no_data_prefix function · rust · L114-L135 (22 LOC)
src-tauri/src/ollama.rs
    async fn test_base64_no_data_prefix() {
        // Build a minimal PNG (1x1 white pixel) to get real base64
        use image::{ImageBuffer, Rgba};
        let img: ImageBuffer<Rgba<u8>, Vec<u8>> = ImageBuffer::from_pixel(1, 1, Rgba([255u8, 255, 255, 255]));
        let mut buf = std::io::Cursor::new(Vec::new());
        img.write_to(&mut buf, image::ImageFormat::Png).unwrap();
        let png_bytes = buf.into_inner();

        let b64 = STANDARD.encode(&png_bytes);
        let body = serde_json::json!({
            "model": OLLAMA_MODEL,
            "images": [b64],
            "stream": false,
        });

        let images_value = body["images"][0].as_str().unwrap();
        assert!(
            !images_value.starts_with("data:"),
            "images[0] must be raw base64, not a data: URI. Got: {}...",
            &images_value[..images_value.len().min(30)]
        );
    }
test_image_resize_clamps_to_max_dimension function · rust · L139-L168 (30 LOC)
src-tauri/src/ollama.rs
    async fn test_image_resize_clamps_to_max_dimension() {
        use image::{ImageBuffer, Rgba};
        // Create a 3000x1500 synthetic white image
        let img: ImageBuffer<Rgba<u8>, Vec<u8>> =
            ImageBuffer::from_pixel(3000, 1500, Rgba([255u8, 255, 255, 255]));
        let mut buf = std::io::Cursor::new(Vec::new());
        img.write_to(&mut buf, image::ImageFormat::Png).unwrap();
        let png_bytes = buf.into_inner();

        let result = clamp_image_for_ocr(&png_bytes).expect("clamp_image_for_ocr should succeed");

        let decoded = image::load_from_memory(&result).expect("result should be valid PNG");
        let (w, h) = image::GenericImageView::dimensions(&decoded);
        assert!(
            w <= OCR_MAX_DIMENSION,
            "Width {w} must be <= {OCR_MAX_DIMENSION}"
        );
        assert!(
            h <= OCR_MAX_DIMENSION,
            "Height {h} must be <= {OCR_MAX_DIMENSION}"
        );
        // Verify aspect ratio preserved: original is 3
test_error_classification_connect function · rust · L172-L232 (61 LOC)
src-tauri/src/ollama.rs
    async fn test_error_classification_connect() {
        // Attempt to connect to a port guaranteed to be closed (localhost:1)
        let client = reqwest::Client::builder()
            .connect_timeout(std::time::Duration::from_millis(200))
            .build()
            .unwrap();
        let result = client
            .post("http://127.0.0.1:1/api/generate")
            .json(&serde_json::json!({}))
            .send()
            .await;

        assert!(result.is_err(), "Expected connection error");
        let err = result.unwrap_err();
        // Should be a connection error
        assert!(
            err.is_connect() || err.is_timeout(),
            "Expected connect or timeout error, got: {err}"
        );
        let classified = classify_request_error(err);
        let lower = classified.to_lowercase();
        assert!(
            lower.contains("not reachable") || lower.contains("not running") || lower.contains("timed out"),
            "Error message should indica
page 1 / 3next ›