# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Releasing a new version

When bumping the plugin version, update **all five** of these locations — missing any one causes a mismatch between what WordPress shows and what is deployed:

1. **[advance-post-grid.php](advance-post-grid.php)** — `Version:` header line and `@package` docblock tag
2. **[readme.md](readme.md)** — `Stable tag:` field and a new `= x.x.x =` changelog entry
3. **[changelog.txt](changelog.txt)** — new `= x.x.x =` entry matching readme.md
4. **[.github/workflows/deploy.yml](.github/workflows/deploy.yml)** — `VERSION:` env var
5. **[ROADMAP.md](ROADMAP.md)** — `Current version:` line at the top

After updating, run `npm run build` before tagging — the deploy workflow triggers on any pushed git tag.

---

## Commands

```bash
# Install all dependencies (Node + Composer)
npm run setup

# Development with watch mode
npm run start

# Production build
npm run build

# PHP linting (WordPress Coding Standards)
npm run lint:php

# PHP auto-fix
npm run fix:php

# JS linting
npm run lint:js

# CSS linting
npm run lint:css

# JS/CSS auto-format
npm run format
```

## Architecture

This is a **WordPress Gutenberg block plugin** (`weboptics/advance-post-grid`) that also supports a `[advance_post_grid]` shortcode.

### Data flow

All block attributes are defined in [src/block.json](src/block.json). They flow through two separate render paths that must stay in sync:

1. **Editor (React)** — [src/edit.jsx](src/edit.jsx) fetches posts via the `@wordpress/data` store (`core` entity records) and renders a live preview inside the block editor. Post types are fetched via `apiFetch({ path: '/wp/v2/types' })`, then filtered to exclude `attachment`, `nav_menu_item`, and any slug starting with `wp_` — all remaining REST-enabled public types are shown.

2. **Frontend (PHP)** — [src/render.php](src/render.php) runs a `WP_Query` using the saved attribute values and outputs HTML directly. Grid column counts are passed as CSS custom properties (`--adv-grid-value`, `--adv-grid-mobile-value`, `--adv-grid-tablet-value`) on the wrapper element.

### Class: `WO_APG_Init` ([advance-post-grid.php](advance-post-grid.php))

The single PHP class that bootstraps the plugin. It:
- Registers the block from the `build/` directory on `init` and localises `woApg.placeholderUrl` to the editor script
- Enqueues `view.js` manually on `wp_enqueue_scripts` with a jQuery dependency (prevents core from auto-enqueuing it without that dependency)
- Extends the REST API on `rest_api_init` to accept `rand`, `menu_order`, and `comment_count` as valid `orderby` values for all REST-enabled post types, keeping the editor preview in sync with the frontend `WP_Query`
- Provides static helpers used in `render.php`: `calculate_read_time()`, `pagination()`, `get_current_page_url()`, `get_page_number_from_url()`
- Handles shortcode rendering via `shortcode_render()`, which maps lowercase shortcode attributes back to camelCase before delegating to `build/render.php`

### Shortcode: `[advance_post_grid]`

All attributes are lowercase in the shortcode. They map to camelCase block attributes internally.

| Shortcode attr      | Block attribute      | Default  | Notes                                                              |
|---------------------|----------------------|----------|--------------------------------------------------------------------|
| `posttype`          | `postType`           | `post`   | Any REST-enabled public post type slug                             |
| `postgrid`          | `postGrid`           | `3`      | Desktop columns (1–4)                                              |
| `postgridtablet`    | `postGridTablet`     | `2`      | Tablet columns (1–4); only used when `enablepostgridres="true"`    |
| `postgridmobile`    | `postGridMobile`     | `1`      | Mobile columns (1–4); only used when `enablepostgridres="true"`    |
| `enablepostgridres` | `enablePostGridRes`  | `true`   | `true`/`false` — enable independent tablet/mobile column counts    |
| `postperpage`       | `postPerPage`        | `12`     | Posts per page                                                     |
| `postorder`         | `PostOrder`          | `DESC`   | `ASC` or `DESC`                                                    |
| `postorderby`       | `PostOrderBy`        | `title`  | `title`, `date`, `modified`, `menu_order`, `rand`, `comment_count` |
| `showhidepagi`      | `showHidePagi`       | `true`   | `true`/`false` — pagination                                        |
| `showhidelink`      | `showHideLink`       | `true`   | `true`/`false` — post links and Read More button                   |
| `showhidecat`       | `showHideCat`        | `true`   | `true`/`false` — category badges                                   |
| `showhidedate`      | `showHideDate`       | `true`   | `true`/`false` — post date                                         |
| `showhidert`        | `showHideRT`         | `true`   | `true`/`false` — estimated read time                               |

### Build output

`npm run build` (via `@wordpress/scripts`) compiles `src/` → `build/` and copies `render.php`. The `build/` directory is what WordPress loads — never edit files there directly.

A custom [webpack.config.js](webpack.config.js) extends the default config to copy `src/assets/images/` → `build/assets/images/` via `CopyWebpackPlugin`. The placeholder SVG and other images must be referenced from `build/assets/images/` in PHP, not from `src/`.

### PHP coding standards

`phpcs.xml.dist` enforces the `WordPress` ruleset. Required global prefixes are `WO_APG`, `Woagp`, `woagp_`. The `vendor/` and `node_modules/` directories are excluded from sniffing.

### Responsive grid

When `enablePostGridRes` is `false`, mobile and tablet columns default to the desktop `postGrid` value. When `true`, `postGridTablet` and `postGridMobile` are used independently. This logic exists in both `render.php` (lines 19–26) and implicitly in `edit.jsx`.
