Skip to content

feat: Expose autocomplete triggers and text insertion utility #165

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Aug 12, 2025

Conversation

dcalhoun
Copy link
Member

@dcalhoun dcalhoun commented Aug 6, 2025

What?

Broadcast @ and + keystrokes triggering autocompletion. Expose bridge method for appending text strings at cursor.

Why?

Close CMM-453.

Enable native host apps to respond to common autocomplete triggers.

How?

  • Register custom editor autocompletion callbacks via the editor.Autocomplete.completers filter.
  • Send new onAutocompleterTriggered bridge event on autocompletion.
  • Expose appendTextAtCursor bridge method allowing hosts to insert suggestions.
  • Fix duplicative @wordpress/hooks namespace in the remote editor that results in discarded filters.

Testing Instructions

See sibling PRs:

Accessibility Testing Instructions

N/A, user-facing changes in sibling PRs.

Screenshots or screencast

N/A, visual changes in sibling PRs.

@dcalhoun dcalhoun added the [Type] Enhancement A suggestion for improvement. label Aug 6, 2025
@dcalhoun dcalhoun force-pushed the feat/broadcast-at-and-plus-keystrokes branch from 82b8831 to 62d1311 Compare August 6, 2025 19:53
Base automatically changed from refactor/improve-debugging to trunk August 6, 2025 20:26
@dcalhoun dcalhoun force-pushed the feat/broadcast-at-and-plus-keystrokes branch from 62d1311 to 5985701 Compare August 6, 2025 20:26
@dcalhoun dcalhoun changed the title feat: Broadcast @ and + keystrokes feat: Expose autocomplete triggers and text insertion utility Aug 7, 2025
Bundling the `@wordpress/hook` package meant two namespaces existed:

1. The module bundled by Vite;
2. And the `wp.hooks` global provided by the remote site scripts.

Hooks registered by GutenbergKit used the former, but the editor runtime
relied upon the latter. Therefore, the GutenbergKit hooks were never
applied. Using a single namespace resolves this issue.

Originally, bundling select modules was done in service of using
`@wordpress/api-fetch` for fetching editor assets from a remote site.
Now that the native bridge authenticates and performs this request, we
no longer need these bundled modules. We can rely upon the globals
provided by the remote site assets.
Enable Android apps to:

1. Receive notifications when autocompleters are triggered (@ and +
   symbols)
2. Programmatically insert text at the cursor position in the editor
Avoid triggering autocompleter when modifying string prefixed with
trigger--e.g., deleting an existing string.

Behavior:
- @<cursor> triggers the event (filterValue is empty)
- @query<cursor> does not trigger (filterValue contains "query")
- Same pattern applies to + symbol

This ensures native apps only receive the initial trigger notification,
not continuous events while the user is typing.
The autocompletion should not disrupt typing strings like an
email--e.g., `[email protected]`.
@dcalhoun dcalhoun force-pushed the feat/broadcast-at-and-plus-keystrokes branch from 9edb626 to f94ca59 Compare August 7, 2025 18:27
We do not need special handling of side-effect modules. The regex change
ensures they are matched by the Vite plugin. The logic then removes the
side-effect module import entirely. This is appropriate, as the remote
editor assets should already include/load the side-effect modules. E.g.,
the `format-library` side effects are included in remote editor assets.
By ignoring CSS files within the module import regex, we loaded the CSS
twice: once within the bundle, again from the remote site assets. We now
match CSS imports and remove them as side-effect imports.

Inlined CSS files are treated differently. We bundle these imports so
that the JavaScript can act upon the imported string content.
@dcalhoun dcalhoun requested a review from Copilot August 8, 2025 12:04
Copy link

@Copilot 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 exposes autocomplete triggers for @ and + symbols and adds a utility for text insertion at cursor position. It enables native host apps to respond to common autocomplete triggers by registering custom editor autocompletion callbacks and sending bridge events when autocompletion is triggered.

Key changes include:

  • Added custom autocompleter hooks for @ and + symbols that trigger bridge events
  • Exposed appendTextAtCursor bridge method for inserting text suggestions
  • Fixed duplicative @wordpress/hooks namespace issues in the remote editor

Reviewed Changes

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

Show a summary per file
File Description
vite.config.remote.js Refactored external module handling and WordPress import transformation logic
src/utils/videopress-bridge.js Changed logging from info to debug level for initialization messages
src/utils/remote-editor.js Removed hardcoded exclusion of api-fetch from remote loading
src/utils/fetch.js Added basic fetch implementation based on @wordpress/api-fetch
src/utils/bridge.js Added autocompleter trigger bridge method and updated asset fetching to use custom fetch
src/remote.jsx Restructured initialization order to load api-fetch after assets
src/components/editor/use-plus-autocompleter.js New hook for handling + symbol autocomplete triggers
src/components/editor/use-host-bridge.js Added appendTextAtCursor functionality for text insertion
src/components/editor/use-at-autocompleter.js New hook for handling @ symbol autocomplete triggers
src/components/editor/index.jsx Integrated the new autocompleter hooks
ios/Sources/GutenbergKit/Sources/EditorViewControllerDelegate.swift Added delegate method for autocompleter triggers
ios/Sources/GutenbergKit/Sources/EditorViewController.swift Implemented appendTextAtCursor method and message handling
ios/Sources/GutenbergKit/Sources/EditorJSMessage.swift Added message types for autocompleter events
android/Gutenberg/src/main/java/org/wordpress/gutenberg/GutenbergView.kt Added autocompleter listener and appendTextAtCursor method

