Skip to content

Conversation

@Jameswlepage
Copy link
Contributor

@Jameswlepage Jameswlepage commented Dec 19, 2025

CleanShot 2025-12-19 at 11 33 57@2x

Summary

  • Adds AI-powered ghost text autocomplete suggestions in the block editor
  • Provides context-aware completions based on surrounding content
  • Keyboard shortcuts: Tab to accept, Escape to dismiss, Ctrl+Space to manually trigger
  • Configurable completion modes (word, sentence, paragraph, smart)

Model preferences

Prioritizes fast, low-cost models for responsive completions:

  1. gpt-5.1-nanoclaude-haiku-4-5gemini-2.5-flashgpt-4o-mini

Cost estimate: $0.004 per 5-minute session with GPT-5 Nano ($0.05/hour). Falls back to Claude Haiku 4.5 if unavailable (~$1/hour).

Changes

  • includes/Abilities/Type_Ahead/ - Ability class for generating suggestions
  • includes/Experiments/Type_Ahead/ - Experiment registration with settings UI
  • src/experiments/type-ahead/ - Block editor integration with ghost text overlay
  • src/utils/run-ability.ts - Utility for invoking abilities from JavaScript

Test plan

  • Enable the Type Ahead experiment in AI settings
  • Create a new post and start typing in a paragraph block
  • Verify ghost text suggestions appear after a pause
  • Press Tab to accept or Escape to dismiss suggestions
  • Test different completion modes in experiment settings
Open WordPress Playground Preview

Adds AI-powered autocomplete suggestions:
- Real-time text completion in the block editor
- Context-aware suggestions based on content
- Keyboard shortcuts for accepting suggestions
The generate_suggestion() method was calling a non-existent
get_model_preferences() method. Changed to use the shared
get_preferred_models() helper function like other abilities.
- Prioritize gpt-5.1-nano, then claude-haiku-4-5 for faster responses
- Lower ghost text z-index to avoid overlap with WordPress UI elements
@codecov
Copy link

codecov bot commented Dec 19, 2025

Codecov Report

❌ Patch coverage is 1.42857% with 414 lines in your changes missing coverage. Please review.
✅ Project coverage is 35.60%. Comparing base (7dbbc44) to head (850d097).
⚠️ Report is 20 commits behind head on develop.

Files with missing lines Patch % Lines
includes/Abilities/Type_Ahead/Type_Ahead.php 0.00% 264 Missing ⚠️
includes/Experiments/Type_Ahead/Type_Ahead.php 3.26% 148 Missing ⚠️
...cludes/Abilities/Type_Ahead/system-instruction.php 0.00% 2 Missing ⚠️
Additional details and impacted files
@@              Coverage Diff               @@
##             develop     #151       +/-   ##
==============================================
- Coverage      46.89%   35.60%   -11.30%     
- Complexity       208      263       +55     
==============================================
  Files             19       22        +3     
  Lines           1271     1691      +420     
==============================================
+ Hits             596      602        +6     
- Misses           675     1089      +414     
Flag Coverage Δ
unit 35.60% <1.42%> (-11.30%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds an AI-powered type-ahead experiment that provides inline ghost text autocomplete suggestions in the WordPress block editor. Users can accept suggestions using keyboard shortcuts (Tab for full, Ctrl+Right for word/sentence, Escape to dismiss). The feature includes configurable completion modes and targets fast, low-cost AI models for responsive performance.

Key changes:

  • New Type Ahead ability with structured input/output schemas and caching
  • React-based block editor integration with ghost text overlay and keyboard handling
  • Experiment registration with comprehensive settings UI (mode, delay, confidence, max words)

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 14 comments.

Show a summary per file
File Description
webpack.config.js Adds entry point for the type-ahead experiment bundle
src/utils/run-ability.ts New utility for safe ability execution with REST API fallback
src/experiments/type-ahead/style.scss Styles for ghost text overlay positioning and appearance
src/experiments/type-ahead/index.tsx Main React component with DOM tracking, caret monitoring, and keyboard handlers
includes/Experiments/Type_Ahead/Type_Ahead.php Experiment registration, settings, and asset enqueuing
includes/Experiment_Loader.php Registers Type_Ahead experiment in default experiments list
includes/Abilities/Type_Ahead/system-instruction.php AI prompt instruction for generating inline completions
includes/Abilities/Type_Ahead/Type_Ahead.php Ability implementation with AI client integration and caching
docs/experiments/type-ahead.md Documentation for hooks, data flow, and testing procedures

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

};

lookup();
const interval = window.setInterval( lookup, 750 );
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This interval-based polling (every 750ms) to find block DOM elements is inefficient and could impact performance, especially with many blocks. Consider using a MutationObserver to detect when the block element is added to the DOM instead of continuous polling.

Copilot uses AI. Check for mistakes.
Comment on lines +451 to +452
if ( mb_strlen( $value ) > self::CONTEXT_LIMIT ) {
return mb_substr( $value, -1 * self::CONTEXT_LIMIT );
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The truncation takes the last CONTEXT_LIMIT characters using a negative offset. For multi-byte UTF-8 characters, this could potentially cut in the middle of a character sequence. While 'mb_substr' should handle this correctly, verify that 'normalize_content' doesn't introduce any issues with multi-byte characters at boundaries.

Suggested change
if ( mb_strlen( $value ) > self::CONTEXT_LIMIT ) {
return mb_substr( $value, -1 * self::CONTEXT_LIMIT );
$length = mb_strlen( $value, 'UTF-8' );
if ( $length > self::CONTEXT_LIMIT ) {
$start = $length - self::CONTEXT_LIMIT;
return mb_substr( $value, $start, null, 'UTF-8' );

Copilot uses AI. Check for mistakes.
let hasShownFallbackNotice = false;

const getAbilityClient = () =>
( window as Record< string, any > )?.wp?.abilities ?? null;
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type casting uses 'as Record<string, any>' and 'as any' which bypasses TypeScript's type safety. Consider defining a proper interface for the window.wp.abilities object to maintain type safety throughout the codebase.

Copilot uses AI. Check for mistakes.
Comment on lines +77 to +81
: addQueryArgs(
`/wp-abilities/v1/abilities/${ ability }/run`,
{
input: normalizedInput,
}
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When using 'GET' or 'DELETE' methods with input, the input is added as query parameters via 'addQueryArgs'. However, complex objects or arrays in 'input' might not serialize correctly as query strings. Consider validating that input is serializable for GET/DELETE requests, or document that these methods only support simple scalar values.

Copilot uses AI. Check for mistakes.
Comment on lines +387 to +389
if ( str_starts_with( $clean, '```' ) ) {
$clean = preg_replace( '/^```[a-zA-Z0-9_-]*\s*/', '', $clean ) ?? $clean;
if ( str_contains( $clean, '```' ) ) {
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function uses 'str_starts_with' and 'str_contains', which require PHP 8.0+. However, there's no minimum PHP version check visible in this file. If the codebase supports PHP 7.x, this will cause fatal errors. Either add a PHP version check in the file or ensure polyfills are in place.

Copilot uses AI. Check for mistakes.
Comment on lines +762 to +769
if (
( event.metaKey || event.ctrlKey ) &&
event.code === 'Space'
) {
event.preventDefault();
setRequestNonce( ( prev ) => prev + 1 );
scheduleFetch( true );
}
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The keyboard shortcut Ctrl+Space (lines 762-769) may conflict with system-level keyboard shortcuts. On macOS, Cmd+Space is typically used for Spotlight search, and on some Linux systems Ctrl+Space is used for input method switching. While the event.preventDefault() will stop the default, document this potential conflict and consider providing an alternative shortcut or making it configurable.

Copilot uses AI. Check for mistakes.
Comment on lines +679 to +680
const textNode = doc.createTextNode( text );
range.insertNode( textNode );
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 'createTextNode' and 'insertNode' operations use unsanitized text from the suggestion. While the suggestion goes through 'sanitize_textarea_field' on the server side (line 366), ensure the text returned from the API hasn't been tampered with during transport. Consider additional client-side sanitization before DOM insertion to prevent potential XSS if the API response is compromised.

Copilot uses AI. Check for mistakes.
$clean = trim( $raw );

if ( str_starts_with( $clean, '```' ) ) {
$clean = preg_replace( '/^```[a-zA-Z0-9_-]*\s*/', '', $clean ) ?? $clean;
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The regex pattern '/^[a-zA-Z0-9_-]*\s*/' allows matching code blocks, but the hyphen in the character class should be escaped or placed at the start/end to avoid being interpreted as a range. While this works because '_-' is a valid range in ASCII, it's better practice to write it as '/^[a-zA-Z0-9_\-]\s/' or '/^```[a-zA-Z0-9-_]\s/' for clarity.

Suggested change
$clean = preg_replace( '/^```[a-zA-Z0-9_-]*\s*/', '', $clean ) ?? $clean;
$clean = preg_replace( '/^```[a-zA-Z0-9_\\-]*\s*/', '', $clean ) ?? $clean;

Copilot uses AI. Check for mistakes.

Asset_Loader::localize_script(
'type_ahead',
'TypeAheadData',
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The localized script data uses 'TypeAheadData' as the object name, but the TypeScript code expects 'aiTypeAheadData' (line 828 in index.tsx checks 'window.aiTypeAheadData'). This mismatch will cause the feature to fail silently after the 5-second polling timeout. The object name should be 'aiTypeAheadData' to match the frontend expectation.

Suggested change
'TypeAheadData',
'aiTypeAheadData',

Copilot uses AI. Check for mistakes.
Comment on lines +308 to +309
private function build_cache_key( string $block_content, string $preceding_text, string $mode, int $max_words ): string {
return 'type_ahead_' . md5( $block_content . '|' . $preceding_text . '|' . $mode . '|' . $max_words );
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cache key includes 'preceding_text' which will cause cache misses for identical content at different cursor positions. This defeats the purpose of caching when users move the cursor backward and forward in the same text. Consider whether cursor position should actually be part of the cache key, or if a more semantic key would be more effective.

Copilot uses AI. Check for mistakes.
@jeffpaul
Copy link
Member

@jeffpaul
Copy link
Member

  • Reminder for us to add a screenshot gif and entry in the readme.txt for this feature before merging/releasing.

@JasonTheAdams JasonTheAdams removed their request for review December 21, 2025 05:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Needs review

Development

Successfully merging this pull request may close these issues.

2 participants