# Flux AI Alt Text & Accessibility Audit

A WordPress plugin that scans your media library for missing or weak alt text and generates AI-powered replacements using OpenAI, Google Gemini, or Anthropic Claude vision APIs.

**Version:** 4.1.0
**Requires:** WordPress 5.8+ | PHP 8.0+
**Tested up to:** WordPress 7.0
**License:** GPL-2.0-or-later

> **README maintenance:** Keep this file in sync with the codebase. It is the live architecture and feature-development reference for this plugin. When you add or change admin UI, REST endpoints, services, shared components, or testing patterns, update the relevant sections here in the same pull request.

## Features

### Compliance Dashboard

- On-demand scan of your entire media library
- Alt text coverage score with per-category breakdown (bare layout on Overview; circular gauge on Compliance tab)
- Images classified into 6 categories: **missing**, **placeholder**, **duplicate**, **descriptive**, **contextual**, **decorative**
- Graduated compliance colors on Risk breakdown cards and Media `alt_category` chips (problem vs. good categories at a glance)
- Filter by risk category and fix issues in bulk
- Mark images as decorative (WCAG 2.1 best practice)
- Auto-reclassification when alt text is changed

### AI Alt Text Generation

- Generate descriptive, context-aware alt text using your choice of provider:
  - **OpenAI** (gpt-4o-mini)
  - **Google Gemini** (gemini-2.5-flash-lite)
  - **Anthropic Claude** (claude-haiku-4-5)
- Bulk generation with background processing via Action Scheduler
- Review, edit, and approve recommendations before applying
- Contextual prompts that incorporate post and product data

### Native Media Library Quick Actions

- Per-attachment actions in the WordPress **Media Library** (list row actions and attachment details sidebar):
  - **Generate alt** — generates and immediately saves alt text to the attachment
  - **Mark decorative** — clears alt text and marks the image decorative (WCAG)
  - **Apply SEO filename** — renames the file when a suggested filename exists in scan meta
- Uses the same SSOT services as the plugin Media page (`AltTextApiService`, `ComplianceScanService`, `SuiteFilenameSeoService`)
- Bulk native actions are out of scope; multi-item work should use existing async batch flows in the plugin UI

### WooCommerce Integration

- Automatic detection of product images (featured, gallery, variations)
- Alt text generation includes product name and attributes
- Filter media by WooCommerce product association

### Usage Tracking

- Per-request tracking of API tokens, cost, and model for **BYOK** traffic
- Current-month usage dashboard with cost estimates
- Automatic monthly reset
- When a Flux Suite license is active, Overview usage figures do not represent Suite-managed generation or Suite billing

## Installation

### From WordPress.org

1. Go to **Plugins > Add New** in your WordPress admin
2. Search for "Flux AI Alt Text"
3. Click **Install Now**, then **Activate**

### Manual Installation

