Internal: Stabilize video block end-to-end coverage across file, Vimeo, Wistia, and YouTube suites by matching viewport-driven playback behavior, retrying stale teardown nonces after local 403 responses, and keeping the mock YouTube ready handshake alive for late iframe subscribers.
Per-release Slack notifications: The Cloudflare Worker now posts a Slack message immediately after each new published release is processed. The message includes the AI-generated compact summary and links to the repo changelog page and the GitHub release.
YouTube: Only add playlist={videoId} URL parameter when loop is enabled. Previously it was added unconditionally, which surfaced prev/next navigation buttons in the post-March-2026 YouTube embed redesign.
YouTube: Remove deprecated showinfo and modestbranding parameters (no effect since 2018/2023).
Internal: Shard the E2E Playwright suite across parallel CI runners with isolated WordPress Playground instances per shard, running strict-autoplay coverage as a separate sharded job and cancelling superseded runs
Internal: Use GitHub runner system Chrome for CI E2E tests, skip Playwright browser downloads during dependency install, and keep video recording local-only to cut install and run time
Internal: Switch the CI E2E reporter to per-shard blob output with a merge job that assembles the full HTML report and publishes the slowest tests to the run summary
Internal: Cache the REST nonce used by E2E fixture setup, track the shared video fixture, and skip per-spec fixture teardown on ephemeral CI Playgrounds
Documentation: Add E2E testing guidance for video iframe readiness, lazy-viewport fixtures, user-pause assertions, local Playwright browser dependencies, and the mock YouTube iframe pattern for CI
Fixed
Video YouTube: Inject the browser-visible origin= URL parameter into the embed src on the client side so YouTube's iframe API dispatches reply postMessages in WordPress Playground and other proxied environments where the server-side origin does not match the actual page origin
Video YouTube: Wait for iframe API ready or delivery events before flushing queued playback commands, derive playback state from infoDelivery.info.playerState when needed, and retry late handshakes so playback state transitions are deterministic
Video YouTube: Forward pauseVideo while the player is in the BUFFERING state (not only PLAYING) so a fast scroll-out cancels a play that has been accepted but has not yet transitioned to playing
Video iframe bridge: Match inbound postMessage origins against an explicit per-provider hostname allowlist (YouTube, Vimeo) instead of strict equality, while still requiring the inbound scheme and port to match the iframe, so replies routed through WordPress Playground service workers or subdomain variants reach the bridge without weakening origin filtering
Tests: Mock the YouTube iframe via Playwright page.route() so CI tests verify our bridge end-to-end without depending on YouTube's media pipeline, which aborts on GitHub Actions Ubuntu runners and produced deterministic --playing state-class timeouts despite the bridge transport working correctly
Internal: Align video overlay E2E expectations with user-pause state semantics and keep lazy-viewport iframe attribute assertions below the fold
Add website module presets for choosing a site distribution from the Module Manager, including Base, Support, Standard, Development, and Demo presets.
Add Module Manager preset controls with active preset display, preset comparison details, preset membership filtering, and an option to keep currently enabled modules when applying a preset.
Add REST API endpoints for reading and applying the selected website preset, including per-module preset membership and selected-preset status in module responses.
Add an admin notice that prompts administrators to choose a website preset when none is selected.
Changed
Replace module auto_enable metadata with preset membership metadata across module definitions, generated module references, and developer documentation.
Update first-run module discovery to enable modules from the selected preset, preserve required infrastructure and dependencies, and track newly discovered modules by preset.
Separate preset membership chips from module taxonomy metadata on Module Manager cards.
Internal: Resolve 46 ESLint warnings across block edit components and admin UI — memoised JSX props with useCallback/useMemo, extracted sub-components for handlers created inside .map(), and silenced intentional sequential-await patterns in e2e test helpers
Internal: Add Airfleet Nexus Tooling (SSH agent) and Airfleet Nexus + WordPress (SSH agent) dev container variants with explicit SSH_AUTH_SOCK bind-mount for developers using 1Password or other non-standard SSH agents
video-youtube: New YouTube Video block with URL input, lazy loading, autoplay/autopause, poster, and iframe postMessage bridge.
video-vimeo: New Vimeo Video block with lazy loading, autoplay/autopause, poster via WP oEmbed proxy, and iframe postMessage bridge.
video-wistia: New Wistia Video block using Aurora Player (<wistia-player> Web Component) with native DOM events, lazy loading, and auto-poster via oEmbed.
allow-wistia-oembed: New module that registers Wistia as an oEmbed provider for auto-poster thumbnail fetching.
Poster image support for all 4 video blocks with auto-poster for YouTube, Vimeo, and Wistia. Video File supports custom upload only.
Shared video architecture in src/shared/scripts/video/ reused across all providers — media controls, iframe bridge, overlay, playback state, unmute indicator, and DOM events (afnx-video-*, afnx-media-*).
Filterable play button and unmute button icons and labels via PHP filters.
Changed
video-file: Refactored to use shared video modules. Editor uses MediaPlaceholder and MediaReplaceFlow. Settings labels updated for clarity.
allow-lottie: Tier changed to enhancement.
block-lottie: Live animation preview in editor via sandbox iframe. Simplified poster handling to match video blocks. Poster panel moved after Behavior panel.
disable-data-uninstalling: New module allowing site administrators to prevent plugin data from being deleted when the plugin is uninstalled or when switching between plugin preview versions in the Airfleet plugin manager. Module state is checked directly in the uninstall hook via ModuleManager to ensure data protection works correctly outside the normal plugin lifecycle.
Internal: Store unreleased changelog entries as separate changelog/unreleased/*.md fragments and consolidate them into CHANGELOG.md during plugin release via @airfleet/wordpress-dev.
Internal: Update @airfleet/wordpress-dev to 4.11.1.
Added changelog-fragments command for validating and consolidating changelog/unreleased/*.md files into CHANGELOG.md.
Changed
changelog-add --check now accepts either CHANGELOG.md Unreleased entries or valid changelog/unreleased/*.md fragments, with --skip-changelog-fragments available when fragments should be ignored.
version-set now includes changelog fragments when auto-determining and checking .airfleet-release, with --skip-changelog-fragments to use only CHANGELOG.md entries.
release github, release npm, release plugin, and release theme now consolidate changelog fragments before Keep a Changelog updates CHANGELOG.md, with --skip-changelog-fragments to bypass consolidation.
Adds a new Accordion module and block with auto-close, icon system, and custom events, alongside a CSS color sanitization utility and documentation updates.
Updated docs docs/guide/modules.md and docs/blocks/background-image.md.
Added
block-registration - added method to unregister blocks that depend on modules, when the modules are not active.
accordion: New Accordion module — registers and manages the lifecycle of the accordion block. Enabled by default; disabling this module will unregister the accordion block and its related child blocks (accordion-item, accordion-item-heading, accordion-item-content).
block-accordion: New Accordion block (airfleet-nexus/accordion) — a container block for collapsible content items with autoClose behavior and showIcon context. Toolbar "Add item" button for faster item creation. CSS modifier classes (.afnb-accordion--has-icon, .afnb-accordion--no-icon, .afnb-accordion--icon-left, .afnb-accordion--icon-right) for icon-aware heading padding. Tab color controls (Active/Inactive Background and Text) set via inline style attribute on the wrapper, cascading to all tabs via CSS custom properties. Auto-switch option with timed item cycling, configurable interval, manual click reset, viewport-aware pausing, and optional progress bar animation.
block-accordion: Extensible icon system featuring SVG path-based animations (plus-minus and arrows), a filterable PHP configuration, and a server-side localized icon picker in the editor.
block-accordion: Custom DOM events dispatched on the wrapper: afnx-accordion-load, afnx-accordion-open, and afnx-accordion-close.
block-accordion: Vanilla JS frontend behavior (view.js) — handles expand/collapse toggling, autoClose logic, and deep linking via URL hash. No external dependencies or Interactivity API.
block-accordion: Enhanced editor experience mimicking the native accordion block.
block-accordion-item: New Accordion Item block (airfleet-nexus/accordion-item) — a wrapper for heading and content, supporting openByDefault for initial state control.
block-accordion-item-heading: New Accordion Item Heading block (airfleet-nexus/accordion-item-heading) — locked inner block for accordion-item. Wraps a core/heading and a toggle button HTML element. The heading's native HTML anchor field enables URL deep-linking to that item.
block-accordion-item-content: New Accordion Item Content block (airfleet-nexus/accordion-item-content) — locked inner block for accordion-item. Provides the collapsible panel body with CSS grid-based transitions.
SanitizeCssColor: New utility for sanitizing CSS color values (hex, rgb/rgba, hsl/hsla, oklch, oklab, color, var, and CSS keywords). Includes comprehensive PHPUnit test suite covering all WordPress-produced color formats and injection attack prevention.
Prevented open_basedir warnings (is_readable(): open_basedir restriction in effect. File(~/.aws/config) is not within the allowed path(s)) when initializing the S3 client on hosts that restrict access to the home directory (e.g. Kinsta). The AWS SDK is now configured with use_aws_shared_config_files => false, so it no longer probes ~/.aws/config and ~/.aws/credentials since credentials are already supplied explicitly.
Suppress spurious invalid_grant: Malformed auth code errors caused by duplicate OAuth callback requests (browser back/forward navigation, link prefetch, and bot replay) re-redeeming a single-use Google authorization code. The callback handler now guards against duplicate processing per state using an atomic object-cache lock with a transient fallback, redirecting already-authenticated users instead of re-exchanging the code.
Docs: move all auto-generated documentation (docs/api/, docs/release-notes.md) to docs/generated/ and add it to .gitignore to prevent merge conflicts across branches.
Slack digest release lists: Weekly Slack digests now list only the latest release per repository and show the total release count across the number of repos, matching the monthly digest format. Both weekly and monthly Slack digests also drop the extra generated-from-summary footer line.
Worker observability: Enabled Cloudflare Worker observability in worker/wrangler.toml and added explicit release-processing/build-hook logs so webhook deliveries, summary generation, and Pages build-trigger attempts can be traced in Cloudflare Workers logs.
Changed
Slack digest workflows: Moved the weekly and monthly Slack schedules off the top of the hour, added shared period-target resolution, and updated both workflows to seed the required closed-period summary before sending to Slack.
Summary and deploy docs: Documented the production site URL, clarified the daily Worker cron and rollover rebuild behavior, and noted the optional PAGES_BUILD_HOOK_URL GitHub Actions secret used to refresh the static site after Slack-triggered summary backfills.
Worker scheduled rebuilds: The daily Worker cron now triggers a Pages rebuild when the featured closed week or month rolls over, even if the relevant summary already exists and no summary content changed.
Fixed
Slack summary availability: Weekly and monthly Slack digests now ensure the required summary exists in KV before posting, so scheduled sends no longer fail when the daily Worker cron has not backfilled that period yet.
Static weekly/monthly freshness: Weekly and monthly landing pages no longer stay pinned to the previous closed period until another release arrives; the Worker now forces a rebuild after week/month rollover so the featured static pages stay aligned with current closed periods.