Function bodies 101 total
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 lconfig_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.hottest_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_trget_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() {
waSource: 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.clmain 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(¤t);
}
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 3test_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 indicapage 1 / 3next ›