# AGENTS.md

Guidance for Codex (Codex.ai/code) in this repo.

## Project

**Extra Fields for ACF** — WordPress plugin extending ACF with custom field types. Requires ACF free or Pro v5.9+. Freemium via Freemius (free fields, Pro-gated options).

**v1.0.1:** 5 fields — Icon Picker, Unit Control, Gradient Picker, Responsive Range, Date Range  
**v1.1 next:** Table Field, Country Selector, Phone Number (see `docs/extra-fields-acf-prd.md`)

## Commands

### Linting
```bash
phpcs --standard=WordPress extra-fields-for-acf.php includes/ fields/
phpcbf --standard=WordPress extra-fields-for-acf.php includes/ fields/
```

### Translation POT
```bash
wp i18n make-pot . languages/extra-fields-for-acf.pot --domain=extra-fields-for-acf
```

### Build for wordpress.org
```bash
wp dist-archive . --format=zip
```

## Architecture

### Bootstrap flow
```
extra-fields-for-acf.php
  └── defines constants (EFACF_VERSION, EFACF_PATH, EFACF_URL, EFACF_BASENAME)
  └── Freemius dual-plugin pattern:
        if effa_fs() exists → set_basename() (premium coexistence)
        else → define effa_fs(), init SDK, instantiate EFACF_Main
  └── EFACF_Main::__construct()
        ├── require helpers.php (needed by settings page + field classes)
        ├── new EFACF_Settings() (registers admin menu + Settings API)
        └── add_action('init', check_acf, 15)
  └── check_acf() → confirms ACF active → init()
  └── init() → new EFACF_Loader() → register_fields()
```

### Field structure
Field classes in `fields/free/class-field-<slug>.php`, namespace `EFACF\Fields\Free`. Extend `\acf_field` (backslash required — global class from within namespace).

```
fields/free/
  class-field-icon-picker.php       → EFACF\Fields\Free\IconPickerField
  class-field-unit-control.php      → EFACF\Fields\Free\UnitControlField
  class-field-gradient-picker.php   → EFACF\Fields\Free\GradientPickerField
  class-field-responsive-range.php  → EFACF\Fields\Free\ResponsiveRangeField
  class-field-date-range.php        → EFACF\Fields\Free\DateRangeField
```

### Adding a new field
1. Create `fields/free/class-field-<slug>.php` with `namespace EFACF\Fields\Free;`
2. Extend `\acf_field`. Set `$this->name`, `$this->label`, `$this->category`, `$this->defaults`, call `parent::__construct()`. No `new ClassName()` at bottom — loader handles instantiation.
3. Implement `input_enqueue()`, `render_field_settings()`, `render_field()`, `format_value()`, `validate_value()`.
4. Add to `EFACF_Loader::get_field_map()` and `EFACF_Settings::get_field_data()`.
5. Add JS to `assets/js/<slug>.js` using `acf.addAction('ready_field/type=<name>', function(field) { ... })`.

### Key patterns

**Asset loading** — each field defines `input_enqueue(): void`; ACF calls automatically via `acf/input/admin_enqueue_scripts` in `acf_field::__construct()`. Existing fields share `efacf-admin` handle. New fields add own handles. WP deduplicates by handle.

**Loader** (`includes/class-efacf-loader.php`) — reads `efacf_settings` option; only `require_once`s + instantiates enabled fields. Disabled fields never load, register, or enqueue.

**Pro gating** — `efacf_is_pro()` (calls `effa_fs()->is_paying()`). Wrap locked settings in `efacf_pro_badge($message)`. Both in `includes/helpers.php`.

**Helpers** — `efacf_` prefix, in `includes/helpers.php`. Filterable data via `apply_filters('efacf_*', ...)`.

**JS field init** — `acf.addAction('ready_field/type=<field_name>', function(field) { ... })`. Use `field.$el.find(...)` for all DOM queries — supports multiple fields per page.

**Freemius** — SDK at `vendor/freemius/start.php`. Accessed via `effa_fs()`. `if (function_exists('effa_fs')) { set_basename() } else { ... }` wrapper supports future premium coexistence.

**Constants**
```php
EFACF_VERSION   // plugin version string
EFACF_PATH      // absolute server path with trailing slash
EFACF_URL       // URL with trailing slash
EFACF_BASENAME  // plugin_basename(__FILE__)
```

**Settings storage** — `get_option('efacf_settings')` → `['fields' => ['unit_control' => true, ...]]`. Missing key defaults to `true` (enabled).

**Icon Picker** — FA Free JSON at `assets/vendor/FontAwesome-v5.0.9-Free.json` uses `{...}` not `[...]`; `get_icon_list()` normalizes before `json_decode`. FA5 CSS from CDN, ACF screens only.

**Text domain:** `extra-fields-for-acf`

## Compatibility

- WordPress 5.6+, PHP 7.4+, ACF free or Pro 5.9+
- Tested up to WordPress 6.8