1. Download the plugin ZIP from the [releases page](https://github.com/stratease/flux-ai-media-alt-creator/releases)
2. Upload to `/wp-content/plugins/flux-ai-media-alt-creator/`
3. Activate through the **Plugins** screen in WordPress

### From Source

```bash
git clone https://github.com/stratease/flux-ai-media-alt-creator.git
cd flux-ai-media-alt-creator
composer install
npm install
npm run build
```

## Configuration

1. Navigate to **Flux Suite > AI Media Alt Creator** in your WordPress admin
2. Go to the **Settings** tab
3. With a **Flux Suite license**, choose **Use Flux Suite hosted generation** (default on) or turn it off to use **BYOK** (bring your own key). Without a license, only BYOK is available.
4. For BYOK: choose your AI provider and enter the API key for your chosen provider:
   - [OpenAI API keys](https://platform.openai.com/settings/organization/api-keys)
   - [Google Gemini API keys](https://aistudio.google.com/apikey)
   - [Anthropic Claude API keys](https://console.anthropic.com/settings/keys)
5. Under **Automation** on the same Settings page, optionally enable automated alt text queueing and a daily compliance scan. With BYOK, a configured vision API key is required for automated generation passes; scheduled compliance scans run locally without calling external vision APIs.

## Architecture

### Tech Stack

| Layer | Technology |
|---|---|
| Backend | PHP 8.0+, WordPress REST API, Action Scheduler |
| Frontend | React 18, MUI v5, TanStack React Query v5, React Router v7 |
| Build | Webpack 5, Babel, Composer (Strauss for namespace prefixing) |
| Testing | Playwright (E2E regression) |

### Project Structure

```
flux-ai-media-alt-creator/
├── flux-ai-media-alt-creator.php  # Plugin bootstrap
├── app/                           # PHP application (FluxAIMediaAltCreator\App\)
│   ├── Plugin.php                 # Main orchestrator
│   ├── Http/Controllers/          # REST API controllers
│   ├── Providers/                 # Boot hooks (API routes, Action Scheduler, automation wiring, native media)
│   └── Services/                  # Business logic
│       └── Vision/                # AI provider abstraction layer
├── assets/js/src/                 # React SPA source
│   ├── pages/                     # Page components (Overview, Media, Compliance, Settings)
│   ├── native-media-library/      # Native Media Library quick-action script
│   ├── hooks/                     # React Query hooks (useAltText, useMedia, etc.)
│   ├── components/                # Shared UI components
│   ├── constants/                 # SSOT config (e.g. compliance categories)
│   ├── utils/                     # Shared frontend helpers (e.g. compliance colors)
│   └── services/                  # API service layer
├── src/assets/common/             # Shared common library assets
├── tests/regression/              # Playwright E2E tests
├── vendor/                        # Composer dependencies
└── vendor-prefixed/               # Strauss-prefixed dependencies
```

### Backend Design

The backend uses a **service-oriented architecture** with singletons for shared state:

- **`AltTextApiService`** is the central facade — controllers never call vision providers directly; apply logic (category skip, filename rewrite) is unified for all backends
- **`AltTextBackendResolver`** is the SSOT for hosted Suite vs BYOK generation (`use_flux_suite_hosted` when licensed)
- **`VisionProviderFactory`** implements Strategy + Factory for BYOK providers; **`SuiteHostedVisionProvider`** wraps Suite external API
- **`AltTextVisionPromptBuilder`** / **`AltTextVisionMetadataParser`** share the Flux Services JSON contract (`alt_text`, `file_name`) across backends
- **`ComplianceScanService`** classifies alt text into risk categories
- **`AsyncJobService`** handles background batch processing via Action Scheduler
- **`NativeMediaActionService`** orchestrates per-attachment native Media Library actions (generate/apply, decorative, filename)
- **`NativeMediaLibraryProvider`** registers native Media Library quick actions and admin assets
- **`SuiteAutomationProvider`** registers automation and compliance scan hooks; schedule reconciliation lives on **`SuiteAutomationService`**
- **`SuiteMediaAdminProvider`** registers media admin SPA extensions (suggested filename column, REST upload response patching)
- Filter hooks in `AltTextApiService` remain for genuine extension points; Suite generation is composed in-process where practical

### REST API

All routes are registered under `flux-ai-media-alt-creator/v1/`:

| Endpoint | Method | Description |
|---|---|---|
| `/alt-text/generate` | POST | Generate alt text for a single image |
| `/alt-text/apply` | POST | Apply generated alt text to an image |
| `/alt-text/batch-generate` | POST | Schedule bulk alt text generation |
| `/media` | GET | Paginated media library with filters |
| `/media/{id}` | GET | Single media item details |
| `/media/scan` | POST | Trigger media library scan |
| `/media/type-groups` | GET | Available MIME type groups |
| `/options` | GET/POST | Read/update plugin settings |
| `/field-visibility` | GET | Feature flag visibility |
| `/compliance/summary` | GET | Compliance score and category counts |
| `/compliance/scan` | POST | Run compliance classification scan |
| `/compliance/set-category` | POST | Set compliance category for an image |
| `/usage` | GET | Current-month usage statistics (BYOK only; not representative when Suite-managed generation is active) |
| `/automation/settings` | GET/POST | Automation toggles (scheduled queueing and daily compliance scan) |
| `/automation/license-status` | GET | License validation snapshot for admin |
| `/automation/filename-apply` | POST | Apply SEO filename to an attachment |
| `/native-media/action` | POST | Run native Media Library quick action (`generate_alt`, `mark_decorative`, `apply_filename`) |

### Frontend

A React SPA served on the WordPress admin page with HashRouter navigation:

| Route | Page | Description |
|---|---|---|
| `#/overview` | OverviewPage | Dashboard with usage stats and compliance summary |
| `#/media` | MediaPage | Media library with batch operations |
| `#/compliance` | CompliancePage | Compliance audit and category management |
| `#/settings` | SettingsPage | Hosted/BYOK toggle when licensed; provider and API keys; **Automation** |

#### Admin UI conventions

- **Theme surfaces:** Dashboard tiles use MUI `Paper` with the shared Flux theme defaults (`elevation: 1`, 8px radius). Avoid ad-hoc borders or shadow overrides on `Paper` unless there is a documented exception (e.g. marketing `FluxSuiteCard`).
- **Coverage summary:** `ComplianceCoverageSection` renders outside inner `Paper` wrappers so Overview and Compliance are not nested in extra cards. The outer `PageLayout` shell `Paper` remains the only page-level container.
- **Compliance SSOT:** Category labels, descriptions, CTAs, and palette mapping live in `assets/js/src/constants/complianceCategories.js`. Color helpers (`getCoverageGaugeColor`, `getCategorySurfaceSx`, etc.) live in `assets/js/src/utils/complianceColors.js`.
- **Shared compliance components** (exported from `assets/js/src/components/`):
  - `ComplianceCoverageSection` — stats, optional circular gauge, alerts, scan/actions
  - `ComplianceCategoryCard` — Risk breakdown tiles on Compliance
  - `ComplianceCategoryChip` — colored `alt_category` chips on Media (and any future surfaces)
- **Category color map** (MUI semantic palette):

| Category | Palette | Role |
|---|---|---|
| missing | `error` | Highest risk |
| placeholder | `warning` | Weak alt text |
| duplicate | `primary` | Needs uniqueness |
| descriptive | `primary` | Good baseline |
| contextual | `success` | Best practice |
| decorative | `success` (muted) | Intentionally empty alt |

- **Coverage gauge tiers** (Compliance tab): `success` ≥ 90%, `primary` ≥ 70%, `warning` &lt; 70% — via `getCoverageGaugeColor()`.
- **High Risk stat:** Uses `error` emphasis when `high_risk_count` &gt; 0.
- **E2E hooks:** `data-testid="compliance-coverage-section"`, `compliance-category-card-{key}`, `compliance-category-chip-{key}` — see `tests/regression/compliance-ui.spec.ts`.

When adding admin UI, extend the SSOT/constants and shared components first; do not duplicate category metadata or colors in page files.

## Development

### Prerequisites

- PHP 8.0+ with extensions: `mbstring`, `dom`, `json`, `libxml`, `tokenizer`, `xml`, `xmlwriter` (required for PHPUnit)
- Composer
- Node.js (LTS)
- A WordPress development environment

### Commands

| Command | Description |
|---|---|
| `composer install` | Install PHP dependencies and prefix namespaces |
| `npm install` | Install Node dependencies |
| `npm run build` | Production webpack build |
| `npm run dev` | Development build with watch mode |
| `npm run start` | Dev server on port 3002 with HMR |
| `composer run phpcs` | Check WordPress coding standards |
| `composer run phpstan` | Run static analysis |
| `composer run test` | Run PHPUnit tests |
| `composer run quality` | Run all quality checks (phpcs + phpstan + tests) |
| `composer run prefix-namespaces` | Re-run Strauss namespace prefixing |
| `npx playwright test` | Run E2E regression tests |
| `npx playwright test tests/regression/compliance-ui.spec.ts` | Compliance admin UI regression |
| `npx playwright test tests/regression/native-media-library.spec.ts` | Native Media Library quick actions |

### Local webpack dev script base

For HMR during admin SPA development, define this **only** in `wp-config.php` (never in plugin PHP):

```php
define( 'FLUX_AI_MEDIA_ALT_CREATOR_DEV_SCRIPT_BASE', 'http://localhost:3002' );
```

Requires `WP_DEBUG` and `SCRIPT_DEBUG` to be true. See [flux-plugins-common](https://github.com/stratease/flux-plugins-common) optional dev script base guidance.

### Namespace Prefixing

This plugin uses [Strauss](https://github.com/BrianHenryIE/strauss) to prefix `stratease/flux-plugins-common` under the `FluxAIMediaAltCreator\` namespace. This runs automatically via Composer post-install/update hooks. The prefixed output goes to `vendor-prefixed/`.

**Flux Plugins Common v1.2.0+:** The shared library no longer bundles Monolog or `psr/log`. After `composer install`, confirm `vendor-prefixed/monolog/` is absent.

**Build pipeline (aligned with [flux-plugins-common](https://github.com/stratease/flux-plugins-common)):**

1. `composer install` — copies runtime common assets (`src/assets/common/js/dist` + `images`), runs Strauss, fixes `vendor/bin` wrappers via `fix-bin-wrappers.php`.
2. `npm run build` — plugin admin bundle to `assets/js/dist/`.
3. `./vendor/bin/build-plugin.sh` — WordPress.org packaging tree (requires wrappers from step 1).
4. Optional: `flux-plugins-common/bin/verify-plugin-distribution.sh` from the plugin root.

**Common assets:** Only `js/dist` and `images` are copied into `src/assets/common/`. Do not commit `src/assets/common/js/src/` (webpack resolves sources from `vendor-prefixed/stratease/flux-plugins-common/src/assets/js/src` or set `FLUX_PLUGINS_COMMON_PATH` to a monorepo checkout of flux-plugins-common).

**Hook naming:** Use `flux_ai_alt_creator/{class_snake}/{method_snake}` (see [flux-plugins-common README](https://github.com/stratease/flux-plugins-common) Hook Naming Convention). Legacy hooks remain supported via `HookCompat` until 5.0.0.

### Contributing

See [CONTRIBUTING.md](CONTRIBUTING.md) for coding standards, architecture details, and the pull request process.

## Privacy

When you generate alt text with BYOK, image data is sent to your chosen AI provider for analysis. When a Flux Suite license is active, hosted generation is handled through Flux infrastructure instead of your provider keys in this plugin. Automation may process images in the background per Settings. See the [Privacy Policy](https://fluxplugins.com/privacy-policy/) for full details.

## Flux Suite (optional license)

A Flux Suite license enables optional **Suite-managed** hosted generation and billing (serviceware). It is **not** combined with BYOK on the same site configuration: while the license is valid, provider API keys are not edited or used for generation through this plugin’s Settings. Automation and compliance scan toggles remain available under **Settings → Automation** without a license; scheduled compliance scans run locally. Background alt generation uses Flux Suite hosted generation when a license is active, or your configured BYOK provider key when not.

## Support

- [WordPress.org Support Forum](https://wordpress.org/support/plugin/flux-ai-media-alt-creator/)
- [GitHub Issues](https://github.com/stratease/flux-ai-media-alt-creator/issues)
- [Flux Plugins](https://fluxplugins.com)

## License

GPL-2.0-or-later. See [LICENSE](https://www.gnu.org/licenses/gpl-2.0.html).
