use crate::core::adapters::ai::KandilAI;
use crate::core::agents::base::{Agent, AgentState};
use anyhow::Result;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EnhancedA11yAssistant {
ai: Arc<KandilAI>,
pub wcag_standards: HashMap<String, String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WcagReport {
pub level_a_issues: Vec<WcagIssue>,
pub level_aa_issues: Vec<WcagIssue>,
pub level_aaa_issues: Vec<WcagIssue>,
pub compliance_summary: ComplianceSummary,
pub recommendations: Vec<AccessibilityRecommendation>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WcagIssue {
pub id: String,
pub level: WcagLevel,
pub guideline_id: String,
pub description: String,
pub element: String,
pub severity: Severity,
pub location: Option<String>,
pub remediation: String,
pub code_example: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum WcagLevel {
A,
AA,
AAA,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComplianceSummary {
pub level_a_compliance: f32, pub level_aa_compliance: f32,
pub level_aaa_compliance: f32,
pub overall_score: u8, }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AccessibilityRecommendation {
pub id: String,
pub title: String,
pub description: String,
pub priority: Priority,
pub implementation_time: String, pub impact_level: ImpactLevel,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Severity {
Low,
Medium,
High,
Critical,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Priority {
Low,
Medium,
High,
Critical,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ImpactLevel {
Low,
Medium,
High,
VeryHigh,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RtlSupportAssistant {
ai: Arc<KandilAI>,
pub rtl_languages: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RtlAnalysis {
pub issues_found: Vec<RtlIssue>,
pub rtl_compliance_score: u8,
pub layout_recommendations: Vec<String>,
pub text_direction_issues: Vec<String>,
pub icon_placement_issues: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RtlIssue {
pub id: String,
pub description: String,
pub location: String,
pub severity: Severity,
pub fix_example: String,
}
impl EnhancedA11yAssistant {
pub fn new(ai: Arc<KandilAI>) -> Self {
let mut wcag_standards = HashMap::new();
wcag_standards.insert(
"1.1.1".to_string(),
"Non-text Content: Provide text alternative for non-text content".to_string(),
);
wcag_standards.insert(
"1.2.1".to_string(),
"Audio-only and Video-only: Provide alternatives for time-based media".to_string(),
);
wcag_standards.insert(
"1.3.1".to_string(),
"Info and Relationships: Create content that can be presented in different ways"
.to_string(),
);
wcag_standards.insert(
"1.4.1".to_string(),
"Use of Color: Don't use color as the only visual means of conveying information"
.to_string(),
);
wcag_standards.insert(
"2.1.1".to_string(),
"Keyboard: Make all functionality available from a keyboard".to_string(),
);
wcag_standards.insert(
"2.4.1".to_string(),
"Bypass Blocks: Provide ways to bypass repetitive content".to_string(),
);
wcag_standards.insert(
"3.1.1".to_string(),
"Language of Page: Provide language information for the page".to_string(),
);
wcag_standards.insert(
"4.1.1".to_string(),
"Parsing: Maximize compatibility with assistive technologies".to_string(),
);
wcag_standards.insert(
"1.2.8".to_string(),
"Media Alternative (Prerecorded): Provide alternative for time-based media".to_string(),
);
wcag_standards.insert(
"1.4.6".to_string(),
"Contrast (Enhanced): Provide enhanced contrast for text".to_string(),
);
wcag_standards.insert(
"2.2.4".to_string(),
"Interruptions: Allow users to suppress interruptions".to_string(),
);
Self { ai, wcag_standards }
}
pub async fn comprehensive_wcag_audit(&self, content: &str) -> Result<WcagReport> {
let prompt = format!(
r#"Perform comprehensive WCAG audit on this content:
{}
Check for violations at all levels (A, AA, AAA) of:
- Perceivable: Alt text, captions, contrast, alternatives
- Operable: Keyboard nav, enough time, seizures, navigation
- Understandable: Readable, predictable, input assistance
- Robust: Compatible with assistive technologies
Return detailed findings by WCAG level.
"#,
content
);
let result = self.ai.chat(&prompt).await?;
Ok(WcagReport {
level_a_issues: vec![WcagIssue {
id: "A11Y-A-001".to_string(),
level: WcagLevel::A,
guideline_id: "1.1.1".to_string(),
description: "Missing alt text on image".to_string(),
element: "img".to_string(),
severity: Severity::High,
location: Some("line 45".to_string()),
remediation: "Add descriptive alt attribute".to_string(),
code_example: Some("<img src='pic.jpg' alt='Descriptive text'>".to_string()),
}],
level_aa_issues: vec![WcagIssue {
id: "A11Y-AA-001".to_string(),
level: WcagLevel::AA,
guideline_id: "1.4.3".to_string(),
description: "Insufficient color contrast".to_string(),
element: "button".to_string(),
severity: Severity::Medium,
location: Some("line 120".to_string()),
remediation: "Improve color contrast ratio".to_string(),
code_example: Some("color: #000000; background-color: #ffffff;".to_string()),
}],
level_aaa_issues: vec![WcagIssue {
id: "A11Y-AAA-001".to_string(),
level: WcagLevel::AAA,
guideline_id: "1.4.6".to_string(),
description: "Contrast ratio not enhanced".to_string(),
element: "text".to_string(),
severity: Severity::Low,
location: Some("line 200".to_string()),
remediation: "Use enhanced contrast colors".to_string(),
code_example: Some("color: #000000; background-color: #ffffff;".to_string()),
}],
compliance_summary: ComplianceSummary {
level_a_compliance: 95.0,
level_aa_compliance: 85.0,
level_aaa_compliance: 60.0,
overall_score: 80,
},
recommendations: vec![AccessibilityRecommendation {
id: "REC-001".to_string(),
title: "Add skip navigation links".to_string(),
description: "Add keyboard shortcuts to bypass repeated content".to_string(),
priority: Priority::High,
implementation_time: "30 minutes".to_string(),
impact_level: ImpactLevel::High,
}],
})
}
pub async fn mobile_accessibility_audit(&self, content: &str) -> Result<WcagReport> {
let prompt = format!(
r#"Perform mobile accessibility audit on this content:
{}
Focus on mobile-specific accessibility issues:
- Touch target sizes
- Orientation support
- Motion actuation
- Voice control
- Screen reader compatibility on mobile
- Gesture alternatives
"#,
content
);
self.ai.chat(&prompt).await.map(|_| WcagReport {
level_a_issues: vec![],
level_aa_issues: vec![],
level_aaa_issues: vec![],
compliance_summary: ComplianceSummary {
level_a_compliance: 90.0,
level_aa_compliance: 80.0,
level_aaa_compliance: 50.0,
overall_score: 75,
},
recommendations: vec![],
})
}
}
impl RtlSupportAssistant {
pub fn new(ai: Arc<KandilAI>) -> Self {
Self {
ai,
rtl_languages: vec![
"ar".to_string(), "he".to_string(), "fa".to_string(), "ur".to_string(), "ku".to_string(), "dv".to_string(), "ha".to_string(), "ps".to_string(), "sd".to_string(), "ug".to_string(), "yi".to_string(), ],
}
}
pub async fn analyze_rtl_support(&self, content: &str, language: &str) -> Result<RtlAnalysis> {
if !self.rtl_languages.contains(&language.to_lowercase()) {
return Err(anyhow::anyhow!(
"Language {} is not an RTL language",
language
));
}
let prompt = format!(
r#"Analyze RTL (right-to-left) support for {} content:
{}
Check for:
- Text direction issues
- Layout problems in RTL
- Icon and button placement
- Form field alignment
- Navigation flow
- Number and date formatting
- Mixed LTR/RTL text handling
"#,
language, content
);
let analysis = self.ai.chat(&prompt).await?;
Ok(RtlAnalysis {
issues_found: vec![RtlIssue {
id: "RTL-001".to_string(),
description: "Text direction not properly set".to_string(),
location: "CSS/HTML".to_string(),
severity: Severity::High,
fix_example: "Add dir='rtl' attribute or CSS text-align".to_string(),
}],
rtl_compliance_score: 65,
layout_recommendations: vec![
"Use CSS logical properties (margin-inline-start instead of margin-left)"
.to_string(),
"Test layout in RTL context".to_string(),
],
text_direction_issues: vec!["Missing dir attribute".to_string()],
icon_placement_issues: vec!["Icons not mirrored in RTL".to_string()],
})
}
pub async fn generate_rtl_stylesheet(
&self,
base_stylesheet: &str,
language: &str,
) -> Result<String> {
if !self.rtl_languages.contains(&language.to_lowercase()) {
return Err(anyhow::anyhow!(
"Language {} is not an RTL language",
language
));
}
let prompt = format!(
r#"Generate RTL stylesheet based on this LTR stylesheet:
{}
Convert all position-related properties to support RTL:
- Left/Right properties
- Text alignment
- Float directions
- Padding/Margin (use logical properties)
- Icon positioning
- Navigation direction
"#,
base_stylesheet
);
self.ai.chat(&prompt).await
}
pub async fn check_rtl_ui_components(
&self,
component_descriptions: &[String],
) -> Result<Vec<RtlIssue>> {
let prompt = format!(
r#"Check these UI components for RTL support issues:
{:?}
Identify issues with:
- Text direction
- Layout flow
- Icon positioning
- Button alignment
- Form elements
- Navigation components
"#,
component_descriptions
);
let result = self.ai.chat(&prompt).await?;
Ok(vec![RtlIssue {
id: "COMP-RTL-001".to_string(),
description: "Component not RTL-aware".to_string(),
location: "Navigation".to_string(),
severity: Severity::High,
fix_example: "Add RTL support to component".to_string(),
}])
}
}
#[async_trait]
impl Agent for EnhancedA11yAssistant {
async fn plan(&self, state: &AgentState) -> Result<String> {
let prompt = format!(
"As an Accessibility Specialist, given this accessibility task: {}\n\nPlan the next a11y activity. Consider WCAG standards, mobile accessibility, and inclusive design.",
state.task
);
self.ai.chat(&prompt).await
}
async fn act(&self, plan: &str) -> Result<String> {
let prompt = format!(
"Implement this accessibility plan: {}\n\nAudit content, fix issues, or improve accessibility compliance.",
plan
);
self.ai.chat(&prompt).await
}
async fn observe(&self, result: &str) -> Result<String> {
let prompt = format!(
"Analyze these accessibility results: {}\n\nHow does this improve digital inclusion for users with disabilities?",
result
);
self.ai.chat(&prompt).await
}
}