Skip to content

feat: add Markdown rendering mechanism for AI crawlers#54

Open
matipojo wants to merge 550 commits into
main2from
feat/markdown-rendering
Open

feat: add Markdown rendering mechanism for AI crawlers#54
matipojo wants to merge 550 commits into
main2from
feat/markdown-rendering

Conversation

@matipojo

Copy link
Copy Markdown
Owner

Add a new module that serves Elementor page content as Markdown when crawlers send Accept: text/markdown header or ?format=markdown param.

  • Register markdown_rendering experiment (default inactive, alpha)
  • Intercept requests via template_redirect with Accept header detection
  • Core MarkdownRenderer tree walker with YAML frontmatter and --- separators
  • Add render_markdown() to Element_Base with recursive default
  • Widget_Base fallback via strip_tags for unknown widgets
  • Override render_markdown() in all classic widgets (29 widgets)
  • Override render_markdown() in atomic widgets (8 widgets)
  • HTML-to-Markdown converter for rich text content
  • Post meta caching with configurable TTL
  • Rate limiting and security hardening
  • Editor top bar View as Markdown menu item
  • Filter hooks for Pro/third-party extensibility
  • Comprehensive test suite

PR Checklist

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Documentation content changes
  • Other... Please describe:

Summary

This PR can be summarized in the following changelog entry:

Description

An explanation of what is done in this PR

Test instructions

This PR can be tested by following these steps:

Quality assurance

  • I have tested this code to the best of my abilities
  • I have added unittests to verify the code works as intended
  • Docs have been added / updated (for bug fixes / features)

Fixes #

@matipojo matipojo force-pushed the feat/markdown-rendering branch 2 times, most recently from 2589d2a to eda9991 Compare February 21, 2026 19:18
Ntnelbaba and others added 28 commits March 23, 2026 21:01
…entor#35231)

## Summary
- Add `Tooltip` wrapper to value component with `-webkit-line-clamp` for
proper multi-line ellipsis truncation
- Fix consistent spacing/padding across all indication list items (`pt:
1.5`, `pb: 1.5`, `pl: 2` instead of uneven `pl: 3`, `pb: 2`)
- Simplify `box-shadow-transformer` and `repeater-to-items-transformer`
to return flat content instead of nested `Stack` wrappers, eliminating
extra spacing artifacts
- Fix `array-transformer` to handle both single and array `ReactNode`
values
- Fix Infotip popover positioning by switching from
`slotProps.popper.modifiers` to `PopperProps.modifiers` so the offset
modifier reliably reaches the custom `StyledPopper` component

## Test plan
- [ ] Open the style origin popover for a repeater property (e.g.
box-shadow, background) and verify text is truncated with ellipsis when
it overflows
- [ ] Hover over a truncated value to confirm the full value is shown in
a tooltip
- [ ] Verify all indication items have consistent vertical and
horizontal spacing
- [ ] Verify the popover arrow aligns correctly with the trigger
indicator (no horizontal shift)
- [ ] Check RTL layout for correct popover positioning

## Jira
[ED-21321](https://elementor.atlassian.net/browse/ED-21321)

[ED-21321]:
https://elementor.atlassian.net/browse/ED-21321?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
<!--start_gitstream_placeholder-->
### ✨ PR Description
Purpose: Fix styling and layout issues in Repeater Indications Popovers
to improve text truncation, spacing consistency, and tooltip
functionality in the editor panel.

Main changes:
- Added Tooltip wrapper with text truncation using WebKit line clamp in
ValueComponent for better overflow handling
- Adjusted popover spacing and padding, replacing slotProps with
PopperProps for proper offset positioning
- Simplified array and repeater transformers by removing Stack
components and using inline rendering for cleaner display

_Generated by LinearB AI and added by gitStream._
<sub>AI-generated content may contain inaccuracies. Please verify before
using.
💡 **Tip:** You can customize your AI Description using **Guidelines**
[Learn
how](https://docs.gitstream.cm/automation-actions/#describe-changes)</sub>
<!--end_gitstream_placeholder-->

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: ElementorBot <48412871+elementorbot@users.noreply.github.com>
…#35236)

## PR Checklist
<!-- 
Please check if your PR fulfills the following requirements:
**Filling out the template is required.** Any pull request that does not
include enough information to be reviewed in a timely manner may be
closed at the maintainers' discretion.
 -->
- [ ] The commit message follows our guidelines:
https://github.com/elementor/elementor/blob/master/.github/CONTRIBUTING.md


## PR Type
What kind of change does this PR introduce?
<!-- Please check the one that applies to this PR using "x" with no
spaces eg: [x]. -->
- [ ] Bugfix
- [ ] Feature
- [ ] Code style update (formatting, local variables)
- [ ] Refactoring (no functional changes, no api changes)
- [ ] Build related changes
- [ ] CI related changes
- [ ] Documentation content changes
- [ ] Other... Please describe:

## Summary

This PR can be summarized in the following changelog entry:

*

## Description
An explanation of what is done in this PR

*

## Test instructions
This PR can be tested by following these steps:

*

## Quality assurance

- [ ] I have tested this code to the best of my abilities
- [ ] I have added unittests to verify the code works as intended
- [ ] Docs have been added / updated (for bug fixes / features)

Fixes #
…23474] (elementor#35257)

## Summary
- Fixed a race condition where `e_opt_in_v4` and `e_atomic_elements`
experiments were not activated on the very first page load of a fresh
Elementor installation
- Root cause: `Modules_Manager` (which registers experiments) is
instantiated before `Core\Upgrade\Manager` (which writes
`installs_history`) in `plugin.php::init_components()`. On first load,
`install_compare()` found empty history and returned `false`, leaving
experiment defaults as `STATE_INACTIVE`
- Fix: when `installs_history` is empty, `install_compare()` now falls
back to comparing `ELEMENTOR_VERSION` against the minimum — the current
version IS the effective first-install version on that first request
- Existing sites and upgrades are completely unaffected (their history
is always populated before experiments are registered)
- Added two test cases to `test-manager.php` covering the empty-history
scenarios

## Test plan
- [ ] Fresh install: verify `e_opt_in_v4` and `e_atomic_elements`
experiments are active on the very first page load (before any
subsequent requests)
- [ ] Simulate by clearing the `elementor_install_history` DB option and
reloading the admin — experiments should be active
- [ ] Verify existing sites (non-empty history) are unaffected —
experiments activate/deactivate as before
- [ ] Run PHPUnit: `vendor/bin/phpunit
tests/phpunit/elementor/core/experiments/test-manager.php
--filter="test_is_feature_active__new_site"` — all 6 data-provider cases
should pass, including the 2 new empty-history cases

## Jira

[https://elementor.atlassian.net/browse/ED-23474](https://elementor.atlassian.net/browse/ED-23474)
<!--start_gitstream_placeholder-->
### ✨ PR Description
Purpose: Fix new-site experiments activation by properly handling first
installation requests before upgrade manager writes installation
history.

Main changes:
- Modified install_compare() to use current plugin version when
installation history is empty on first request
- Added fallback logic comparing ELEMENTOR_VERSION against minimum
version for fresh installations before history initialization
- Added test cases validating experiment activation behavior with empty
installation history scenarios

_Generated by LinearB AI and added by gitStream._
<sub>AI-generated content may contain inaccuracies. Please verify before
using.
💡 **Tip:** You can customize your AI Description using **Guidelines**
[Learn
how](https://docs.gitstream.cm/automation-actions/#describe-changes)</sub>
<!--end_gitstream_placeholder-->

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: ElementorBot <48412871+elementorbot@users.noreply.github.com>
## PR Checklist
<!-- 
Please check if your PR fulfills the following requirements:
**Filling out the template is required.** Any pull request that does not
include enough information to be reviewed in a timely manner may be
closed at the maintainers' discretion.
 -->
- [ ] The commit message follows our guidelines:
https://github.com/elementor/elementor/blob/master/.github/CONTRIBUTING.md


## PR Type
What kind of change does this PR introduce?
<!-- Please check the one that applies to this PR using "x" with no
spaces eg: [x]. -->
- [ ] Bugfix
- [ ] Feature
- [ ] Code style update (formatting, local variables)
- [ ] Refactoring (no functional changes, no api changes)
- [ ] Build related changes
- [ ] CI related changes
- [ ] Documentation content changes
- [ ] Other... Please describe:

## Summary

This PR can be summarized in the following changelog entry:

*

## Description
An explanation of what is done in this PR

*

## Test instructions
This PR can be tested by following these steps:

*

## Quality assurance

- [ ] I have tested this code to the best of my abilities
- [ ] I have added unittests to verify the code works as intended
- [ ] Docs have been added / updated (for bug fixes / features)

Fixes #

<!--start_gitstream_placeholder-->
### ✨ PR Description
Purpose: Fix unit selector highlighting logic in size control component
to correctly display active state based on value presence and unit type.

Main changes:
- Renamed isActive prop to isUnitHighlighted and moved highlighting
logic from resolve-bound-prop-value to size-field component
- Removed hasSizeValue utility function and replaced with inline
shouldHighlightUnit function checking size or auto unit
- Updated unit selector tests to use isUnitHighlighted prop and added
comprehensive test cases for highlighting behavior

_Generated by LinearB AI and added by gitStream._
<sub>AI-generated content may contain inaccuracies. Please verify before
using.
💡 **Tip:** You can customize your AI Description using **Guidelines**
[Learn
how](https://docs.gitstream.cm/automation-actions/#describe-changes)</sub>
<!--end_gitstream_placeholder-->

---------

Co-authored-by: ElementorBot <48412871+elementorbot@users.noreply.github.com>
…ic nested elements [ED-22825] (elementor#35223)

## Summary

Undo/redo operations on atomic nested elements (e.g. Tabs widget
children) fail because the undoable closures hold stale `V1Element`
references. Atomic elements render children asynchronously via
client-side Twig templates, so the Marionette view tree may not contain
the recreated child views when an undo/redo closure executes.

This PR adds a **fallback resolution strategy** to all undoable
operations and legacy commands:

- **`resolveContainer()`** — tries `container.lookup()` first, then
falls back to `elementor.getContainer(id)` with DOM-connection
validation
- **Model-tree fallback** — when both view-based lookups fail,
operations manipulate the Backbone model tree directly using
`addModelToParent` / `removeModelFromParent` with `{ silent: true }`.
When the parent is mid-render, its `_renderChildren` naturally picks up
the silently-added models; when the parent has a live view, the two-step
resolution in the legacy commands prefers the view-based path
- **`findModelById`** — new utility in `document/component.js` to
traverse the model tree independently of the view tree

### Why `rerenderAtomicAncestor` was removed

The initial implementation included a `rerenderAtomicAncestor` helper
that would force-render the nearest atomic widget after a silent
model-tree change. Testing showed this is unnecessary:

- **Legacy commands** use a two-step resolution: first try
`findContainerById` to get a fresh container, prefer the view-based
`addElement` path when a live view exists, and only fall through to the
silent model-tree path when the parent is genuinely mid-render — in
which case `_renderChildren` picks up the model automatically
- **Undoable actions** in the turbo-repo follow the same pattern via
`resolveContainer` + `addModelToParent`/`removeModelFromParent`

## Changes

| File | Change |
|------|--------|
| `document/component.js` | Added `findModelById` utility for model-tree
traversal |
| `elements/commands/create.js` | Two-step container resolution with
`findContainerById` fallback, `addViaModelTree` for viewless containers
|
| `elements/commands/delete.js` | Same two-step resolution pattern,
`deleteViaModelTree` for viewless containers |
| `resolve-element.ts` | New shared utilities: `resolveContainer`,
`findModelInDocTree`, `addModelToParent`, `removeModelFromParent` |
| `create-elements.ts` | Uses `resolveContainer` + model-tree fallback
in undo/redo |
| `remove-elements.ts` | Uses `resolveContainer` + model-tree fallback
in undo/redo |
| `duplicate-elements.ts` | Uses `resolveContainer` + model-tree
fallback in undo/redo |
| `move-elements.ts` | Uses `resolveContainer` in undo/redo |
| `types.ts` | Added `BackboneModel`, `BackboneCollection` types and
extended `ExtendedWindow` |
| `create-elements.test.ts` | Tests for model-tree fallback on undo/redo
|
| `remove-elements.test.ts` | Updated test expectations for
`resolveContainer` behavior |

## Test plan

- [x] Unit tests pass for `resolve-element.ts`,
`create-elements.test.ts`, `remove-elements.test.ts`
- [x] Manual QA: Add Tabs widget → add tab → add element to tab → undo
all → redo all (all cycles work, fourth tab renders correctly)
- [x] Manual QA: Delete tab child → undo → redo (all cycles work)
- [x] Manual QA: Multi-step undo all → redo all (widget removal +
re-creation works)
- [x] No new console errors from the implemented changes
<!--start_gitstream_placeholder-->
### ✨ PR Description
Purpose: Fix stale element reference errors in undo/redo operations for
nested atomic elements by implementing fallback model-tree manipulation
when views are unavailable.

Main changes:
- Added resolveContainer utility with DOM connectivity check and
fallback model lookup to handle async-rendered nested elements
- Implemented direct model-tree manipulation methods (findModelById,
addModelToParent, removeModelFromParent) as fallback for detached views
- Updated all undoable operations (create, delete, duplicate, move,
remove) to use container resolution with model-tree fallback

_Generated by LinearB AI and added by gitStream._
<sub>AI-generated content may contain inaccuracies. Please verify before
using.
💡 **Tip:** You can customize your AI Description using **Guidelines**
[Learn
how](https://docs.gitstream.cm/automation-actions/#describe-changes)</sub>
<!--end_gitstream_placeholder-->

---------

Co-authored-by: ElementorBot <48412871+elementorbot@users.noreply.github.com>
…lementor#35265)

## PR Checklist
<!-- 
Please check if your PR fulfills the following requirements:
**Filling out the template is required.** Any pull request that does not
include enough information to be reviewed in a timely manner may be
closed at the maintainers' discretion.
 -->
- [ ] The commit message follows our guidelines:
https://github.com/elementor/elementor/blob/master/.github/CONTRIBUTING.md


## PR Type
What kind of change does this PR introduce?
<!-- Please check the one that applies to this PR using "x" with no
spaces eg: [x]. -->
- [ ] Bugfix
- [ ] Feature
- [ ] Code style update (formatting, local variables)
- [ ] Refactoring (no functional changes, no api changes)
- [ ] Build related changes
- [ ] CI related changes
- [ ] Documentation content changes
- [ ] Other... Please describe:

## Summary

This PR can be summarized in the following changelog entry:

*

## Description
An explanation of what is done in this PR

*

## Test instructions
This PR can be tested by following these steps:

*

## Quality assurance

- [ ] I have tested this code to the best of my abilities
- [ ] I have added unittests to verify the code works as intended
- [ ] Docs have been added / updated (for bug fixes / features)

Fixes #
…e [ED-23473] (elementor#35269)

## Summary
- Export `getComponentDocumentData` from `editor-components` public
index
- This export is needed by the components MCP tool to use async domain +
resources instead of the synchronous list-components approach

## Test plan
- [ ] Verify `getComponentDocumentData` is importable from
`@elementor/editor-components`
- [ ] Verify the components MCP tool works correctly with the async
domain/resource implementation

## Jira
[ED-23473](https://elementor.atlassian.net/browse/ED-23473)

[ED-23473]:
https://elementor.atlassian.net/browse/ED-23473?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
<!--start_gitstream_placeholder-->
### ✨ PR Description
Purpose: Export getComponentDocumentData utility function to enable
async MCP resource integration for component document data retrieval in
external modules.

Main changes:
- Added public export of getComponentDocumentData from
component-document-data utils module to package API surface
- Enables external async MCP resource consumers to access component
document metadata without internal imports

_Generated by LinearB AI and added by gitStream._
<sub>AI-generated content may contain inaccuracies. Please verify before
using.
💡 **Tip:** You can customize your AI Description using **Guidelines**
[Learn
how](https://docs.gitstream.cm/automation-actions/#describe-changes)</sub>
<!--end_gitstream_placeholder-->

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
## PR Checklist
<!-- 
Please check if your PR fulfills the following requirements:
**Filling out the template is required.** Any pull request that does not
include enough information to be reviewed in a timely manner may be
closed at the maintainers' discretion.
 -->
- [ ] The commit message follows our guidelines:
https://github.com/elementor/elementor/blob/master/.github/CONTRIBUTING.md


## PR Type
What kind of change does this PR introduce?
<!-- Please check the one that applies to this PR using "x" with no
spaces eg: [x]. -->
- [ ] Bugfix
- [ ] Feature
- [ ] Code style update (formatting, local variables)
- [ ] Refactoring (no functional changes, no api changes)
- [ ] Build related changes
- [ ] CI related changes
- [ ] Documentation content changes
- [ ] Other... Please describe:

## Summary

This PR can be summarized in the following changelog entry:

*

## Description
An explanation of what is done in this PR

*

## Test instructions
This PR can be tested by following these steps:

*

## Quality assurance

- [ ] I have tested this code to the best of my abilities
- [ ] I have added unittests to verify the code works as intended
- [ ] Docs have been added / updated (for bug fixes / features)

Fixes #

<!--start_gitstream_placeholder-->
### ✨ PR Description
Purpose: Update Playwright screenshot tests to support WordPress 7.0 by
implementing dynamic screenshot naming based on WordPress major version
for nightly builds.

Main changes:
- Added getWpVersionedScreenshotName utility function to append
version-specific suffix to screenshot filenames when WP_MAJOR_VERSION is
set
- Enhanced GitHub workflow to update both WordPress instances, run
database migrations, and export major version as environment variable
- Simplified Block Inserter interaction logic to use aria-expanded
attribute check instead of complex visibility conditions

_Generated by LinearB AI and added by gitStream._
<sub>AI-generated content may contain inaccuracies. Please verify before
using.
💡 **Tip:** You can customize your AI Description using **Guidelines**
[Learn
how](https://docs.gitstream.cm/automation-actions/#describe-changes)</sub>
<!--end_gitstream_placeholder-->

---------

Co-authored-by: ElementorBot <48412871+elementorbot@users.noreply.github.com>
elementor#35263)

## PR Checklist
<!-- 
Please check if your PR fulfills the following requirements:
**Filling out the template is required.** Any pull request that does not
include enough information to be reviewed in a timely manner may be
closed at the maintainers' discretion.
 -->
- [ ] The commit message follows our guidelines:
https://github.com/elementor/elementor/blob/master/.github/CONTRIBUTING.md


## PR Type
What kind of change does this PR introduce?
<!-- Please check the one that applies to this PR using "x" with no
spaces eg: [x]. -->
- [ ] Bugfix
- [ ] Feature
- [ ] Code style update (formatting, local variables)
- [ ] Refactoring (no functional changes, no api changes)
- [ ] Build related changes
- [ ] CI related changes
- [ ] Documentation content changes
- [ ] Other... Please describe:

## Summary

This PR can be summarized in the following changelog entry:

*

## Description
An explanation of what is done in this PR

*

## Test instructions
This PR can be tested by following these steps:

*

## Quality assurance

- [ ] I have tested this code to the best of my abilities
- [ ] I have added unittests to verify the code works as intended
- [ ] Docs have been added / updated (for bug fixes / features)

Fixes #

<!--start_gitstream_placeholder-->
### ✨ PR Description
Purpose: Remove inline children display feature from the structure panel
navigator to simplify the UI and eliminate unused functionality for
formatted text elements.

Main changes:
- Deleted InlineChildren class and all references, removing
functionality that displayed nested formatted text nodes in navigator
- Updated hasChildren() method to exclude inline children check,
simplifying element hierarchy detection logic
- Modified test suite to verify inline children are no longer displayed
in navigator panel for formatted text widgets

_Generated by LinearB AI and added by gitStream._
<sub>AI-generated content may contain inaccuracies. Please verify before
using.
💡 **Tip:** You can customize your AI Description using **Guidelines**
[Learn
how](https://docs.gitstream.cm/automation-actions/#describe-changes)</sub>
<!--end_gitstream_placeholder-->

---------

Co-authored-by: ElementorBot <48412871+elementorbot@users.noreply.github.com>
## Summary
- Added exponential backoff retry to the `@elementor/http-client`
singleton Axios instance via a response interceptor — all 22+ call sites
across the codebase get retry automatically with no changes required
- Retries up to 3 times on network errors (no response), 5xx server
errors, and 429 Too Many Requests; does **not** retry non-idempotent
methods (POST, PATCH) to prevent duplicate operations on endpoints like
create/lock/archive
- Backoff delays: 1s → 2s → 4s with ±10% random jitter to avoid
thundering-herd under load
- Config object is never mutated — each retry receives a spread copy
with an incremented `__retryCount` field
- Added 17 unit tests covering initialization, success passthrough, all
retry/no-retry conditions, delay timing, immutability, and 429 handling

## Test plan
- [ ] Verify that GET requests against a temporarily unavailable
endpoint are retried up to 3 times before surfacing an error
- [ ] Verify that POST requests (e.g. saving a component, locking an
element) are **not** retried on 5xx — they should fail immediately
- [ ] Verify that 429 responses are retried with backoff
- [ ] Verify that 4xx errors (400, 401, 403, 404) fail immediately with
no retry
- [ ] Run unit tests: `cd packages && npx jest
packages/libs/http-client/src/__tests__/http.test.ts --no-coverage`
- [ ] Confirm no regressions in editor save/load flows

## Jira
[ED-23523](https://elementor.atlassian.net/browse/ED-23523)

[ED-23523]:
https://elementor.atlassian.net/browse/ED-23523?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
<!--start_gitstream_placeholder-->
### ✨ PR Description
Purpose: Add exponential backoff retry mechanism to HTTP client to
improve resilience against transient failures and rate-limiting errors.

Main changes:
- Implemented response interceptor with exponential backoff retry logic
for safe HTTP methods (GET, HEAD, OPTIONS, PUT, DELETE)
- Added automatic retry on 5xx server errors, 429 rate-limiting, and
network failures with 3 max retries and progressive timeout scaling
- Created comprehensive test suite covering retry behavior, timeout
scaling, method filtering, and edge cases

_Generated by LinearB AI and added by gitStream._
<sub>AI-generated content may contain inaccuracies. Please verify before
using.
💡 **Tip:** You can customize your AI Description using **Guidelines**
[Learn
how](https://docs.gitstream.cm/automation-actions/#describe-changes)</sub>
<!--end_gitstream_placeholder-->

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: ElementorBot <48412871+elementorbot@users.noreply.github.com>
…#35278)

<img width="373" height="179" alt="image"
src="https://github.com/user-attachments/assets/2f2c744e-e570-4ce6-96d6-ae8f4c8dc206"
/>

## Summary
- Remove `componentsProps` tooltip margin and `PopperProps` offset
modifier from `Infotip` in styles-inheritance-infotip
- Fixes popover/inheritance popover positioning so they appear above the
editor panel instead of over Angie
- Fixes the small triangle indicator
- Prevents popovers from stacking on top of each other

## Test plan
- [ ] Open Elementor editor with Angie active
- [ ] Trigger styles inheritance infotip/popover
- [ ] Verify popover appears above the editor panel, not over Angie
- [ ] Verify the triangle indicator renders correctly
- [ ] Verify multiple popovers don't stack on top of each other

## Jira
[ED-23465](https://elementor.atlassian.net/browse/ED-23465)

[ED-23465]:
https://elementor.atlassian.net/browse/ED-23465?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
<!--start_gitstream_placeholder-->
### ✨ PR Description
Purpose: Fix popover positioning logic in styles inheritance infotip to
correctly handle RTL layouts when Angie (AI assistant) is active.

Main changes:
- Removed custom popover offset calculation logic that was causing
incorrect positioning in RTL layouts
- Changed Infotip placement from "top" to "top-end" for consistent
anchor positioning across different UI states
- Eliminated unused triggerRef and sectionWidth props from
TooltipOrInfotip component to simplify implementation

_Generated by LinearB AI and added by gitStream._
<sub>AI-generated content may contain inaccuracies. Please verify before
using.
💡 **Tip:** You can customize your AI Description using **Guidelines**
[Learn
how](https://docs.gitstream.cm/automation-actions/#describe-changes)</sub>
<!--end_gitstream_placeholder-->

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
…activated [ED-23517] (elementor#35279)

## Summary
Fixes [ED-23517](https://elementor.atlassian.net/browse/ED-23517): when
Elementor Pro is deactivated, interactions saved with Pro-only triggers
(hover, click, scrollOn) were still executed on page load via the free
frontend `interactions.js` default path.

## Changes
- Gate `applyAnimation` so only free-frontend-supported triggers
(`load`, `scrollIn`, `scrollOut`) run on init.
- Export `isFreeFrontendSupportedTrigger` from `interactions-utils.js`
with documented trigger allowlist.

## Test plan
- [ ] With Pro off, page with saved hover/click/scrollOn interactions
does not run those animations on load.
- [ ] Load/scrollIn/scrollOut still behave as before.

Made with [Cursor](https://cursor.com)

[ED-23517]:
https://elementor.atlassian.net/browse/ED-23517?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
<!--start_gitstream_placeholder-->
### ✨ PR Description
Purpose: Fix pro-only interaction triggers (hover, click, scrollOn) from
executing on page load when pro features are deactivated in the free
frontend bundle.

Main changes:
- Added FREE_FRONTEND_SUPPORTED_TRIGGERS whitelist containing only load,
scrollIn, and scrollOut triggers for free tier
- Implemented isFreeFrontendSupportedTrigger validation function to
filter interaction triggers before execution
- Updated processElementInteractions to skip applying animations for
pro-only triggers in the free bundle

_Generated by LinearB AI and added by gitStream._
<sub>AI-generated content may contain inaccuracies. Please verify before
using.
💡 **Tip:** You can customize your AI Description using **Guidelines**
[Learn
how](https://docs.gitstream.cm/automation-actions/#describe-changes)</sub>
<!--end_gitstream_placeholder-->
…3463] (elementor#35252)

## PR Checklist
<!-- 
Please check if your PR fulfills the following requirements:
**Filling out the template is required.** Any pull request that does not
include enough information to be reviewed in a timely manner may be
closed at the maintainers' discretion.
 -->
- [ ] The commit message follows our guidelines:
https://github.com/elementor/elementor/blob/master/.github/CONTRIBUTING.md


## PR Type
What kind of change does this PR introduce?
<!-- Please check the one that applies to this PR using "x" with no
spaces eg: [x]. -->
- [ ] Bugfix
- [ ] Feature
- [ ] Code style update (formatting, local variables)
- [ ] Refactoring (no functional changes, no api changes)
- [ ] Build related changes
- [ ] CI related changes
- [ ] Documentation content changes
- [ ] Other... Please describe:

## Summary

This PR can be summarized in the following changelog entry:

*

## Description
An explanation of what is done in this PR

*

## Test instructions
This PR can be tested by following these steps:

*

## Quality assurance

- [ ] I have tested this code to the best of my abilities
- [ ] I have added unittests to verify the code works as intended
- [ ] Docs have been added / updated (for bug fixes / features)

Fixes #

<!--start_gitstream_placeholder-->
### ✨ PR Description
Purpose: Add scroll interaction tracking system to synchronize grid
overlay visualization with active scroll-based animation configurations
in the editor.

Main changes:
- Created scroll interaction event system with CustomEvent dispatching
to communicate active scroll parameters across components
- Added popover lifecycle callbacks to Repeater component enabling grid
overlay sync on interaction item open/close
- Integrated syncGridOverlay calls in InteractionDetails and
InteractionsList to update visual feedback when scroll settings change

_Generated by LinearB AI and added by gitStream._
<sub>AI-generated content may contain inaccuracies. Please verify before
using.
💡 **Tip:** You can customize your AI Description using **Guidelines**
[Learn
how](https://docs.gitstream.cm/automation-actions/#describe-changes)</sub>
<!--end_gitstream_placeholder-->

---------

Co-authored-by: Batsirai Muchareva <162136788+Batsirai-muchareva@users.noreply.github.com>
Co-authored-by: Netanel Baba <50736016+Ntnelbaba@users.noreply.github.com>
Co-authored-by: ElementorBot <48412871+elementorbot@users.noreply.github.com>
## PR Checklist
<!-- 
Please check if your PR fulfills the following requirements:
**Filling out the template is required.** Any pull request that does not
include enough information to be reviewed in a timely manner may be
closed at the maintainers' discretion.
 -->
- [ ] The commit message follows our guidelines:
https://github.com/elementor/elementor/blob/master/.github/CONTRIBUTING.md


## PR Type
What kind of change does this PR introduce?
<!-- Please check the one that applies to this PR using "x" with no
spaces eg: [x]. -->
- [ ] Bugfix
- [ ] Feature
- [ ] Code style update (formatting, local variables)
- [ ] Refactoring (no functional changes, no api changes)
- [ ] Build related changes
- [ ] CI related changes
- [ ] Documentation content changes
- [ ] Other... Please describe:

## Summary

This PR can be summarized in the following changelog entry:

*

## Description
An explanation of what is done in this PR

*

## Test instructions
This PR can be tested by following these steps:

*

## Quality assurance

- [ ] I have tested this code to the best of my abilities
- [ ] I have added unittests to verify the code works as intended
- [ ] Docs have been added / updated (for bug fixes / features)

Fixes #

<!--start_gitstream_placeholder-->
### ✨ PR Description
Purpose: Fix file upload validation to allow WordPress-managed temporary
files from AI image generation while maintaining security for direct
user uploads.

Main changes:
- Added conditional path validation that skips check when
is_elementor_upload flag is true for WordPress filter context
- Preserved strict path validation for direct method calls like
import_template to prevent security vulnerabilities
- Enhanced code documentation explaining the dual validation behavior
for different upload scenarios

_Generated by LinearB AI and added by gitStream._
<sub>AI-generated content may contain inaccuracies. Please verify before
using.
💡 **Tip:** You can customize your AI Description using **Guidelines**
[Learn
how](https://docs.gitstream.cm/automation-actions/#describe-changes)</sub>
<!--end_gitstream_placeholder-->
…6] (elementor#35280)

Angie “create widget” install promotions were shown to every editor user
when the Angie plugin was not active. They are now limited to sites
where the Angie plugin is active (any user) or to WordPress
administrators (`manage_options`), who can still install the plugin.

**Changes:** `is_angie_active` is added to editor config (via
`is_plugin_active( 'angie/angie.php' )`). `@elementor/editor-mcp`
exports `shouldShowAngieInstallPromotion()` (true when Angie is active
or the current user is an administrator). The Elements panel
widget-creation footer and the `CreateWidget` modal listener respect
this flag.

**Ticket:** https://elementor.atlassian.net/browse/ED-23546

**Merge order:** Core should merge before the matching Elementor Pro PR
that consumes `shouldShowAngieInstallPromotion`.

---

## PR Checklist

- [x] The commit message follows our guidelines:
https://github.com/elementor/elementor/blob/master/.github/CONTRIBUTING.md

## PR Type

- [x] Bugfix

## Summary

Hide Angie widget-creation install promotions for non-administrators
when the Angie plugin is not active; keep promotions for all users when
Angie is active, and for administrators when it is not.

## Description

See top of this description.

## Test instructions

1. As **administrator**, Angie **inactive**: open Elements, search
widgets — widget-creation footer with Angie CTA should appear; opening
the install modal should work.
2. As **non-admin** (e.g. Editor), Angie **inactive**: same search —
footer should **not** appear.
3. With Angie **active** (any role that can use the editor): footer
should appear for users who could see it before (per existing panel
rules).

## Quality assurance

- [x] I have tested this code to the best of my abilities
- [x] I have added unittests to verify the code works as intended
- [ ] Docs have been added / updated (for bug fixes / features)

Fixes #

<!--start_gitstream_placeholder-->
### ✨ PR Description
Purpose: Fix Angie promotion visibility in Elementor panel by
restricting widget creation prompts to administrators or when Angie
plugin is actively installed.

Main changes:
- Added elementorIsAngieIframeInDocument() helper function to detect
active Angie plugin presence via iframe
- Implemented access control in onSearchEmpty() to gate widget creation
view based on Angie presence or administrator role
- Empty widget creation region when neither Angie is installed nor user
has administrator privileges

_Generated by LinearB AI and added by gitStream._
<sub>AI-generated content may contain inaccuracies. Please verify before
using.
💡 **Tip:** You can customize your AI Description using **Guidelines**
[Learn
how](https://docs.gitstream.cm/automation-actions/#describe-changes)</sub>
<!--end_gitstream_placeholder-->

---------

Co-authored-by: ElementorBot <48412871+elementorbot@users.noreply.github.com>
## PR Checklist
<!-- 
Please check if your PR fulfills the following requirements:
**Filling out the template is required.** Any pull request that does not
include enough information to be reviewed in a timely manner may be
closed at the maintainers' discretion.
 -->
- [ ] The commit message follows our guidelines:
https://github.com/elementor/elementor/blob/master/.github/CONTRIBUTING.md


## PR Type
What kind of change does this PR introduce?
<!-- Please check the one that applies to this PR using "x" with no
spaces eg: [x]. -->
- [ ] Bugfix
- [ ] Feature
- [ ] Code style update (formatting, local variables)
- [ ] Refactoring (no functional changes, no api changes)
- [ ] Build related changes
- [ ] CI related changes
- [ ] Documentation content changes
- [ ] Other... Please describe:

## Summary

This PR can be summarized in the following changelog entry:

*

## Description
An explanation of what is done in this PR

*

## Test instructions
This PR can be tested by following these steps:

*

## Quality assurance

- [ ] I have tested this code to the best of my abilities
- [ ] I have added unittests to verify the code works as intended
- [ ] Docs have been added / updated (for bug fixes / features)

Fixes #

Co-authored-by: ElementorBot <48412871+elementorbot@users.noreply.github.com>
## Summary
- Fix `one-click-release.yml` passing `PACKAGE_VERSION` (e.g.
`3.30.0-beta1`) directly to `publish-packages.yml`'s `version` input,
which fails the strict `^X.Y.Z$` semver validation
- For **beta**: passes `version=3.30.0`, `version_suffix=beta1`,
`tag=beta`
- For **GA**: passes `version=3.30.0`, `version_suffix=` (empty),
`tag=latest`

## Test plan
- [ ] Trigger `one-click-release` with `channel=beta`, `dry_run=true` —
`Validate Inputs` step should pass
- [ ] Trigger `one-click-release` with `channel=ga`, `dry_run=true` —
`Validate Inputs` step should pass
- [ ] Confirm beta packages publish as `X.Y.Z-betaN` under dist-tag
`beta`
- [ ] Confirm GA packages publish as `X.Y.Z` under dist-tag `latest`
<!--start_gitstream_placeholder-->
### ✨ PR Description
Purpose: Fix package publishing workflow by correcting version and tag
parameter passing to ensure proper NPM package release with appropriate
version suffixes and distribution tags.

Main changes:
- Added three new job outputs (CLEAN_PACKAGE_VERSION, VERSION_SUFFIX,
NPM_TAG) to release job for downstream workflow consumption
- Implemented channel-based logic to compute VERSION_SUFFIX from package
version and set NPM_TAG (latest for GA, channel name otherwise)
- Updated publish-packages workflow inputs to use corrected version
parameters instead of full PACKAGE_VERSION and PACKAGE_TAG

_Generated by LinearB AI and added by gitStream._
<sub>AI-generated content may contain inaccuracies. Please verify before
using.
💡 **Tip:** You can customize your AI Description using **Guidelines**
[Learn
how](https://docs.gitstream.cm/automation-actions/#describe-changes)</sub>
<!--end_gitstream_placeholder-->

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: ElementorBot <48412871+elementorbot@users.noreply.github.com>
…or#35295)

## Summary

Add Mixpanel tracking for Angie CTA and install flows in the widget
search / Create Widget experience.

## PR Checklist

- [x] The commit message follows our guidelines

## PR Type

- [x] Feature

## Description

This PR adds two new Mixpanel events to track user engagement with Angie
promotion and installation from the widget search flow:

1. **`angie_cta_clicked`** — fires when user clicks "Create with Angie"
CTA from the widget search empty state
   - `entry_point: "search_widget"`
   - `has_angie_installed: true | false`

2. **`angie_install_started`** — fires when user clicks "Install Angie"
button in the install dialog
   - `trigger_source: "search_widget"`

**Implementation:**
- Added `@elementor/events` dependency to
`@elementor/editor-widget-creation`
- Integrated `trackEvent` calls in `CreateWidget` component
(`handleShow` and `handleInstall`)
- Unit tests verify both events fire with correct properties

**Decision rationale:**
- Used existing `trackEvent` from `@elementor/events` (same pattern as
transition control analytics)
- Events fire before any async operations to ensure tracking even if
operations fail
- Separate `entry_point` / `trigger_source` property names match the
Jira spec exactly

## Test instructions

1. Open editor on test.local
2. Go to widgets panel → search for a non-existent widget (to trigger
empty state)
3. Click "Create with Angie" CTA
4. Verify `angie_cta_clicked` event in Mixpanel with `entry_point:
"search_widget"` and `has_angie_installed` boolean
5. If Angie not installed, click "Install Angie" button
6. Verify `angie_install_started` event in Mixpanel with
`trigger_source: "search_widget"`

Unit tests:
```bash
npm run test:packages -- packages/core/editor-widget-creation/src/components/__tests__/create-widget.test.tsx
```

## Quality assurance

- [x] I have tested this code to the best of my abilities
- [x] I have added unittests to verify the code works as intended
- [ ] Docs have been added / updated (for bug fixes / features) — not
applicable for internal analytics

Fixes https://elementor.atlassian.net/browse/ED-23550

Made with [Cursor](https://cursor.com)
<!--start_gitstream_placeholder-->
### ✨ PR Description
Purpose: Add Mixpanel analytics tracking to Angie AI widget creation
flow to monitor user engagement and installation patterns from search
widget entry point.

Main changes:
- Implemented `angie_cta_clicked` event tracking with entry point
context and Angie installation status on widget creation trigger
- Added `angie_install_started` event tracking with trigger source
parameter when user initiates Angie plugin installation
- Extended CustomEvent detail type to include entry_point parameter and
persist it using useRef across component lifecycle

_Generated by LinearB AI and added by gitStream._
<sub>AI-generated content may contain inaccuracies. Please verify before
using.
💡 **Tip:** You can customize your AI Description using **Guidelines**
[Learn
how](https://docs.gitstream.cm/automation-actions/#describe-changes)</sub>
<!--end_gitstream_placeholder-->
…23556] (elementor#35305)

## Summary
- The modal close button was changed from a `<div>` to a `<button>`
(ED-18852) for accessibility, but the browser-default button reset was
only applied inside `#elementor-template-library-modal`
- All other modals sharing the same header template were broken: Finder,
Hotkeys, New Template, and Beta Tester showed a box-styled button
instead of a clean icon
- Moved the `button` reset into `_dialog-templates.scss` using
`:is(button)` so it applies universally to every modal using
`.elementor-templates-modal__header__close`
- Added `align-items: center` to `__items-area` to fix vertical
misalignment between close button and adjacent items (e.g. Beta Tester's
"Don't Show Again" text)
- Removed the now-redundant `button` reset from `_templates-modal.scss`
and the partial `border: none` workaround from `_beta-tester.scss`

## Test plan
- [ ] Open Finder (Cmd+E) — close button should show as a clean × icon
with a left border, not a box
- [ ] Open Keyboard Shortcuts modal — close button should look correct
- [ ] WP Admin > Templates > Add New — close button styled correctly
- [ ] WP Admin > Elementor > Beta Tester modal — close button and "Don't
Show Again" text should be vertically aligned
- [ ] Open Template Library (Add Template in editor) — regression check,
close button still looks correct

## Jira
[ED-23556](https://elementor.atlassian.net/browse/ED-23556)

[ED-23556]:
https://elementor.atlassian.net/browse/ED-23556?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
<!--start_gitstream_placeholder-->
### ✨ PR Description
Purpose: Fix inconsistent close button styling in shared modal
components by consolidating button reset styles into common dialog
template styles.

Main changes:
- Removed duplicate button style resets from beta-tester.scss and
templates-modal.scss to eliminate redundancy
- Added centralized button reset styles in dialog-templates.scss using
:is(button) selector for proper rendering
- Fixed modal header items alignment by adding center alignment to
items-area flexbox container

_Generated by LinearB AI and added by gitStream._
<sub>AI-generated content may contain inaccuracies. Please verify before
using.
💡 **Tip:** You can customize your AI Description using **Guidelines**
[Learn
how](https://docs.gitstream.cm/automation-actions/#describe-changes)</sub>
<!--end_gitstream_placeholder-->

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Made String Translatable in controls.php
<!--start_gitstream_placeholder-->
### ✨ PR Description
Purpose: Make promotional control text translatable in motion effects UI
to support internationalization and improve user experience for
non-English speakers.

Main changes:
- Wrapped hardcoded "None" string with esc_html__() translation function
and 'elementor' text domain for i18n support

_Generated by LinearB AI and added by gitStream._
<sub>AI-generated content may contain inaccuracies. Please verify before
using.
💡 **Tip:** You can customize your AI Description using **Guidelines**
[Learn
how](https://docs.gitstream.cm/automation-actions/#describe-changes)</sub>
<!--end_gitstream_placeholder-->

Co-authored-by: ElementorBot <48412871+elementorbot@users.noreply.github.com>
Co-authored-by: Louis Wolmarans <louiswol94@gmail.com>
…ntor#35310)

## PR Checklist
<!-- 
Please check if your PR fulfills the following requirements:
**Filling out the template is required.** Any pull request that does not
include enough information to be reviewed in a timely manner may be
closed at the maintainers' discretion.
 -->
- [x] The commit message follows our guidelines:
https://github.com/elementor/elementor/blob/master/.github/CONTRIBUTING.md


## PR Type
What kind of change does this PR introduce?
<!-- Please check the one that applies to this PR using "x" with no
spaces eg: [x]. -->
- [ ] Bugfix
- [ ] Feature
- [ ] Code style update (formatting, local variables)
- [ ] Refactoring (no functional changes, no api changes)
- [ ] Build related changes
- [ ] CI related changes
- [x] Documentation content changes
- [ ] Other... Please describe:

## Description
An explanation of what is done in this PR

Applies small changes to the copy of the plugin description on both the
plugin file and also the readme.txt to include information about the
Atomic Editor and its features.

## Quality assurance

- [x] I have tested this code to the best of my abilities
- [ ] I have added unittests to verify the code works as intended
- [ ] Docs have been added / updated (for bug fixes / features)
## PR Checklist
<!-- 
Please check if your PR fulfills the following requirements:
**Filling out the template is required.** Any pull request that does not
include enough information to be reviewed in a timely manner may be
closed at the maintainers' discretion.
 -->
- [ ] The commit message follows our guidelines:
https://github.com/elementor/elementor/blob/master/.github/CONTRIBUTING.md


## PR Type
What kind of change does this PR introduce?
<!-- Please check the one that applies to this PR using "x" with no
spaces eg: [x]. -->
- [ ] Bugfix
- [ ] Feature
- [ ] Code style update (formatting, local variables)
- [ ] Refactoring (no functional changes, no api changes)
- [ ] Build related changes
- [ ] CI related changes
- [ ] Documentation content changes
- [ ] Other... Please describe:

## Summary

This PR can be summarized in the following changelog entry:

*

## Description
An explanation of what is done in this PR

*

## Test instructions
This PR can be tested by following these steps:

*

## Quality assurance

- [ ] I have tested this code to the best of my abilities
- [ ] I have added unittests to verify the code works as intended
- [ ] Docs have been added / updated (for bug fixes / features)

Fixes #

<!--start_gitstream_placeholder-->
### ✨ PR Description
Purpose: Temporarily disable video widget tests in regression and
container test suites to unblock CI pipeline while video-related
functionality is being fixed.

Main changes:
- Commented out 'video' widget from elements regression test suite
template list
- Skipped container test for widgets display with various directions and
content width
- Skipped container flex wrap widget display test with TODO references
to ED-23584

_Generated by LinearB AI and added by gitStream._
<sub>AI-generated content may contain inaccuracies. Please verify before
using.
💡 **Tip:** You can customize your AI Description using **Guidelines**
[Learn
how](https://docs.gitstream.cm/automation-actions/#describe-changes)</sub>
<!--end_gitstream_placeholder-->
louiswol94 and others added 30 commits May 5, 2026 20:15
…#35675)

## PR Checklist
<!-- 
Please check if your PR fulfills the following requirements:
**Filling out the template is required.** Any pull request that does not
include enough information to be reviewed in a timely manner may be
closed at the maintainers' discretion.
 -->
- [ ] The commit message follows our guidelines:
https://github.com/elementor/elementor/blob/master/.github/CONTRIBUTING.md


## PR Type
What kind of change does this PR introduce?
<!-- Please check the one that applies to this PR using "x" with no
spaces eg: [x]. -->
- [ ] Bugfix
- [ ] Feature
- [ ] Code style update (formatting, local variables)
- [ ] Refactoring (no functional changes, no api changes)
- [ ] Build related changes
- [ ] CI related changes
- [ ] Documentation content changes
- [ ] Other... Please describe:

## Summary

This PR can be summarized in the following changelog entry:

*

## Description
An explanation of what is done in this PR

*

## Test instructions
This PR can be tested by following these steps:

*

## Quality assurance

- [ ] I have tested this code to the best of my abilities
- [ ] I have added unittests to verify the code works as intended
- [ ] Docs have been added / updated (for bug fixes / features)

Fixes #

<!--start_gitstream_placeholder-->
### ✨ PR Description
## 1. Problem & Context

Introduces number-range prop type to support min/max value constraints
for textarea fields during widget migration (ED-23579). Enables
structured validation of bounded numeric inputs instead of flat scalar
values.

## 2. What Changed (Where)

- **Backend**: New `Number_Range_Prop_Type` class defines shape with
min/max Number fields
- **Frontend**: TypeScript prop type and schema export for editor-props
package
- **Migration**: Added number→number-range conversion ops and textarea
maxlength→length field mapping in manifest

## 3. How It Works

Migration converts legacy number values to number-range structure:
scalar moves to `value.max.value`, type becomes `number-range`. Reverse
migration extracts max value back to scalar on downgrade. Shape ensures
min/max are both Number_Prop_Type instances for validation consistency.

## 4. Risks

Migration ops assume source `$type === "number"` — if existing data
lacks type field or has unexpected structure, conversion silently skips.
Test with actual textarea data to confirm maxlength→length→number-range
chain works end-to-end.

_Generated by LinearB AI and added by gitStream._
<sub>AI-generated content may contain inaccuracies. Please verify before
using.
💡 **Tip:** You can customize your AI Description using **Guidelines**
[Learn
how](https://docs.gitstream.cm/automation-actions/#describe-changes)</sub>
<!--end_gitstream_placeholder-->

---------

Co-authored-by: ElementorBot <48412871+elementorbot@users.noreply.github.com>
<!--start_gitstream_placeholder-->
### ✨ PR Description
## 1. Problem & Context

Form input fields were inaccessible because the schema validation wasn't
properly filtering non-configurable properties. The fix refactors
`isPropKeyConfigurable` to accept both prop key and type, enabling
context-aware filtering of internal properties like `_cssid`, `classes`,
and `attributes`.

## 2. What Changed (Where)

- **widgets-schema-resource.ts**: Moved property filtering into
`Object.entries().filter()` chain using enhanced `isPropKeyConfigurable`
check; removed post-processing deletion loop.
- **validate-input.ts**: Updated `isPropKeyConfigurable` call to pass
`propSchema` parameter for type-aware validation.
- **props-to-llm-schema.ts**: Rewrote `isPropKeyConfigurable(key,
propType?)` to allow LLM-configurable overrides via
`propType.meta.llm_configurable`; removed unused
`propsSchemaToJsonSchema` function.

## 3. How It Works

Non-configurable keys are now checked in two stages: (1) if key is in
blocklist, check if propType explicitly allows it via `llm_configurable`
meta flag; (2) otherwise, property is configurable. This lets special
properties opt-in to exposure when needed, rather than blanket
exclusion.

## 4. Risks

Minor: The signature change to `isPropKeyConfigurable` adds an optional
parameter. Callers passing only the key still work but won't benefit
from type-aware filtering—confirm all call sites get updated. No schema
version bump needed since this is internal API.

_Generated by LinearB AI and added by gitStream._
<sub>AI-generated content may contain inaccuracies. Please verify before
using.
💡 **Tip:** You can customize your AI Description using **Guidelines**
[Learn
how](https://docs.gitstream.cm/automation-actions/#describe-changes)</sub>
<!--end_gitstream_placeholder-->

---------

Co-authored-by: Nikita (Mykyta) <134918658+mykytamurzin@users.noreply.github.com>
<!--start_gitstream_placeholder-->
### ✨ PR Description
## 1. Problem & Context

Refactoring design-system import flows to support both legacy
(single-file) and streaming (directory-based) formats with configurable
conflict resolution strategies. Centralizes import logic into reusable
utilities and decouples runner concerns from data handling.

## 2. What Changed (Where)

| File | Change |
|------|--------|
| `import.php` (route) | Added `include` and `customization` parameters
to API schema |
| `design-system-import-context.php` | New context resolver for import
flow type and conflict strategy |
| `processes/import.php` | Extracted runner data construction into
`build_runner_data()` method |
| `global-classes-repository.php` | Added `get_order()`,
`update_order_and_labels()`, `delete_all()` methods |
| `global-classes/runners/import.php` | Refactored to use context
resolver; delegates to `Import_Utils`/`Legacy_Import_Utils` |
| `import-utils.php`, `legacy-import-utils.php` | New utilities with
detailed conflict resolution (skip/replace/merge/override-all) |
| `variables/runners/import.php` | Updated to use context resolver;
returns structured result objects |
| Tests | Added comprehensive conflict resolution tests; fixed kit state
isolation bugs |

## 3. How It Works

Import request specifies `include: ['settings'|'design-system']` and
optional `customization.{settings|design-system}.conflict_resolution`.
`Design_System_Import_Context` normalizes this into a resolution
strategy. For v4.1.0+, `Import_Utils` streams directory of individual
class files with `order.json` metadata; legacy format falls back to
single JSON. Conflict resolution (skip/replace/merge/override-all)
applies per-item, tracking created/renamed/replaced/skipped/failed
results. Repository operations wrapped in transactions with runtime
cache invalidation.

## 4. Risks

**Backward compatibility**: Legacy format detection uses version
comparison—verify `manifest.elementor_version` is always present in old
exports. **Transaction safety**: ROLLBACK on exception assumes DB
isolation; test with concurrent imports. **Kit switching**:
`get_kit_for_reading()` logic assumes previous kit still exists after
new kit creation—dangling references will fail silently. Mitigate with
tests validating kit state transitions (added in test helpers).

_Generated by LinearB AI and added by gitStream._
<sub>AI-generated content may contain inaccuracies. Please verify before
using.
💡 **Tip:** You can customize your AI Description using **Guidelines**
[Learn
how](https://docs.gitstream.cm/automation-actions/#describe-changes)</sub>
<!--end_gitstream_placeholder-->

---------

Co-authored-by: ElementorBot <48412871+elementorbot@users.noreply.github.com>
Co-authored-by: Omer Haim Israeli <omeri@elementor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
## PR Checklist
- [x] The commit message follows our guidelines

## PR Type
- [x] Feature

## Summary

This PR can be summarized in the following changelog entry:

* Internal: Export global classes in chunks to support up to 10k classes
per site [ED-23093]

## Description

Replaces the single `global-classes.json` export file with the chunked
layout the matching import (`Import_Utils::import_classes`) already
expects:

- `global-classes/order.json` — `[{ id, label }, ...]`
- `global-classes/<class_id>.json` — one file per class

Streams classes from the repository via
`Global_Classes_Repository::each_item()` and pre-encodes each entry to
JSON so the export framework writes raw strings into the zip without
holding a full in-memory array. This is the export-side counterpart of
the chunked import shipped in elementor#35677.

Also fixes a pre-existing perf bug in
`Site_Settings::get_classes_count()`: it was loading every global class
via `Global_Classes_Repository::all()->get()` just to count them, which
made export hang on sites with hundreds/thousands of classes. Now reads
the order array from kit meta instead — same number, no class data
loaded. O(N) → O(1).

Reopen of elementor#35681 (closed when the base
`feature/ED-23093/frontend-optimizations` was merged into main).

## Test instructions

1. Seed a site with a large number of global classes (e.g. 1000) —
`scratch/seed-global-classes.php` can be used for this.
2. Trigger an export from the editor (Tools → Export Kit) including site
settings.
3. Verify the exported zip contains `global-classes/order.json` plus one
`global-classes/<id>.json` per class.
4. Verify the export completes in seconds (was previously hanging for
minutes due to the count bug).
5. Re-import the resulting kit and verify all classes appear correctly.

## Quality assurance

- [x] I have tested this code to the best of my abilities
- [x] I have added unittests to verify the code works as intended
- [ ] Docs have been added / updated (for bug fixes / features)

Fixes ED-23093

Made with [Cursor](https://cursor.com)

[ED-23093]:
https://elementor.atlassian.net/browse/ED-23093?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
<!--start_gitstream_placeholder-->
### ✨ PR Description
## 1. Problem & Context

Refactoring global classes export to use chunked individual class files
instead of a single monolithic file, enabling better scalability and
incremental export of design system data. Part of ED-23093.

## 2. What Changed (Where)

- **export.php**: Export logic restructured to generate per-class JSON
files plus `order.json` manifest; sanitization logic extracted to
private methods
- **site-settings.php**: `get_classes_count()` now uses kit-scoped
`Global_Classes_Order` instead of repository-wide count
- **test-export-runner.php**: Tests updated to verify chunked file
structure, with new setUp() and helper method for file indexing

## 3. How It Works

Export now iterates kit-scoped classes via
`Global_Classes_Repository::each_item()`, sanitizing each via
`Global_Classes_Parser::parse_items()`. Valid items generate individual
JSON files keyed by class ID. Concurrently builds `order.json` mapping
IDs to labels from sanitized data. Invalid classes are silently dropped;
empty exports return bare manifest. Kit-scoped ordering replaces global
repository ordering, ensuring consistent per-kit exports.

## 4. Risks

**Data loss during export**: Invalid classes filtered silently—malformed
data won't surface in exports but won't error either. Mitigation:
logs/monitoring on parse failures advised. **Kit-scoped behavior
shift**: Count/order now kit-dependent vs. global—verify no code assumes
global state. **Test coverage**: New tests handle happy path and invalid
items, but no test for missing kit scenario (returns 0 count silently).

_Generated by LinearB AI and added by gitStream._
<sub>AI-generated content may contain inaccuracies. Please verify before
using.
💡 **Tip:** You can customize your AI Description using **Guidelines**
[Learn
how](https://docs.gitstream.cm/automation-actions/#describe-changes)</sub>
<!--end_gitstream_placeholder-->

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: ElementorBot <48412871+elementorbot@users.noreply.github.com>
…0] (elementor#35699)

## Summary

- **Scope:** This PR only updates the **widget creation empty state and
search footer** copy and CTA styling (the `e_widget_creation` experiment
UI when searching the Elements panel). It does **not** implement the
full persistent Angie section below the main widgets described in the
ticket.
- CTA label is **Create custom widget** when the Angie plugin is active,
**Install Angie** when it is not (`Hints::is_plugin_active( 'angie' )`).
Click behavior is unchanged: still dispatches
`elementor/editor/create-widget` for the existing install modal / Angie
flow.
- CTA uses `--e-pink-900` for link color; AI icon removed from the CTA
button markup (text-only CTA).
- Playwright expectations updated for Angie-not-installed (search footer
and empty state).

## Test plan

- [ ] With `e_widget_creation` active and Angie inactive: search with
results → footer CTA reads Install Angie; search with no matches →
empty-state CTA reads Install Angie; click still opens the promotion
modal / install flow.
- [ ] With Angie active: CTA reads Create custom widget and opens Angie
as before.
- [ ] Run widget creation Playwright suite (or rely on CI).

## Jira

[ED-23900](https://elementor.atlassian.net/browse/ED-23900)


[ED-23900]:
https://elementor.atlassian.net/browse/ED-23900?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
<!--start_gitstream_placeholder-->
### ✨ PR Description
## 1. Problem & Context

Conditionally show "Install Angie" vs "Create custom widget" CTA based
on Angie plugin status, and simplify button markup by removing redundant
icon markup. Ticket: ED-23900.

## 2. What Changed (Where)

- `panel-elements.php`: Added `Hints` import; compute CTA text
server-side based on `Hints::is_plugin_active('angie')`; removed icon
markup from both buttons.
- `_elements.scss`: Changed button color from
`var(--e-a-color-accent-promotion)` to `#{$e-pink-900}`.
- `widget-creation.test.ts`: Updated assertions to expect "Install
Angie" text instead of "Create custom widget".

## 3. How It Works

Server-side logic checks plugin activation once per page load, stores
result in `$widget_creation_cta_text`, and injects the appropriate label
into both template buttons. Icon removed from markup (likely handled via
CSS or not needed).

## 4. Risks

Test expectations hardcoded to "Install Angie"—will fail if Angie
becomes active in test environment. Verify test setup doesn't
auto-activate Angie or add conditional logic.

_Generated by LinearB AI and added by gitStream._
<sub>AI-generated content may contain inaccuracies. Please verify before
using.
💡 **Tip:** You can customize your AI Description using **Guidelines**
[Learn
how](https://docs.gitstream.cm/automation-actions/#describe-changes)</sub>
<!--end_gitstream_placeholder-->

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: ElementorBot <48412871+elementorbot@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…tor#35716)

## Description
Bumps the minimum required WordPress version from **6.7** to **6.8** per
ED-23932.

## What changed
- `readme.txt`: `Requires at least: 6.7` → `6.8`
- `.github/workflows/phpunit.yml`: dropped `"6.7"` from the
`wordpress_versions` matrices for both the PR/Merge job and the Nightly
job, since 6.7 is no longer a supported minimum.

## Jira
[ED-23932](https://elementor.atlassian.net/browse/ED-23932)

Made with [Cursor](https://cursor.com)

[ED-23932]:
https://elementor.atlassian.net/browse/ED-23932?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
<!--start_gitstream_placeholder-->
### ✨ PR Description
## 1. Problem & Context

Bumping WordPress minimum version from 6.7 to 6.8 across CI testing to
align with updated project requirements.

## 2. What Changed (Where)

`.github/workflows/phpunit.yml`: Updated WordPress version matrix for
PR/merge tests (`["latest", "6.9", "6.8"]`) and nightly tests
(`["latest", "6.9", "6.8", "nightly"]`), dropping 6.7 support.

## 3. How It Works

PHPUnit runner workflows now test against WordPress 6.8+ instead of
6.7+, ensuring CI validates compatibility with the new minimum version
floor.

## 4. Risks

None — straightforward version bump in test matrix with no code logic
changes or dependency modifications.

_Generated by LinearB AI and added by gitStream._
<sub>AI-generated content may contain inaccuracies. Please verify before
using.
💡 **Tip:** You can customize your AI Description using **Guidelines**
[Learn
how](https://docs.gitstream.cm/automation-actions/#describe-changes)</sub>
<!--end_gitstream_placeholder-->

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: ElementorBot <48412871+elementorbot@users.noreply.github.com>
This PR bumps the version to **4.2.0**.

**Changes:**
- Updated version in `elementor.php`
- Updated version in `package.json` files
- Updated version in packages

**Created by:** GitHub Actions workflow
**Branch:** `version-4.2.0-to-main`

Co-authored-by: elementorbot <admin@elementor.com>
<!--start_gitstream_placeholder-->
### ✨ PR Description
## 1. Problem & Context

CI flakiness in global classes deletion tests — the save button's state
transition wasn't being properly awaited after clicking, causing race
conditions. Replaced a commented-out wait on button `disabled` state
with a wait on `hidden` state using the project's centralized timeout
config.

## 2. What Changed (Where)

- `tests/playwright/sanity/modules/v4-tests/global-classes/utils.ts`:
Added timeout config import and replaced commented-out button state wait
with proper hidden state wait in `saveAndCloseClassManager()`

## 3. How It Works

After clicking save in the class manager, the test now explicitly waits
for the save button to disappear (hidden state) before attempting to
close the dialog. Uses `timeouts.expect` from centralized config instead
of hardcoded timeout, ensuring consistency with project-wide timing
expectations. The `.catch(() => {})` swallows errors if the button
disappears faster than expected.

## 4. Risks

None — this strictly improves test stability by synchronizing on UI
state that already exists.

_Generated by LinearB AI and added by gitStream._
<sub>AI-generated content may contain inaccuracies. Please verify before
using.
💡 **Tip:** You can customize your AI Description using **Guidelines**
[Learn
how](https://docs.gitstream.cm/automation-actions/#describe-changes)</sub>
<!--end_gitstream_placeholder-->
…ntor#35626)

## Summary
- Add `wordpress/mcp-adapter` (^0.5.0) and lockfile updates including
`wordpress/php-mcp-schema`.
- Register the `mcp` module in `Modules_Manager` and implement
`modules/mcp` wired to `WP\\MCP\\Core\\McpAdapter`.
- Gate the module behind the `e_wp_abilities_api` experiment; register
Elementor ability category, abilities (list pages, structure, settings,
create page, globals), and an MCP server exposing those tools over HTTP
transport.

## Test plan
- [ ] Enable the `e_wp_abilities_api` experiment (target validation on
7.0 per ticket).
- [ ] Confirm `McpAdapter` loads and MCP server registers without
`WP_Error`.
- [ ] Smoke-test MCP HTTP transport against the registered abilities
(list pages, get structure, etc.) in a staging environment.

## Jira
[ED-23888](https://elementor.atlassian.net/browse/ED-23888)

Made with [Cursor](https://cursor.com)

[ED-23888]:
https://elementor.atlassian.net/browse/ED-23888?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
<!--start_gitstream_placeholder-->
### ✨ PR Description
## 1. Problem & Context

Integrates WordPress MCP (Model Context Protocol) adapter to expose
Elementor capabilities through a standardized API for AI/external tool
integration. Enables remote clients to read/modify Elementor pages
programmatically.

## 2. What Changed (Where)

- **modules-manager.php**: Registered new 'mcp' module in module loading
sequence
- **modules/mcp/module.php**: Main module orchestrating ability
registration and MCP server setup with WordPress Abilities API
- **modules/mcp/abilities/**: Five ability classes (List_Pages,
Get_Structure, Create_Page, Update_Settings, Get_Globals) wrapping core
Elementor operations
- **composer.json**: Added wordpress/mcp-adapter ^0.5.0 dependency

## 3. How It Works

Module activates only when McpAdapter class exists and experiment
feature flag 'e_wp_abilities_api' is enabled. On init, it registers an
'elementor' ability category, instantiates five ability handlers that
call `wp_register_ability()` with input/output schemas and permission
callbacks, then creates an MCP server binding these abilities. Each
ability validates user permissions (edit_posts/edit_post) before
executing core operations (creating drafts, fetching element trees,
updating document settings, listing Elementor-built pages, reading
global classes/variables).

## 4. Risks

Permission model relies on `current_user_can()` checks—verify those are
sufficient for MCP transport layer (HTTP assumed). No validation of
schema conformance for settings updates (additionalProperties: true
risks invalid data). Experiment flag defaults to ACTIVE which exposes
API broadly; should default INACTIVE pending stability verification.

_Generated by LinearB AI and added by gitStream._
<sub>AI-generated content may contain inaccuracies. Please verify before
using.
💡 **Tip:** You can customize your AI Description using **Guidelines**
[Learn
how](https://docs.gitstream.cm/automation-actions/#describe-changes)</sub>
<!--end_gitstream_placeholder-->

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
…elementor#35718)

## PR Checklist
<!-- 
Please check if your PR fulfills the following requirements:
**Filling out the template is required.** Any pull request that does not
include enough information to be reviewed in a timely manner may be
closed at the maintainers' discretion.
 -->
- [ ] The commit message follows our guidelines:
https://github.com/elementor/elementor/blob/master/.github/CONTRIBUTING.md


## PR Type
What kind of change does this PR introduce?
<!-- Please check the one that applies to this PR using "x" with no
spaces eg: [x]. -->
- [ ] Bugfix
- [ ] Feature
- [ ] Code style update (formatting, local variables)
- [ ] Refactoring (no functional changes, no api changes)
- [ ] Build related changes
- [ ] CI related changes
- [ ] Documentation content changes
- [ ] Other... Please describe:

## Summary

This PR can be summarized in the following changelog entry:

*

## Description
An explanation of what is done in this PR

*

## Test instructions
This PR can be tested by following these steps:

*

## Quality assurance

- [ ] I have tested this code to the best of my abilities
- [ ] I have added unittests to verify the code works as intended
- [ ] Docs have been added / updated (for bug fixes / features)

Fixes #
Need to copy this workflow to elementor-pro

Co-authored-by: Davids Seveloff <94905340+davseve@users.noreply.github.com>
…lementor#35724)

## Summary
- Zero-pad the minor version in the generated release branch name so
that branches `4.00`–`4.10` sort correctly lexicographically
- Updated `scripts/create-version-change.js`: use
`String(minor).padStart(2, '0')` instead of bare `minor`
- Updated `.github/workflows/create-version-change.yml`: use `printf
'%02d' $MINOR` instead of bare `$MINOR`

## Test plan
- [ ] Run `node scripts/create-version-change.js` when `package.json`
version is `4.2.0` and verify the branch name shown is `4.02` (not
`4.2`)
- [ ] Confirm branches sort correctly: `echo -e "4.02\n4.10\n4.09" |
sort -V` should output `4.02 → 4.09 → 4.10`
- [ ] Verify that versions >= `4.10` remain unaffected (no
triple-padding)

## Jira
[ED-23982](https://elementor.atlassian.net/browse/ED-23982)

[ED-23982]:
https://elementor.atlassian.net/browse/ED-23982?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
<!--start_gitstream_placeholder-->
### ✨ PR Description
## 1. Problem & Context

Branch names like `4.0` were inconsistent with the upcoming `4.0x`
versioning scheme. This standardizes branch naming to zero-pad minor
versions (e.g., `4.00`, `4.01`) across both the Node.js script and
GitHub Actions workflow, ensuring consistency as versions progress
beyond single digits.

## 2. What Changed (Where)

- `scripts/create-version-change.js`: Updated branch name format from
`${major}.${minor}` to `${major}.${String(minor).padStart(2, '0')}`
- `.github/workflows/create-version-change.yml`: Updated shell branch
name format from `${MAJOR}.${MINOR}` to `${MAJOR}.$(printf '%02d'
$MINOR)`

## 3. How It Works

Both files now zero-pad the minor version to two digits when
constructing branch names. The JavaScript implementation uses
`String.padStart(2, '0')` while the bash workflow uses `printf '%02d'`.
For version `4.0`, branch name becomes `4.00`; for `4.1` it becomes
`4.01`, and so on through `4.99`.

## 4. Risks

Existing branches with old naming format (`4.0`) won't automatically map
to new format (`4.00`). Verify that any branch references in CI/CD,
merge scripts, or documentation are updated to match the new convention.

_Generated by LinearB AI and added by gitStream._
<sub>AI-generated content may contain inaccuracies. Please verify before
using.
💡 **Tip:** You can customize your AI Description using **Guidelines**
[Learn
how](https://docs.gitstream.cm/automation-actions/#describe-changes)</sub>
<!--end_gitstream_placeholder-->
…ources [Ed 23921] (elementor#35692)

## PR Checklist
<!-- 
Please check if your PR fulfills the following requirements:
**Filling out the template is required.** Any pull request that does not
include enough information to be reviewed in a timely manner may be
closed at the maintainers' discretion.
 -->
- [ ] The commit message follows our guidelines:
https://github.com/elementor/elementor/blob/master/.github/CONTRIBUTING.md


## PR Type
What kind of change does this PR introduce?
<!-- Please check the one that applies to this PR using "x" with no
spaces eg: [x]. -->
- [ ] Bugfix
- [ ] Feature
- [ ] Code style update (formatting, local variables)
- [ ] Refactoring (no functional changes, no api changes)
- [ ] Build related changes
- [ ] CI related changes
- [ ] Documentation content changes
- [ ] Other... Please describe:

## Summary

This PR can be summarized in the following changelog entry:

*

## Description
An explanation of what is done in this PR

*

## Test instructions
This PR can be tested by following these steps:

*

## Quality assurance

- [ ] I have tested this code to the best of my abilities
- [ ] I have added unittests to verify the code works as intended
- [ ] Docs have been added / updated (for bug fixes / features)

Fixes #

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
…sorignore (elementor#35708)

## Summary
- Condense 5 workspace rules (`development-workflow`,
`legacy-integration`, `react-typescript`, `turbo-repo`, `wordpress-php`)
from verbose to concise, and switch them from `alwaysApply: true` to
`alwaysApply: false` (on-demand), saving ~4k tokens per session
- Add root `.cursorignore` to exclude generated directories
(`node_modules/`, `build/`, `vendor/`, `.turbo/`, etc.) from Cursor
indexing
- Net reduction: 1,055 lines removed, 98 added

## Test plan
- [ ] Verify Cursor still picks up on-demand rules when editing matching
files (e.g. `.php` files trigger `wordpress-php.mdc`)
- [ ] Verify Cursor indexing excludes directories listed in
`.cursorignore`
- [ ] Confirm no impact on existing development workflows


Made with [Cursor](https://cursor.com)
<!--start_gitstream_placeholder-->
### ✨ PR Description
## 1. Problem & Context

Cursor's AI context was bloated with verbose rules (~1000+ lines) that
always applied, slowing down every interaction and including irrelevant
legacy/PHP context for TypeScript work. This optimizes token usage by
condensing rules 5-10x, disabling auto-apply (`alwaysApply: false`), and
excluding build artifacts via `.cursorignore`.

## 2. What Changed (Where)

- `.cursor/rules/development-workflow.mdc` - Condensed from 261 to 38
lines (command reference only)
- `.cursor/rules/legacy-integration.mdc` - Condensed from 304 to 55
lines (removed adapters, test patterns)
- `.cursor/rules/react-typescript.mdc` - Condensed from 325 to 56 lines
(kept core patterns, removed examples)
- `.cursor/rules/turbo-repo.mdc` - Condensed from 150 to 49 lines
(workflow and pluggable pattern focus)
- `.cursor/rules/wordpress-php.mdc` - Condensed from 176 to 48 lines
(security + performance essentials)
- `.cursorignore` - New file excluding `node_modules/`, `vendor/`, build
artifacts, test outputs

## 3. How It Works

All rule files now act as quick-reference cheat sheets instead of
comprehensive guides. Set `alwaysApply: false` so Cursor only loads
rules when file globs match (e.g., PHP rules only for `**/*.php`). The
`.cursorignore` prevents Cursor from indexing 10K+ files in
`node_modules/`, `vendor/`, and build directories, reducing context
window pollution. Developers manually reference rules when needed rather
than auto-injecting ~1000 lines per query.

## 4. Risks

**False negatives on rule application**: If globs are too narrow or
developers forget rules exist, they might miss guidance. Mitigate by
keeping rule names descriptive and ensuring common file patterns are
covered. **Context starvation on complex tasks**: Removing detailed
examples might force developers to external docs. Monitor if this
increases back-and-forth or errors—can restore specific examples if
needed.

_Generated by LinearB AI and added by gitStream._
<sub>AI-generated content may contain inaccuracies. Please verify before
using.
💡 **Tip:** You can customize your AI Description using **Guidelines**
[Learn
how](https://docs.gitstream.cm/automation-actions/#describe-changes)</sub>
<!--end_gitstream_placeholder-->

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: ElementorBot <48412871+elementorbot@users.noreply.github.com>
…ementor#35701)

## Summary
- Registers `custom-widgets` panel category fallback from Widget
Creation module when the experiment is active, Angie is **not** active,
and the category is not already registered.
- `Elements_Manager::CATEGORY_CUSTOM_WIDGETS`, panel template hooks,
Install Angie heading CTA, empty-state copy, SCSS, and panel JS
(`create-widget` from category with `widgets_panel` entry point).
- Playwright widget-creation suite updates included.

## Test plan
- [ ] With `e_widget_creation` on and Angie inactive: Elements panel
shows Custom widgets category and fallback UI as expected.
- [ ] With Angie active: fallback category is not registered by core
(existing Angie-owned category remains).
- [ ] CI / targeted Playwright as appropriate.

## Jira
[ED-23900](https://elementor.atlassian.net/browse/ED-23900)

<img width="357" height="252" alt="image"
src="https://github.com/user-attachments/assets/8efd90f8-d83e-4a53-8318-a8f2c248a3d4"
/>


Made with [Cursor](https://cursor.com)

[ED-23900]:
https://elementor.atlassian.net/browse/ED-23900?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
<!--start_gitstream_placeholder-->
### ✨ PR Description
## 1. Problem & Context

When Angie (AI widget builder) is inactive, the custom widgets feature
remains exposed but non-functional. This PR provides fallback UI and
messaging to guide users toward installing Angie, preventing dead-end
UX.

## 2. What Changed (Where)

- **panel-elements.php**: Conditional CTA text ("Install Angie" vs
"Create custom widget") based on Angie status; removed hardcoded icon
markup.
- **elements.php**: Added `CATEGORY_CUSTOM_WIDGETS` constant and
category promotion logic.
- **widget-creation/module.php**: New fallback category registration
when Angie inactive; renders "Install Angie" button in panel heading and
empty state messaging.
- **category.js**: Wired custom widgets CTA click handler; reformatted
sorting/events code (no logic change).
- **widget-creation.js**: Extracted `CREATE_WIDGET_PROMPT` as exportable
constant for reuse across entry points.
- **_elements.scss**: Styled custom widgets empty state and heading CTA;
changed color from accent variable to `$e-pink-900`.
- **widget-creation.test.ts**: Added custom widgets category visibility
test; updated CTA assertions to expect "Install Angie" when no Angie.

## 3. How It Works

On editor load with widget creation experiment active, the module checks
Angie status. If inactive, it registers a "Custom widgets" category via
hook and attaches handlers to render a heading CTA and empty state. Both
dispatch the same `create-widget` event with entry point tracking. When
Angie is active, the normal flow takes over and these fallbacks are
skipped.

## 4. Risks

Minor: Color hardcoding (`$e-pink-900` instead of CSS variable) loses
theme flexibility—verify this aligns with design intent. Test coverage
added for the no-Angie path, but missing Angie-active scenario
validation.

_Generated by LinearB AI and added by gitStream._
<sub>AI-generated content may contain inaccuracies. Please verify before
using.
💡 **Tip:** You can customize your AI Description using **Guidelines**
[Learn
how](https://docs.gitstream.cm/automation-actions/#describe-changes)</sub>
<!--end_gitstream_placeholder-->

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
…em panel [ED-23880] (elementor#35689)

## PR Checklist
- [x] The commit message follows our guidelines

## PR Type
- [x] Feature

## Summary

This PR can be summarized in the following changelog entry:

* Internal: Wire the design system import dialog to the unified
import-export-customization REST flow and host it inside the design
system panel header.

## Description

Builds on top of elementor#35623 (which introduced the import dialog UI) by:

* Moving the import code from `@elementor/editor-editing-panel` into a
new `import/` module inside `@elementor/editor-design-system`, and
rendering the trigger button in the design system panel header (left of
the close button) instead of next to the class manager icon.
* Connecting the producer to the unified import-export-customization
REST flow introduced in elementor#35613 / elementor#35677:
1. `POST /elementor/v1/import-export-customization/upload` (multipart,
field `e_import_file`) — returns a `session`.
2. `POST /elementor/v1/import-export-customization/import` with `{
session, include: ['design-system'], customization: { 'design-system': {
conflict_resolution } } }` — returns the runner list.
3. `POST /elementor/v1/import-export-customization/import-runner` once
per supported runner (`global-classes`, `global-variables`).
* Mapping the UI conflict choice to the backend strategy: `replace ->
replace`, `keep -> skip`.
* Refreshing the editor after import: calls `reloadCurrentDocument()`
(which already re-runs `loadCurrentDocumentClasses` via
`editor/documents/attach-preview`) and dispatches
`GLOBAL_STYLES_IMPORTED_EVENT`. The variables listener was flipped to
call `service.load()` unconditionally because the runner response only
carries imported IDs/labels, not full style definitions.
* Adding Mixpanel analytics for the PRD-defined events (`importOpened`,
`fileSelected`, `validationFailed`, `conflictChoice`, `confirmed`,
`imported`, `importFailed`) and registering their names in
`events-config.js`.
* Distinguishing upload validation errors from generic import errors via
a dedicated `DesignSystemUploadValidationError` so the analytics fire
the right event.

## Test instructions

1. Open a page in the editor with the design system experiment enabled.
2. Open the design system panel and click the upload icon in its header.
3. Drop a `.zip` exported from `import-export-customization` (containing
`design-system`), pick a conflict strategy, click *Import*.
4. Verify that:
* The dialog closes immediately and an *Importing in progress*
notification appears.
* The 3-call backend flow runs (`upload` -> `import` -> `import-runner`
per runner) — visible in the network tab.
* On success, the *Design system imported* notification appears, the
document reloads, the class manager shows the imported classes, and the
variables panel shows the imported variables.
* On failure, an error notification appears with a *Try again* button
that reopens the dialog.
   * The trigger button is disabled while an import is running.

## Quality assurance

- [x] I have tested this code to the best of my abilities
- [x] I have added unittests to verify the code works as intended
- [ ] Docs have been added / updated (for bug fixes / features)

Fixes ED-23880


Made with [Cursor](https://cursor.com)
<!--start_gitstream_placeholder-->
### ✨ PR Description
## 1. Problem & Context

**[ED-23880]** Wired up design system import/export UI to the backend
API (`elementor/v1/import-export-customization`). Previously just had UI
scaffolding—now it's functional. Export downloads a .zip of
classes+variables; import uploads, runs backend runners
(`global-classes`, `global-variables`), then triggers UI updates. Added
tracking hooks for analytics, moved trigger from standalone button into
header menu to consolidate UX, and simplified global-styles event
handling to directly reload data instead of manual merges.

## 2. What Changed (Where)

- **`design-system-header-menu.tsx`** (new): Menu with import/export
items replacing old `TriggerButton`, disables during mutations
- **`design-system-panel-content.tsx`**: Wired `DesignSystemHeaderMenu`
into header with spacing adjustments
- **`use-export-request.ts`** (new): POST to `/export`, decodes base64 →
Blob, returns `{ blob, fileName }`
- **`use-import-request.ts`** (new): 3-step flow—upload file → start
import w/ conflict strategy → run runners sequentially
- **`import-design-system-dialog.tsx`**: Hooked event handlers for
tracking, typed error handling for validation failures
- **`tracking.ts`** (new): Mixpanel wrapper for import/export events (7
events total)
- **`export-notifications.ts`** (new): Toast notifications for export
states (in-progress, success, failure w/ retry)
- **`download.ts`** (new): Creates temp anchor element to trigger
browser download
- **`global-styles-import-listener.tsx`** (classes): Falls back to
server reload when event has no payload
- **`global-styles-import-listener.tsx`** (variables): Simplified to
always call `service.load()`, removed manual merge logic
- **`use-variables-manager-state.ts`**: Listens to
`STORAGE_UPDATED_EVENT`, resets dirty/deleted state on external updates
- **`storage.ts`**: Exported `STORAGE_UPDATED_EVENT` constant, renamed
internal event dispatch
- **`variables-resource.ts`**: Updated MCP listener to use new
`STORAGE_UPDATED_EVENT` constant
- **`events-config.js`**: Added `design_system` analytics event names (7
events)
- **Tests**: Full coverage for import/export flows, validation failures,
retry logic, mutation state, storage events

## 3. How It Works

**Import flow:** User clicks "Import" → opens dialog → selects file →
chooses conflict strategy ("keep"/"replace") → backend validates zip →
uploads to `/upload` (returns session) → POSTs to `/import` with
session+strategy → backend returns runner names → runs each runner
(`/import-runner`) → reloads document → dispatches
`GLOBAL_STYLES_IMPORTED_EVENT` → global-classes/variables listeners
reload from storage.

**Export flow:** User clicks "Export" → POST to `/export` with `{
include: ['settings'], customization: { settings: { theme: false,
classes: true, variables: true }}}` → backend returns base64-encoded zip
→ decode to Blob → trigger browser download via temporary anchor
element.

**Key decision:** Import runs runners sequentially (not parallel)
because backend may have dependencies between `global-classes` and
`global-variables`. Only supported runners are executed; others are
filtered out client-side.

**Storage sync:** Variables/classes now listen to
`STORAGE_UPDATED_EVENT` instead of custom events. Variables manager
resets dirty state when storage updates externally (e.g., from import),
preventing stale UI state bugs.

## 4. Risks

**Timeout on large imports/exports:** 20s timeout
(`IMPORT_REQUEST_TIMEOUT_MS`, `EXPORT_REQUEST_TIMEOUT_MS`) may be too
low for large design systems. Backend should enforce reasonable limits,
but consider user feedback on timeouts before adjusting.

**Base64 decode failure:** `base64ToZipBlob` uses `atob()` which can
throw on invalid base64. Currently caught by generic try/catch in
`useExportRequest`, but errors are opaque to users—only shows generic
"export failed" toast. Backend should validate export response
structure.

**Runner filtering:** Client filters runners to `['global-classes',
'global-variables']`. If backend adds new required runners later,
imports will silently skip them. Document this contract or make
filtering server-driven.

**Race condition in retry logic:** Import failure retry checks
`isMutating()` before reopening dialog to prevent double-imports, but
there's a small window between mutation settling and check completing.
Low probability but could result in duplicate imports if user
spam-clicks retry.

_Generated by LinearB AI and added by gitStream._
<sub>AI-generated content may contain inaccuracies. Please verify before
using.
💡 **Tip:** You can customize your AI Description using **Guidelines**
[Learn
how](https://docs.gitstream.cm/automation-actions/#describe-changes)</sub>
<!--end_gitstream_placeholder-->


[ED-23880]:
https://elementor.atlassian.net/browse/ED-23880?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: ElementorBot <48412871+elementorbot@users.noreply.github.com>
… hang [ED-23093] (elementor#35730)

## Summary

`Global_Classes_Parser::parse_items()` rebuilt a `Style_Parser` (via
`Style_Schema::get()`) on every call. With the chunked global-classes
export (elementor#35687) it's invoked once per class, and the resulting
schema/parser construction explodes on large kits — turning a kit export
of ~1k classes into a server hang.

## PR Type
- [x] Bugfix

## Description

* Memoize the `Style_Parser` per `Global_Classes_Parser` instance, so
the style schema is built once regardless of how many `parse_items()`
calls (or items) it processes.
* Behaviour is unchanged for existing call sites that do
`Global_Classes_Parser::make()->parse(...)` once — they get a fresh
instance and a single schema build, exactly as before.
* Fix lives in `Global_Classes_Parser` rather than the export runner so
every consumer of `parse_items()` benefits, and the export runner stays
decoupled from `Style_Parser` / `Style_Schema`.

### Why the export was the only visible symptom

The chunked export streams classes via
`Global_Classes_Repository::each_item()` and was calling `parse_items( [
id => class_data ] )` once per class, building a fresh `Style_Parser`
(and triggering many internal `Style_Schema::get()` calls during
parsing) every time. With ~1k classes this was ~127k
`Style_Schema::get()` calls and the request never finished. Other call
sites only call `parse(...)` once per request, so they were unaffected.

## Runtime evidence

Reproduced locally with a kit seeded to 999 global classes (as in the
original PR's test instructions).

| | Before | After |
|---|---|---|
| `Style_Schema::get()` calls during export | 127,800+ (still going at
113s) | 1 |
| `each_item` loop completion | never (timed out / hung) | 2.12s (998
classes, avg 1.66ms/class) |

## Test instructions

1. Seed a site with ~1000 global classes
(`scratch/seed-global-classes.php`).
2. Trigger a kit export from Tools → Export Kit including site settings.
3. Verify the export completes in seconds (was hanging on `main`).
4. Re-import the resulting kit and verify all classes appear correctly.

## Quality assurance

- [x] I have tested this code to the best of my abilities
- [ ] I have added unittests to verify the code works as intended
- [ ] Docs have been added / updated (for bug fixes / features)

Fixes ED-23093

Made with [Cursor](https://cursor.com)
<!--start_gitstream_placeholder-->
### ✨ PR Description
## 1. Problem & Context

Fixes ED-23093 export hang caused by repeated `Style_Schema::get()`
calls inside a loop. Each export item was instantiating a new
`Style_Parser`, making schema retrieval O(n) instead of O(1).
Memoization moves this overhead outside the loop.

## 2. What Changed (Where)

- `modules/global-classes/global-classes-parser.php`: Added private
`$style_parser` property and lazy-loading getter to cache `Style_Parser`
instance across `parse_items()` iterations.

## 3. How It Works

`parse_items()` now calls `get_style_parser()` once before the loop
instead of instantiating `Style_Parser` per item. Lazy initialization
pattern ensures the parser is created only when needed and reused for
all subsequent items in the same request lifecycle.

## 4. Risks

None. Change is internal to the class with no API surface modifications.
The memoized instance lives only for the request duration via the parser
object lifecycle.

_Generated by LinearB AI and added by gitStream._
<sub>AI-generated content may contain inaccuracies. Please verify before
using.
💡 **Tip:** You can customize your AI Description using **Guidelines**
[Learn
how](https://docs.gitstream.cm/automation-actions/#describe-changes)</sub>
<!--end_gitstream_placeholder-->

Co-authored-by: Cursor <cursoragent@cursor.com>
Events changes at Angie entry points

<!--start_gitstream_placeholder-->
### ✨ PR Description
## 1. Problem & Context

Adding analytics instrumentation to track Angie AI widget entry points
across the editor. Establishes baseline metrics for CTA visibility,
clicks, install flows, and abandonment patterns.

## 2. What Changed (Where)

| File | Change |
|------|--------|
| `use-action-props.ts` | Added `useEffect` hook to fire
`ai_widget_cta_viewed` on mount when Angie unavailable; changed
entry_point from `'top_bar'` → `'top_bar_icon'` |
| `create-widget.tsx` | Renamed event constant `ANGIE_CTA_CLICKED_EVENT`
→ `AI_WIDGET_CTA_CLICKED_EVENT`; added `angie_install_completed` and
`angie_install_abandoned` tracking with state-aware abandon_step logic |
| `use-action-props.test.ts` | Added mocks for `trackEvent`; updated
entry_point assertions; added 2 new tests for CTA viewed tracking |
| `create-widget.test.tsx` | New 304-line test file covering all 4 event
scenarios with install state transitions |

## 3. How It Works

User triggers AI widget CTA → `ai_widget_cta_viewed` fires on render (if
Angie missing) → user clicks → `ai_widget_cta_clicked` fires with
entry_point and install status → on install: `angie_install_started` →
success triggers `angie_install_completed` OR close/error triggers
`angie_install_abandoned` with context-aware abandon_step
('install_modal' vs 'install_error'). Entry point threaded through
entire flow for attribution.

## 4. Risks

Entry point string now hardcoded as `'top_bar_icon'` — any future entry
points require code changes rather than configuration. No validation
that event names match backend schema; typos silently fail. Install
abandonment logic relies on accurate `installState` enum tracking —
should verify state transitions can't be missed under async race
conditions.

_Generated by LinearB AI and added by gitStream._
<sub>AI-generated content may contain inaccuracies. Please verify before
using.
💡 **Tip:** You can customize your AI Description using **Guidelines**
[Learn
how](https://docs.gitstream.cm/automation-actions/#describe-changes)</sub>
<!--end_gitstream_placeholder-->

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: ElementorBot <48412871+elementorbot@users.noreply.github.com>
elementor#35743)

## PR Checklist
<!-- 
Please check if your PR fulfills the following requirements:
**Filling out the template is required.** Any pull request that does not
include enough information to be reviewed in a timely manner may be
closed at the maintainers' discretion.
 -->
- [ ] The commit message follows our guidelines:
https://github.com/elementor/elementor/blob/master/.github/CONTRIBUTING.md


## PR Type
What kind of change does this PR introduce?
<!-- Please check the one that applies to this PR using "x" with no
spaces eg: [x]. -->
- [ ] Bugfix
- [ ] Feature
- [ ] Code style update (formatting, local variables)
- [ ] Refactoring (no functional changes, no api changes)
- [ ] Build related changes
- [ ] CI related changes
- [ ] Documentation content changes
- [ ] Other... Please describe:

## Summary

This PR can be summarized in the following changelog entry:

*

## Description
An explanation of what is done in this PR

*

## Test instructions
This PR can be tested by following these steps:

*

## Quality assurance

- [ ] I have tested this code to the best of my abilities
- [ ] I have added unittests to verify the code works as intended
- [ ] Docs have been added / updated (for bug fixes / features)

Fixes #

<!--start_gitstream_placeholder-->
### ✨ PR Description
## 1. Problem & Context

Removes hardcoded model preferences from 7 MCP tools across
styling/design modules to rely on system defaults, standardizing model
selection behavior rather than forcing specific model hints per tool.

## 2. What Changed (Where)

| File | Change |
|------|--------|
| build-composition/tool.ts | Removed `modelPreferences` with
claude-sonnet-4-5 hint |
| configure-element/tool.ts | Removed `modelPreferences` with model
hints + priority weights |
| get-element-config/tool.ts | Removed `modelPreferences` with
intelligence/speed priorities |
| mcp-apply-unapply-global-classes.ts | Removed identical
`modelPreferences` from 2 tool definitions |
| mcp-get-global-class-usages.ts | Removed `modelPreferences` with
priorities |
| mcp-manage-global-classes.ts | Removed `modelPreferences` with
inverted priorities |
| manage-variable-tool.ts | Removed `modelPreferences` with balanced
priorities |

## 3. How It Works

Tools now use registry-level or global default preferences instead of
tool-specific overrides. No functional changes to handlers or
schemas—purely configuration-layer simplification.

## 4. Risks

None identified. Removal of explicit preferences delegates to defaults;
no behavioral regression if defaults are properly configured upstream.
Verify registry defaults cover all priority scenarios previously set
per-tool.

_Generated by LinearB AI and added by gitStream._
<sub>AI-generated content may contain inaccuracies. Please verify before
using.
💡 **Tip:** You can customize your AI Description using **Guidelines**
[Learn
how](https://docs.gitstream.cm/automation-actions/#describe-changes)</sub>
<!--end_gitstream_placeholder-->

---------

Co-authored-by: Omer Israeli <94297319+Omerisra6@users.noreply.github.com>
…lementor#35748)

## PR Checklist
<!-- 
Please check if your PR fulfills the following requirements:
**Filling out the template is required.** Any pull request that does not
include enough information to be reviewed in a timely manner may be
closed at the maintainers' discretion.
 -->
- [ ] The commit message follows our guidelines:
https://github.com/elementor/elementor/blob/master/.github/CONTRIBUTING.md


## PR Type
What kind of change does this PR introduce?
<!-- Please check the one that applies to this PR using "x" with no
spaces eg: [x]. -->
- [ ] Bugfix
- [ ] Feature
- [ ] Code style update (formatting, local variables)
- [ ] Refactoring (no functional changes, no api changes)
- [ ] Build related changes
- [ ] CI related changes
- [ ] Documentation content changes
- [ ] Other... Please describe:

## Summary

This PR can be summarized in the following changelog entry:

*

## Description
An explanation of what is done in this PR

*

## Test instructions
This PR can be tested by following these steps:

*

## Quality assurance

- [ ] I have tested this code to the best of my abilities
- [ ] I have added unittests to verify the code works as intended
- [ ] Docs have been added / updated (for bug fixes / features)

Fixes #
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.