The previous implementation might lead to data loss by stripping
unsupported characters.
Comment on lines +304 to +305
// Use our fetch utility, as we have not yet loaded the `wp.apiFetch` utility
return await basicFetch( url, {
Copy link
Member Author

@dcalhoun dcalhoun Aug 8, 2025

Choose a reason for hiding this comment

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

basicFetch is a simplified version of the defaultHandler found in the @wordpress/api-fetch module. It aims to provide rudimentary request processing for this one-off request performed when initializing the remote editor.

This utility was added to allow us to stop bundling the @wordpress/api-fetch module with GutenbergKit. This module and its dependencies were bundled, whereas all other WordPress modules were fetched from the remote site. This became problematic when multiple copies of a module were loaded—e.g., duplicate @wordpress/hooks, which is a @wordpress/api-fetch dependency.

With this change, we no longer bundle any WordPress module, meaning that all modules function the same way: loaded from the remote site.

Comment on lines -110 to -114
/**
* Locally-sourced Gutenberg packages excluded from remote loading to avoid
* conflicts.
*/
const localGutenbergPackages = [ 'api-fetch', ...disallowedPackages ];
Copy link
Member Author

Choose a reason for hiding this comment

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

@wordpress/api-fetch and its dependencies are now loaded from the remote site.

Comment on lines +69 to +74
return import( './utils/api-fetch' ).then(
( { initializeApiFetch: _initializeApiFetch } ) => {
_initializeApiFetch();
return assetsResult;
}
);
Copy link
Member Author

Choose a reason for hiding this comment

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

The remote editor now lazy loads this API setup utility to ensure the window.wp.apiFetch global (remote site's copy) is available before executing.

const magicString = new MagicString( code );
let hasReplacements = false;

// Match WordPress and React JSX runtime import statements
const regex =
/import\s*(?:{([^}]+)}\s*from)?\s*['"](@wordpress\/([^'"]+)|react\/jsx-runtime)['"];/g;
/import\s*(?:(?:(\w+)|{([^}]+)})\s*from\s*)?['"](@wordpress\/[^'"]+|react\/jsx-runtime)['"];/g;
Copy link
Member Author

Choose a reason for hiding this comment

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

Fix erroneously overlooked "default" import matching.

Comment on lines +70 to +72
if ( module.match( /\.css\?inline$/ ) ) {
continue; // Exclude inlined CSS files from externalization
}
Copy link
Member Author

Choose a reason for hiding this comment

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

Now that default imports matching is fixed, we must exclude inlined CSS files from externalization. There are no globals that represent inlined CSS strings.

return (
!! externalDefinition &&
! id.match( /\.css(?:\?inline)?$/ ) &&
! [ 'apiFetch', 'i18n', 'url', 'hooks' ].includes(
Copy link
Member Author

Choose a reason for hiding this comment

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

Stop bundling @wordpress/api-fetch and its dependencies.

@dcalhoun dcalhoun marked this pull request as ready for review August 8, 2025 12:51
@nbradbury nbradbury self-assigned this Aug 8, 2025
@nbradbury
Copy link
Contributor

@dcalhoun This looks good from the Android side. I'm not sure if you also want a review from @crazytonyli . If not, let me know and I'll approve it.

@dcalhoun
Copy link
Member Author

dcalhoun commented Aug 8, 2025

Thank you, @nbradbury. I appreciate you sharing your feedback.

I'll await @crazytonyli's review and approval before merging.

Copy link
Contributor

@crazytonyli crazytonyli left a comment

Choose a reason for hiding this comment

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

I don't fully understand the JS code, but the Swift code looks good to me.

@dcalhoun
Copy link
Member Author

I don't fully understand the JS code, but the Swift code looks good to me.

Thank you for the review, @crazytonyli. If desired, please feel free to leave specific questions regarding unclear JavaScript changes—both on this PR or future PRs. I am happy to provide additional explanation.

@dcalhoun dcalhoun merged commit baf41e6 into trunk Aug 12, 2025
11 checks passed
@dcalhoun dcalhoun deleted the feat/broadcast-at-and-plus-keystrokes branch August 12, 2025 17:46
@crazytonyli
Copy link
Contributor

@dcalhoun I may need a crash course on the Gutenberg library and vite 😅 .

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Type] Enhancement A suggestion for improvement.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants