-
Notifications
You must be signed in to change notification settings - Fork 3
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
Conversation
82b8831
to
62d1311
Compare
Enable the host to respond to autocomplete events.
Debouncing ensures the native suggestion UI does not reappear after selecting an suggestion.
62d1311
to
5985701
Compare
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]`.
9edb626
to
f94ca59
Compare
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.
There was a problem hiding this 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 |
android/Gutenberg/src/main/java/org/wordpress/gutenberg/GutenbergView.kt
Show resolved
Hide resolved
The previous implementation might lead to data loss by stripping unsupported characters.
// Use our fetch utility, as we have not yet loaded the `wp.apiFetch` utility | ||
return await basicFetch( url, { |
There was a problem hiding this comment.
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.
/** | ||
* Locally-sourced Gutenberg packages excluded from remote loading to avoid | ||
* conflicts. | ||
*/ | ||
const localGutenbergPackages = [ 'api-fetch', ...disallowedPackages ]; |
There was a problem hiding this comment.
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.
return import( './utils/api-fetch' ).then( | ||
( { initializeApiFetch: _initializeApiFetch } ) => { | ||
_initializeApiFetch(); | ||
return assetsResult; | ||
} | ||
); |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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.
if ( module.match( /\.css\?inline$/ ) ) { | ||
continue; // Exclude inlined CSS files from externalization | ||
} |
There was a problem hiding this comment.
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( |
There was a problem hiding this comment.
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.
android/Gutenberg/src/main/java/org/wordpress/gutenberg/GutenbergView.kt
Show resolved
Hide resolved
@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. |
Thank you, @nbradbury. I appreciate you sharing your feedback. I'll await @crazytonyli's review and approval before merging. |
There was a problem hiding this 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.
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 I may need a crash course on the Gutenberg library and vite 😅 . |
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?
editor.Autocomplete.completers
filter.onAutocompleterTriggered
bridge event on autocompletion.appendTextAtCursor
bridge method allowing hosts to insert suggestions.@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.