diff --git a/src/analysis/mod.rs b/src/analysis/mod.rs index 537b5fb..7996fd2 100644 --- a/src/analysis/mod.rs +++ b/src/analysis/mod.rs @@ -11,7 +11,7 @@ pub use entities::EntityExtractor; use serde::{Deserialize, Serialize}; /// Available activity categories (as per MVP spec) -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum ActivityCategory { Development, Meeting, diff --git a/src/capture/mod.rs b/src/capture/mod.rs index 6ab8a82..63f6809 100644 --- a/src/capture/mod.rs +++ b/src/capture/mod.rs @@ -2,10 +2,10 @@ /// Captures screenshots at regular intervals and collects window metadata use chrono::{DateTime, Utc}; -use image::{DynamicImage, ImageFormat}; + use serde::{Deserialize, Serialize}; -use std::io::Cursor; -use crate::error::{AppError, Result}; + +use crate::error::Result; pub mod screenshot; pub mod window; @@ -44,19 +44,6 @@ impl CaptureData { } } - /// Compress image to WebP format with specified quality - pub fn compress_to_webp(image: DynamicImage, quality: u8) -> Result> { - // Convert to RGB8 for WebP encoding - let rgb_image = image.to_rgb8(); - let (width, height) = rgb_image.dimensions(); - - // Encode to WebP - let encoder = webp::Encoder::from_rgb(&rgb_image, width, height); - let webp = encoder.encode(quality as f32); - - Ok(webp.to_vec()) - } - /// Get file size in MB pub fn size_mb(&self) -> f64 { if let Some(ref data) = self.screenshot { diff --git a/src/capture/screenshot.rs b/src/capture/screenshot.rs index 878ff20..a57b9b0 100644 --- a/src/capture/screenshot.rs +++ b/src/capture/screenshot.rs @@ -1,6 +1,5 @@ /// Screenshot capture functionality use crate::error::{AppError, Result}; -use image::DynamicImage; use screenshots::Screen; pub struct ScreenshotCapture { @@ -21,17 +20,25 @@ impl ScreenshotCapture { .ok_or_else(|| AppError::Capture("No screens available".to_string()))?; // Capture screenshot - let image_buf = screen.capture() + let image = screen.capture() .map_err(|e| AppError::Capture(format!("Failed to capture screenshot: {}", e)))?; - // Convert to DynamicImage - let dynamic_image = DynamicImage::ImageRgba8(image_buf); + // Get dimensions and convert to RGB + let width = image.width() as u32; + let height = image.height() as u32; - // Compress to WebP - let rgb_image = dynamic_image.to_rgb8(); - let (width, height) = rgb_image.dimensions(); + // Convert RGBA to RGB (screenshots library returns RGBA) + let rgba_data = image.rgba(); + let mut rgb_data = Vec::with_capacity((width * height * 3) as usize); - let encoder = webp::Encoder::from_rgb(&rgb_image, width, height); + for chunk in rgba_data.chunks(4) { + rgb_data.push(chunk[0]); // R + rgb_data.push(chunk[1]); // G + rgb_data.push(chunk[2]); // B + // Skip alpha channel + } + + let encoder = webp::Encoder::from_rgb(&rgb_data, width, height); let webp = encoder.encode(quality as f32); log::debug!( diff --git a/src/report/generator.rs b/src/report/generator.rs index 2007639..f1adc39 100644 --- a/src/report/generator.rs +++ b/src/report/generator.rs @@ -2,7 +2,7 @@ use super::{DailyReport, ReportMetadata, Period, Activity, Statistics, CategoryStats, Entities, Screenshot}; use crate::storage::{Database, StoredCapture}; use crate::error::Result; -use chrono::{DateTime, Utc}; +use chrono::{Utc, Timelike}; use std::collections::HashMap; pub struct ReportGenerator { @@ -57,8 +57,6 @@ impl ReportGenerator { /// Convert stored captures to activities fn captures_to_activities(&self, captures: Vec) -> Vec { let mut activities = Vec::new(); - let mut tool_accumulator: HashMap> = HashMap::new(); - let mut lang_accumulator: HashMap> = HashMap::new(); for capture in captures { let duration = 300; // 5 minutes default (capture interval) diff --git a/src/report/mod.rs b/src/report/mod.rs index 3ab02f7..ee691f3 100644 --- a/src/report/mod.rs +++ b/src/report/mod.rs @@ -12,7 +12,6 @@ pub use export::JsonExporter; use chrono::{DateTime, Utc, Duration}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; -use crate::analysis::ActivityCategory; /// Daily activity report #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/src/storage/database.rs b/src/storage/database.rs index c2b3b3f..ccec299 100644 --- a/src/storage/database.rs +++ b/src/storage/database.rs @@ -1,5 +1,5 @@ /// Database operations with SQLite -use rusqlite::{params, Connection, Row}; +use rusqlite::{params, Connection, Row, OptionalExtension}; use std::path::Path; use chrono::{DateTime, Utc}; use sha2::{Sha256, Digest}; @@ -36,7 +36,7 @@ impl Database { .map_err(|e| AppError::Storage(format!("Failed to start transaction: {}", e)))?; // Insert window metadata - let window_id: i64 = tx.execute( + tx.execute( "INSERT INTO windows (title, process_name, process_id, is_active, timestamp) VALUES (?1, ?2, ?3, ?4, ?5)", params![ @@ -208,7 +208,7 @@ impl Database { /// Cleanup old data based on retention policy pub fn cleanup_old_data(&mut self, retention_days: i64) -> Result { let query = cleanup_old_data_query(retention_days); - let deleted = self.conn.execute_batch(&query) + self.conn.execute_batch(&query) .map_err(|e| AppError::Storage(format!("Failed to cleanup old data: {}", e)))?; log::info!("Cleaned up data older than {} days", retention_days); diff --git a/src/storage/encryption.rs b/src/storage/encryption.rs index a48c785..f481bf7 100644 --- a/src/storage/encryption.rs +++ b/src/storage/encryption.rs @@ -22,7 +22,7 @@ impl Encryptor { pub fn from_password(password: &str) -> Result { // Generate random salt let salt = SaltString::generate(&mut OsRng); - let key = Self::derive_key(password, salt.as_bytes())?; + let key = Self::derive_key(password, salt.as_str().as_bytes())?; let cipher = Aes256Gcm::new_from_slice(&key) .map_err(|e| AppError::Encryption(format!("Failed to create cipher: {}", e)))?;