Skip to content

feat: modal & ai actions#2860

Open
Soare-Robert-Daniel wants to merge 23 commits into
developmentfrom
feat/content-ai
Open

feat: modal & ai actions#2860
Soare-Robert-Daniel wants to merge 23 commits into
developmentfrom
feat/content-ai

Conversation

@Soare-Robert-Daniel

@Soare-Robert-Daniel Soare-Robert-Daniel commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Closes https://github.com/Codeinwp/otter-internals/issues/280

Summary

  • Display the generated results in the modal
  • Add custom AI toolbar actions
  • Add magic tags

Screenshots

CleanShot 2026-06-11 at 17 05 31@2x CleanShot 2026-06-11 at 17 19 03@2x

Test instructions


Checklist before the final review

  • Included E2E or unit tests for the changes in this PR.
  • Visual elements are not affected by independent changes.
  • It is at least compatible with the minimum WordPress version.
  • It loads additional script in frontend only if it is required.
  • Does not impact the Core Web Vitals.
  • In case of deprecation, old blocks are safely migrated.
  • It is usable in Widgets and FSE.
  • Copy/Paste is working if the attributes are modified.
  • PR is following the best practices

@Soare-Robert-Daniel Soare-Robert-Daniel self-assigned this Jun 11, 2026
@pirate-bot pirate-bot added pr-checklist-incomplete The Pull Request checklist is incomplete. (automatic label) pr-checklist-complete The Pull Request checklist is complete. (automatic label) and removed pr-checklist-incomplete The Pull Request checklist is incomplete. (automatic label) labels Jun 11, 2026
generatedHtml: string,
sourceBlocks: BlockProps<unknown>[]
) => {
const plainText = generatedHtml.replace( /<[^>]+>/g, '' ).trim();
@@ -0,0 +1,64 @@
const stripTags = ( html ) => html.replace( /<[^>]+>/g, '' ).trim();
@pirate-bot

pirate-bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Bundle Size Diff

Package Old Size New Size Diff
Animations 178.27 KB 178.27 KB 0 B (0.00%)
Blocks 1.52 MB 1.57 MB 57.37 KB (3.69%)
CSS 7.87 KB 7.87 KB 0 B (0.00%)
Dashboard 111.06 KB 175.39 KB 64.33 KB (57.92%)
Onboarding 68.14 KB 68.14 KB 0 B (0.00%)
Export Import 4.7 KB 4.7 KB 0 B (0.00%)
Pro 328.43 KB 329.59 KB 1.16 KB (0.35%)

@pirate-bot

pirate-bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Plugin build for 17705d5 is ready 🛎️!

@pirate-bot

pirate-bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

E2E Tests

Playwright Test Status: See serial and parallel matrix jobs

Performance Results serverResponse: {"q25":493.3,"q50":505.55,"q75":521.5,"cnt":10}, firstPaint: {"q25":557.8,"q50":700.3,"q75":750.3,"cnt":10}, domContentLoaded: {"q25":3629.3,"q50":3653.85,"q75":3663.1,"cnt":10}, loaded: {"q25":3631.4,"q50":3656.2,"q75":3665.1,"cnt":10}, firstContentfulPaint: {"q25":4215.3,"q50":4227.4,"q75":4260.5,"cnt":10}, firstBlock: {"q25":14953.7,"q50":14984.35,"q75":15092.1,"cnt":10}, type: {"q25":27.6,"q50":28.03,"q75":31.08,"cnt":10}, typeWithoutInspector: {"q25":25.11,"q50":26.4,"q75":28.43,"cnt":10}, typeWithTopToolbar: {"q25":31.93,"q50":32.96,"q75":47.45,"cnt":10}, typeContainer: {"q25":14.64,"q50":15.48,"q75":15.82,"cnt":10}, focus: {"q25":115.27,"q50":117.62,"q75":123.01,"cnt":10}, inserterOpen: {"q25":40.25,"q50":40.86,"q75":42.61,"cnt":10}, inserterSearch: {"q25":14.75,"q50":15.05,"q75":16.04,"cnt":10}, inserterHover: {"q25":5.71,"q50":5.96,"q75":6.27,"cnt":20}, loadPatterns: {"q25":1692.31,"q50":1714.15,"q75":1734.41,"cnt":10}, listViewOpen: {"q25":235.12,"q50":240.21,"q75":251.19,"cnt":10}

Soare-Robert-Daniel and others added 6 commits June 16, 2026 18:44
Resolve conflicts integrating the multi-backend AI work (WP AI Client /
Connectors) with the content-ai toolbar/modal refactor:

- AI settings stay in the dedicated AI dashboard tab; ported the
  multi-backend "WordPress AI"/Connectors panel, provider-status notices,
  and legacy OpenAI-key fallback into AI.js.
- Editor toolbar gate now uses isAIBackendConfigured().
- Adapt prompt response consumers (modal, content-generator, block
  generation) to the normalized {ok, content, usedTokens} contract; drop
  the deleted ChatResponse type.
- Merge e2e specs/config and keep both server-side AI and captcha HTTP
  stubs; dashboard specs cover the multi-backend states on the AI tab.
- Guard rest_sanitize_boolean inputs in toolbar-action sanitization.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
# Conflicts:
#	packages/e2e-tests/mu-plugins/otter-e2e-bootstrap.php
The content-generator refactor stopped passing the `resultHistory`
attribute into PromptPlaceholder, so the component's restore-last-prompt
effect bailed out (`! props.resultHistory`) and the prompt field came up
empty for blocks that carry history. Re-wire the prop.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ve across reset

Two pre-existing e2e failures on this branch:

- The AI mocks returned HTML, but the new `generateBlocksFromTask`
  pipeline JSON.parses the reply expecting `{ rationale, roots }`, so no
  blocks were produced. Make both the legacy /chat/completions and the WP
  AI Client /responses mocks emit phase-aware block-generation JSON.

- `reset()` deleted PRO_LICENSE_OPTION, which global-setup activates only
  once for the whole run. ai-toolbar's reset() therefore deactivated Pro
  for every later spec, failing the Pro-gated Form tests. Restore the stub
  license in reset() instead of deleting it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the separate "Replace" and "Insert below" buttons in the AI
content generator with one primary "Done" button that applies the
generated blocks in place (the former Replace behavior). Drop the
now-unused insert-below handler/import and update the e2e specs to the
new label.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The content generator labelled every generation request as an
"OpenAI request (gpt-5-mini)", but the backend is resolved server-side:
on the WP AI Client connector path the model pin is dropped and the
configured provider/model is used. Make the debug labels backend-agnostic.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Soare-Robert-Daniel and others added 5 commits June 17, 2026 15:16
…orm submit

Add two instructions to the block-generation prompt:
- system prompt: for design requests, produce a clean, well-structured,
  visually appealing layout.
- structure prompt: when using a form block, don't add a separate submit
  button — the Otter Form already renders its own.

Also document the missing @param tags on buildStructureCatalog,
buildAttributeSchema and validateStructure, which were failing
`npm run lint` (jsdoc/require-param).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Drop the aiDebug step logger that was on by default during the beta:
delete debug.ts, its call sites in the block-generation pipeline and the
content generator, and the test mock that silenced it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The block-generation rewrite hardcoded the "content" placeholder and
title, so inserting the block with promptID 'form' no longer showed
"Start describing what form you need…". Restore a small preset lookup
keyed by promptID (form vs textTransformation) while keeping the single
Done action. Fixes the ai-block-wp-client form e2e test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The content generator rewrite routed every prompt through the block
generation pipeline, so promptID 'form' stopped producing Form field
blocks. Send the form prompt through the embedded-prompt path again
(onPreview → parseFormPromptResponseToBlocks → Form block), keeping the
new pipeline for content prompts. Fixes the ai-block-wp-client form e2e.

Also poll for the inserted item in the accordion "add new item" e2e
instead of snapshotting immediately after the click, which raced the
async store update and flaked under parallel load.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
In the WP 7.0 iframe editor, a button rendered through InnerBlocks'
renderAppender prop does not fire its React onClick when clicked, so
"Add Accordion Item" did nothing (the e2e add-item test caught this).
Render the appender as block content with renderAppender={false},
matching the working Tabs block, so the click reaches the handler.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Soare-Robert-Daniel Soare-Robert-Daniel linked an issue Jun 18, 2026 that may be closed by this pull request
Soare-Robert-Daniel and others added 3 commits June 18, 2026 17:00
The toolbar filter only returned actions whose availability matched
'richtext' when every selected block was rich text, which dropped
"Any block" actions from text. Allow 'any' actions everywhere and
restrict 'richtext' actions to all-rich-text selections, which also
fixes mixed selections showing no actions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Update the attribute-fill prompt to set tasteful, coherent colors when
a block exposes color attributes, keeping text readable, so generated
layouts look polished instead of bland.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase 1 now produces a plan (mission, design direction, and a section
outline with per-section notes) instead of a bare slug tree. The mission,
design direction and each section's notes are trickled into every per-root
attribute call so the page builds toward one coherent vision.

generateBlocksFromTask gains onPlanReady/onRootComplete callbacks; the AI
block renders the plan (mission, palette, outline checklist) and inserts
each section as it completes, with a staleness guard and undo-safe previews.
Verbose design chips stay internal (prompt context only).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Soare-Robert-Daniel and others added 5 commits June 18, 2026 18:17
The WP AI Client sends provider requests with a 30s timeout, which
reasoning models on OpenAI's /v1/responses endpoint regularly exceed
(cURL error 28 -> 502). Raise the timeout for the generation request to
120s via http_request_args (filterable through otter_ai_request_timeout),
and retry transient backend failures (timeouts, 5xx, network errors) with
exponential backoff via withPromptRetry, leaving permanent errors alone.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Route AI block + toolbar generation through the AIContentModal: a
version-based flow with a sparkle prompt row, live BlockPreview at
Desktop/Mobile widths, an animated phase-aware generating state
(planning → per-section), and Done/Regenerate with token + version nav.

- Fix block generation to use the strict-JSON endpoint instead of the
  HTML-forcing textTransformation template, so the plan parses.
- Move scope (Section/Full page) + starter prompt chips into the AI block
  (the first interaction); the modal auto-generates on open.
- Lock the modal while generating so click-outside/Esc can't lose work.
- Fixed-height modal with header/scroll/footer layout: the footer stays
  pinned at the bottom and the content scrolls between header and footer.
- Stabilize useSelect in the AI toolbar HOC and useResponsiveAttributes
  (return store-memoized/primitive values, derive the rest with useMemo)
  to stop the "useSelect returns different values" warning storm that the
  modal's BlockPreview surfaced across every block.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
# Conflicts:
#	src/blocks/test/unit/utils.test.ts
…on + e2e

- Modal: redesign the create/section experience (branded header, framed
  preview stage, status pill, bottom bar that generates then refines, footer
  with version stepper + Discard/Insert section).
- Generation: feed the resolved theme palette into both prompt phases so
  generated colors reference real theme slugs instead of invented ones.
- Insertion: clone generated blocks with fresh clientIds before replaceBlocks
  so they no longer share identity with the live BlockPreview tree.
- e2e: add ai-block-section-insert spec reproducing the insertion path, plus a
  setAtomicWindBlocks fixture (and whitelist entry) to register atomic-wind/*.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- e2e: drive the real AI toolbar → Generate → Insert section flow (replaces the
  selected block while the modal + live BlockPreview unmount), the one path the
  store-only cases can't cover. Self-skips where Atomic Wind can't register.
- bootstrap: emit a real atomic-wind/* features tree from the OpenAI stub when
  the Atomic Wind option is on, and fix block-generation phase detection (the
  plan-prompt wording no longer matched is_block_generation_request).

Confirms the cloneBlock insertion fix: generating + inserting an Atomic Wind
section on WP 7.0 (with the 404-ing icon slugs) no longer crashes the editor.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr-checklist-complete The Pull Request checklist is complete. (automatic label)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Custom AI action

3 participants