Prototype : Implement slug generation feature with UI and tests - #897
Prototype : Implement slug generation feature with UI and tests#897milindmore22 wants to merge 11 commits into
Conversation
…ate setup in settings spec, and update gitignore
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## develop #897 +/- ##
=============================================
+ Coverage 79.68% 79.83% +0.15%
- Complexity 2460 2512 +52
=============================================
Files 104 107 +3
Lines 9955 10178 +223
=============================================
+ Hits 7933 8126 +193
- Misses 2022 2052 +30
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
dkotter
left a comment
There was a problem hiding this comment.
Overall this looks good, tested well. I've left a handful of comments but let me know if there's questions on any of those. In addition, we'll want to ensure we add proper documentation that matches all of our other experiment docs.
| protected function guideline_categories(): array { | ||
| return array( 'site', 'copy' ); | ||
| } |
There was a problem hiding this comment.
Open to thoughts here but not sure if generating a slug benefits from these guidelines. May be fine to just not pass these in
| $prompt_input .= "\n\n<additional-context>{$context}</additional-context>"; | ||
| } | ||
|
|
||
| $number_of_suggestions = (int) apply_filters( 'wpai_slug_generation_number_of_suggestions', (int) $args['number_of_suggestions'] ); |
There was a problem hiding this comment.
We should add a filter docblock here
There was a problem hiding this comment.
Also, we should probably have some validation here after the filter runs. What if someone uses the filter and doesn't return a number, or returns something like 1000 or 0? Ideally we set a minimum and maximum (1 to 10?) and ensure this is always an integer between those two
There was a problem hiding this comment.
If we do the above, we can also update the schema with minimum and maximum values
| ); | ||
| } | ||
|
|
||
| // Parse the output lines into clean, sanitized WordPress slugs. |
There was a problem hiding this comment.
Any reason to approach it this way instead of using structured outputs? Typically that will work better than just hoping the LLM returns things in the structure we want and avoids us having to do this type of parsing
| - Output exactly %d suggestions, one per line. | ||
| - Do not include any markdown, bullets, numbering, or formatting. | ||
| - Output only the raw slug text. Respond directly without preamble. Do not wrap the output in quotes. Do not add closing remarks or follow-up questions.', | ||
| $wpai_slug_num |
There was a problem hiding this comment.
Would we want a line in here asking it to match the language of the title given? We do this for other abilities to ensure the content returned matches the language of the content passed in
| return; | ||
| } | ||
|
|
||
| // Enqueue frontend scripts, styles, and pass localized configuration settings to window. |
There was a problem hiding this comment.
This claims "frontend" but this only loads in the admin
| aria-labelledby={ labelId } | ||
| > | ||
| { suggestions.map( ( slug, index ) => ( | ||
| <Button |
There was a problem hiding this comment.
Should these be buttons? Seems like RadioControl or ToggleGroupControl will be better for accessibility
|
|
||
| <TextControl | ||
| label={ __( 'Selected slug', 'ai' ) } | ||
| value={ selectedSlug } |
There was a problem hiding this comment.
There's a bug here on regeneration. On initial generation, seems this input is set to the first slug suggestion in the modal. If I regenerate in the modal, no slug is selected and this input shows the previous first slug
| * Helper to fetch localized settings passed from PHP to the global window object. | ||
| */ | ||
| const getSettings = (): SlugGenerationData => { | ||
| const settings = ( window as any ).aiSlugGenerationData ?? {}; |
There was a problem hiding this comment.
We've been trying to move away from ever use as any. I think we can move this to the types file to avoid doing this and update this across all three files
| findAndAttach(); | ||
|
|
||
| // Create observer to listen for sidebar renders/toggles and popover display. | ||
| observer = new MutationObserver( () => { |
There was a problem hiding this comment.
So I understand why we're doing this but I think this could cause some performance issues, as this is constantly running. Could we maybe add debouncing or narrowing the observed subtree to just the sidebar or gating on a store selector for the URL popover being open? If there's a way to narrow the scope here so this isn't constantly firing for any and all changes, that would be great
| 'supports' => array( 'editor' ), | ||
| ) | ||
| ); | ||
| set_current_screen( 'no_title_cpt' ); |
There was a problem hiding this comment.
We need to ensure we clean up the screen in tearDown to avoid this leaking into other tests
What?
Closes #876
This PR introduces the Slug Generation experimental feature (
ai/slug-generationability & experiment). It provides AI-assisted permalink suggestions directly inside the WordPress Block Editor based on post title, content, and additional context.Open Questions
Both entry points (sidebar popover + pre-publish panel) are currently supported in prototype.
WordPress core natively enforces slug uniqueness on post save, not sure if we should cover this in Slug generation
Not covered in prototype, but we can cover if required.
Why?
Creating concise, keyword-rich, and SEO-friendly permalink slugs often requires extra effort from content creators. This feature streamlines the publishing workflow by automatically suggesting URL-safe slugs within the permalink inspector popover and the pre-publish panel.
How?
Abilities API Integration:
WordPress\AI\Abilities\Slug_Generation\Slug_GenerationextendingAbstract_Ability.system-instruction.php) enforcing URL-safe, lowercase, hyphenated slug generation.sanitize_title().Experiment Framework:
WordPress\AI\Experiments\Slug_Generation\Slug_GenerationunderExperiment_Category::EDITOR.Block Editor UI Components:
.editor-post-url).SlugGenerationModal): Renders a modal interface allowing users to preview, manually edit, select, or regenerate slug options.SlugPrePublishPanel): Adds aPluginPrePublishPanelsidebar card to review or change the post slug prior to publishing.Automated Tests:
Slug_GenerationTest.php) for ability execution, argument validation, and permissions.helpers.ts) for reliable REST API save synchronization during bulk toggle tests.Use of AI Tools
AI assistance: Yes
Tool(s): Antigravity AI
Model(s): Gemini 3.6 Flash
Used for: Initial implementation of ability and experiment classes, React UI components, unit/integration tests, and code documentation. Final changes reviewed and verified by human developer.
Testing Instructions
Screenshots or screencast
Screen.Recording.2026-07-24.at.4.59.49.PM.2.mov
Changelog Entry