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)]
pub struct A11yAssistant {
ai: Arc<KandilAI>,
pub wcag_standards: HashMap<String, String>, }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct A11yReport {
pub issues_found: Vec<A11yIssue>,
pub compliance_level: ComplianceLevel,
pub accessibility_score: u8,
pub recommendations: Vec<String>,
pub priority_issues: Vec<String>,
pub overall_status: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct A11yIssue {
pub id: String,
pub element: String,
pub description: String,
pub wcag_level: WcagLevel,
pub wcag_guidelines: Vec<String>,
pub severity: Severity,
pub location: Option<String>, pub remediation: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum WcagLevel {
A, AA, AAA, }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ComplianceLevel {
NonCompliant,
PartiallyCompliant,
Compliant,
ExceedsCompliance,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Severity {
Low,
Medium,
High,
Critical,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WcagGuideline {
pub id: String,
pub title: String,
pub description: String,
pub techniques: Vec<String>,
pub failures: Vec<String>,
}
impl A11yAssistant {
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(),
);
Self { ai, wcag_standards }
}
pub async fn wcag_audit(&self, content: &str, level: WcagLevel) -> Result<A11yReport> {
let level_str = match level {
WcagLevel::A => "A",
WcagLevel::AA => "AA",
WcagLevel::AAA => "AAA",
};
let prompt = format!(
r#"Perform WCAG {} audit on this content:
{}
Check for violations of:
- Perceivable: Alt text, captions, contrast
- Operable: Keyboard nav, focus order
- Understandable: Labels, error messages
- Robust: Valid HTML, ARIA
Return violations with remediation code.
"#,
level_str, content
);
let result = self.ai.chat(&prompt).await?;
let issues = vec![A11yIssue {
id: "A11Y-001".to_string(),
element: "img".to_string(),
description: "Missing alt text".to_string(),
wcag_level: WcagLevel::A,
wcag_guidelines: vec!["1.1.1".to_string()],
severity: Severity::High,
location: Some("index.html:line 45".to_string()),
remediation: "Add descriptive alt attribute".to_string(),
}];
Ok(A11yReport {
issues_found: issues,
compliance_level: ComplianceLevel::PartiallyCompliant,
accessibility_score: 78,
recommendations: vec![
"Add proper alt text to images".to_string(),
"Improve color contrast ratios".to_string(),
"Add skip navigation links".to_string(),
],
priority_issues: vec!["Image alt text missing".to_string()],
overall_status: format!("WCAG {} compliance review completed", level_str),
})
}
pub async fn audit_html(&self, html_content: &str) -> Result<A11yReport> {
let prompt = format!(
r#"Audit this HTML for accessibility:
{}
Focus on:
- Semantic HTML usage
- Proper heading structure
- Form labels and associations
- Link text descriptiveness
- ARIA attributes
- Color contrast
- Focus management
"#,
html_content
);
self.ai.chat(&prompt).await.map(|_| A11yReport {
issues_found: vec![],
compliance_level: ComplianceLevel::PartiallyCompliant,
accessibility_score: 75,
recommendations: vec!["Improve semantic structure".to_string()],
priority_issues: vec!["Heading structure issues".to_string()],
overall_status: "HTML accessibility review completed".to_string(),
})
}
pub async fn generate_a11y_guidelines(&self, component_type: &str) -> Result<String> {
let prompt = format!(
r#"Generate accessibility guidelines for {} components.
Include requirements for:
- Keyboard navigation
- Screen reader compatibility
- Focus management
- ARIA attributes
- Color contrast
- Alternative text
"#,
component_type
);
self.ai.chat(&prompt).await
}
pub async fn remediate_issues(&self, html_content: &str) -> Result<String> {
let prompt = format!(
r#"Fix accessibility issues in this HTML:
{}
Apply fixes for:
- Missing alt text
- Insufficient color contrast
- Poor heading structure
- Missing form labels
- Non-semantic markup
"#,
html_content
);
self.ai.chat(&prompt).await
}
}
#[async_trait]
impl Agent for A11yAssistant {
async fn plan(&self, state: &AgentState) -> Result<String> {
let prompt = format!(
"As an accessibility specialist, given this a11y task: {}\n\nPlan the next accessibility activity. Consider WCAG standards, user needs, and implementation feasibility.",
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 update accessibility guidelines.",
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 and user experience for people with disabilities?",
result
);
self.ai.chat(&prompt).await
}
}