Skip to content

Connectors: Show filesystem credentials modal on plugin install - #78367

Open
westonruter wants to merge 13 commits into
trunkfrom
fix/65223-connectors-fs-credentials
Open

westonruter wants to merge 13 commits into
trunkfrom
fix/65223-connectors-fs-credentials

Conversation

@westonruter

@westonruter westonruter commented May 15, 2026 •

Copy link
Copy Markdown
Member

Important

The corresponding wordpress-develop backport PR has not been created yet. Once the approach is validated here and approved, the core PR will be created.

Edited by @t-hamano: Core backport PR: WordPress/wordpress-develop#13953

Summary

Fixes the Connectors install flow when FS_METHOD is not direct — for example, ftpext / ftpsockets. The current behavior is a generic "Failed to install plugin for X." snackbar with no path forward; Plugins > Add Plugin already handles the same case by showing the standard "Connection Information" modal.

This PR mirrors that flow on the Connectors screen:

  • lib/experimental/connectors/load.php — on the Connectors page only, enqueue wp.updates, print wp_print_request_filesystem_credentials_modal() in the footer, and expose filesystemCredentialsRequired via the script_module_data_options-connectors-wp-admin filter.
  • routes/connectors-home/use-connector-plugin.ts — when filesystemCredentialsRequired is true, route through wp.updates.installPlugin() (which knows how to surface the FTP credentials modal, collect creds, and POST them with the install). After success, activate via the existing REST flow and refresh the plugin entity. Cancelling the modal silently aborts.
  • routes/connectors-home/default-connectors.tsx — read the new flag and forward to useConnectorPlugin.

Edited by @t-hamano:

  • lib/compat/plugin/connectors.php — on the Connectors page only, enqueue wp.updates and print wp_print_request_filesystem_credentials_modal() in the footer.
  • routes/connectors-home/use-connector-plugin.ts / ai-plugin-callout.tsx — install via the REST plugin endpoint first. If it rejects with fs_unavailable, fall back to wp.updates.installPlugin(), which shows the FTP credentials modal, collects credentials, and POSTs them with the install. After success, activate via REST and refresh the plugin entity. Cancelling the modal silently aborts. Any other REST error shows the existing error notice.

Fixes Core-65223.

Screenshots

Open AI plugin is not installed yet:

Screenshot 2026-05-15 at 12 30 26

Clicking Install causes the FTP credentials modal to appear:

Screenshot 2026-05-15 at 12 26 18

Providing credentials results in the plugin being active and ready for setup:

Screenshot 2026-05-15 at 12 28 42

Test plan

Reproduces against an environment where the filesystem method requires credentials:

  • Configure environment to use an FTP filesystem. See Trac comment.
  • Ensure the AI plugin is active and at least one provider plugin (e.g. ai-provider-for-openai) is not installed.
  • Visit Settings → Connectors.
  • Click Install on the un-installed provider.
  • Confirm the standard Connection Information modal appears (matches Plugins > Add Plugin).
  • Enter valid FTP credentials and submit — the plugin installs, activates, and the API key form expands inline.
  • Re-test with invalid credentials — the modal shows the standard error and stays open.
  • Re-test with Cancel — install aborts with no error snackbar.
  • Re-test on a direct filesystem environment — the install proceeds via REST as before, no modal.

Use of AI Tools

Claude Opus 4.7 was used to create the PR and validate the fix.

Claude prompt and conversation summary

Prompt

I need to work on fixing a bug in WordPress which is seen in Trac at https://core.trac.wordpress.org/ticket/65223

The fix needs to be done in the gutenberg plugin (at src/wp-content/plugins/gutenberg).

You can reproduce the issue by going to http://localhost:8000/wp-admin/options-general.php?page=options-connectors-wp-admin in Chrome and attempting to click the Install button for OpenAI, in which case an error shows in a snackbar notification:

Failed to install plugin for OpenAI.

The fix should be added to a new branch in the gutenberg repo. Somehow the connectors screen needs to include request_filesystem_credentials() and the React components for Gutenberg need to be updated to be aware of this and to show it when needed. And then the plugin should be installed using whatever JS is involved with request_filesystem_credentials() to get a plugin installed via FTP when the user has provided the FTP credentials.

Investigation

  1. Read Trac ticket DataViews Sidebar: Display item count on DataViews sidebar #65223 — confirmed scope: install fails on Connectors page when FS_METHOD ≠ direct, while Plugins > Add Plugin handles it correctly.
  2. Traced WordPress core's existing FTP credential flow:
    • request_filesystem_credentials() + wp_print_request_filesystem_credentials_modal() in wp-admin/includes/file.php render the "Connection Information" modal.
    • wp.updates.installPlugin() (in wp-admin/js/updates.js) wraps the AJAX call, includes _fs_nonce and FTP credentials in the POST body, shows the modal when shouldRequestFilesystemCredentials is true, and fires wp-plugin-install-success/wp-plugin-install-error jQuery events.
  3. Traced the Connectors install path: the React useConnectorPlugin hook calls saveEntityRecord( 'root', 'plugin', … ), which hits WP_REST_Plugins_Controller::create_item. That controller calls is_filesystem_available(), which returns a fs_unavailable WP_Error up front whenever FS_METHOD isn't direct and no credentials are stored — so the REST endpoint can never accept FTP creds in its JSON body.
  4. Confirmed the gutenberg plugin's connectors screen is registered in lib/experimental/connectors/load.php and the per-page enqueue/render is auto-generated from packages/wp-build/templates/page-wp-admin.php.template (not safe to edit directly), so additions must hook off the connectors menu page specifically.

Implementation (commit 171d758)

Branch: fix/65223-connectors-fs-credentials in WordPress/gutenberg.

  • lib/experimental/connectors/load.php: capture the $hook_suffix from add_submenu_page() and register a load-{hook_suffix} action that

    • enqueues wp.updates, and
    • hooks admin_footer to print wp_print_request_filesystem_credentials_modal() + wp_print_admin_notice_templates().
      Also added a script_module_data_options-connectors-wp-admin filter that exposes filesystemCredentialsRequired: bool based on get_filesystem_method() and stored credentials.
  • routes/connectors-home/use-connector-plugin.ts: added a new requiresFilesystemCredentials option to the hook. When set, the install path routes through a new installPluginViaWpUpdates(slug) helper that calls wp.updates.installPlugin({ slug }) and resolves on the wp-plugin-install-success jQuery event (rejects on wp-plugin-install-error, silently aborts on credential-modal-cancel). After install succeeds, it activates via the existing REST flow and invalidates the plugin entity resolution.

  • routes/connectors-home/default-connectors.tsx: added a getRequiresFilesystemCredentials() reader on the script module data and forwarded it to useConnectorPlugin.

Verification

Tested in Chrome at http://localhost:8000/wp-admin/options-general.php?page=options-connectors-wp-admin against an environment with FS_METHOD=ftpsockets:

  1. Modal appears: clicking Install on OpenAI now opens the standard "Connection Information" dialog with Hostname / FTP Username / FTP Password / Connection Type fields — same as Plugins > Add Plugin.
  2. Full install succeeds: entered the FTP creds from the docker test env (ftp / ftpuser / ftppass), submitted, and wp plugin status ai-provider-for-openai reported Status: Active. The React UI flipped the button to Cancel and auto-expanded the API KEY entry form, matching the direct-FS path.
  3. Script module wiring: verified filesystemCredentialsRequired: true, the modal <div> present, wp.updates.installPlugin loaded, and wp.updates.shouldRequestFilesystemCredentials: true.
  4. No new lint errors on the changed files (the two pre-existing warnings are unchanged).
  5. No new console errors during the install flow (the two 404s are pre-existing — initial probes for not-yet-installed plugin entities).

After verification: deleted the installed ai-provider-for-openai plugin and the ftp_credentials option to leave the env in a re-testable state.

When `FS_METHOD` is not `direct` and FTP credentials have not been
stored, the REST `/wp/v2/plugins` endpoint rejects an install attempt
up front, surfacing only a generic "Failed to install plugin" snackbar
on the Connectors screen. Plugins > Add Plugin handles the same case
by displaying the standard "Connection Information" modal.

Print that modal on the Connectors screen, expose
`filesystemCredentialsRequired` via the script module data, and route
through `wp.updates.installPlugin()` when credentials are required so
the legacy AJAX install path supplies them. After the install
succeeds, activate via REST and refresh the plugin entity.
@westonruter westonruter added [Feature] Connectors screen Tracks connectors screen related tasks [Type] Bug An existing feature does not function as intended labels May 15, 2026
@github-project-automation github-project-automation Bot moved this to 🔎 Needs Review in WordPress 7.0 Editor Tasks May 15, 2026
@westonruter
westonruter marked this pull request as ready for review May 15, 2026 19:42
@westonruter
westonruter requested a review from spacedmonkey as a code owner May 15, 2026 19:42
Comment thread lib/experimental/connectors/load.php Outdated
@github-actions

github-actions Bot commented May 15, 2026 •

Copy link
Copy Markdown

Size Change: 0 B

Total Size: 8.21 MB

ℹ️ View Unchanged
Filename Size Change
build/modules/a11y/index.min.js 355 B 0 B
build/modules/abilities/index.min.js 42.3 kB 0 B
build/modules/block-editor/utils/fit-text-frontend.min.js 617 B 0 B
build/modules/block-library/accordion/view.min.js 595 B 0 B
build/modules/block-library/file/view.min.js 346 B 0 B
build/modules/block-library/form/view.min.js 528 B 0 B
build/modules/block-library/image/view.min.js 2.64 kB 0 B
build/modules/block-library/navigation/view.min.js 1.14 kB 0 B
build/modules/block-library/playlist/view.min.js 10.9 kB 0 B
build/modules/block-library/query/view.min.js 518 B 0 B
build/modules/block-library/search/view.min.js 498 B 0 B
build/modules/block-library/tabs/view.min.js 946 B 0 B
build/modules/boot/index.min.js 51.8 kB 0 B
build/modules/connectors/index.min.js 2.05 kB 0 B
build/modules/content-types/index.min.js 157 kB 0 B
build/modules/core-abilities/index.min.js 926 B 0 B
build/modules/edit-site-init/index.min.js 1.4 kB 0 B
build/modules/interactivity-router/full-page.min.js 451 B 0 B
build/modules/interactivity-router/index.min.js 11.6 kB 0 B
build/modules/interactivity/index.min.js 15.3 kB 0 B
build/modules/latex-to-mathml/index.min.js 56.5 kB 0 B
build/modules/latex-to-mathml/loader.min.js 131 B 0 B
build/modules/lazy-editor/index.min.js 14.2 kB 0 B
build/modules/route/index.min.js 25.2 kB 0 B
build/modules/vips/loader.min.js 127 B 0 B
build/modules/vips/worker.min.js 4.56 MB 0 B
build/modules/workflow/index.min.js 19.9 kB 0 B
build/scripts/a11y/index.min.js 1.1 kB 0 B
build/scripts/annotations/index.min.js 2.53 kB 0 B
build/scripts/api-fetch/index.min.js 3.34 kB 0 B
build/scripts/autop/index.min.js 2.21 kB 0 B
build/scripts/base-styles/index.min.js 146 B 0 B
build/scripts/blob/index.min.js 665 B 0 B
build/scripts/block-directory/index.min.js 43 kB 0 B
build/scripts/block-editor/index.min.js 379 kB 0 B
build/scripts/block-library/index.min.js 324 kB 0 B
build/scripts/block-serialization-default-parser/index.min.js 1.2 kB 0 B
build/scripts/block-serialization-spec-parser/index.min.js 3.12 kB 0 B
build/scripts/blocks/index.min.js 57.1 kB 0 B
build/scripts/commands/index.min.js 21 kB 0 B
build/scripts/components/index.min.js 263 kB 0 B
build/scripts/compose/index.min.js 11.7 kB 0 B
build/scripts/core-commands/index.min.js 4.37 kB 0 B
build/scripts/core-data/index.min.js 31.4 kB 0 B
build/scripts/customize-widgets/index.min.js 14.6 kB 0 B
build/scripts/data-controls/index.min.js 832 B 0 B
build/scripts/data/index.min.js 9.74 kB 0 B
build/scripts/date/index.min.js 23.7 kB 0 B
build/scripts/deprecated/index.min.js 784 B 0 B
build/scripts/dom-ready/index.min.js 502 B 0 B
build/scripts/dom/index.min.js 5.04 kB 0 B
build/scripts/edit-post/index.min.js 52 kB 0 B
build/scripts/edit-site/index.min.js 295 kB 0 B
build/scripts/edit-widgets/index.min.js 22.2 kB 0 B
build/scripts/editor/index.min.js 462 kB 0 B
build/scripts/element/index.min.js 5.63 kB 0 B
build/scripts/escape-html/index.min.js 622 B 0 B
build/scripts/format-library/index.min.js 13.1 kB 0 B
build/scripts/hooks/index.min.js 1.86 kB 0 B
build/scripts/html-entities/index.min.js 529 B 0 B
build/scripts/i18n/index.min.js 2.5 kB 0 B
build/scripts/is-shallow-equal/index.min.js 607 B 0 B
build/scripts/keyboard-shortcuts/index.min.js 1.65 kB 0 B
build/scripts/keycodes/index.min.js 1.6 kB 0 B
build/scripts/list-reusable-blocks/index.min.js 2.53 kB 0 B
build/scripts/media-utils/index.min.js 112 kB 0 B
build/scripts/notices/index.min.js 1.72 kB 0 B
build/scripts/nux/index.min.js 909 B 0 B
build/scripts/patterns/index.min.js 8 kB 0 B
build/scripts/plugins/index.min.js 2.18 kB 0 B
build/scripts/preferences-persistence/index.min.js 2.19 kB 0 B
build/scripts/preferences/index.min.js 3.33 kB 0 B
build/scripts/primitives/index.min.js 1.05 kB 0 B
build/scripts/priority-queue/index.min.js 1.65 kB 0 B
build/scripts/private-apis/index.min.js 1.15 kB 0 B
build/scripts/react-i18n/index.min.js 868 B 0 B
build/scripts/redux-routine/index.min.js 3.4 kB 0 B
build/scripts/reusable-blocks/index.min.js 3.14 kB 0 B
build/scripts/rich-text/index.min.js 14.1 kB 0 B
build/scripts/router/index.min.js 5.99 kB 0 B
build/scripts/server-side-render/index.min.js 1.98 kB 0 B
build/scripts/shortcode/index.min.js 1.62 kB 0 B
build/scripts/style-engine/index.min.js 2.45 kB 0 B
build/scripts/sync/index.min.js 39.6 kB 0 B
build/scripts/theme/index.min.js 22 kB 0 B
build/scripts/token-list/index.min.js 767 B 0 B
build/scripts/undo-manager/index.min.js 954 B 0 B
build/scripts/upload-media/index.min.js 11.5 kB 0 B
build/scripts/url/index.min.js 4.02 kB 0 B
build/scripts/vendors/react-dom.min.js 57.7 kB 0 B
build/scripts/vendors/react-jsx-runtime.min.js 513 B 0 B
build/scripts/vendors/react.min.js 3.1 kB 0 B
build/scripts/viewport/index.min.js 1.25 kB 0 B
build/scripts/warning/index.min.js 482 B 0 B
build/scripts/widgets/index.min.js 7.84 kB 0 B
build/scripts/wordcount/index.min.js 1.07 kB 0 B
build/styles/base-styles/admin-schemes-rtl.css 1.71 kB 0 B
build/styles/base-styles/admin-schemes-rtl.min.css 775 B 0 B
build/styles/base-styles/admin-schemes.css 1.71 kB 0 B
build/styles/base-styles/admin-schemes.min.css 775 B 0 B
build/styles/block-directory/style-rtl.css 1.97 kB 0 B
build/styles/block-directory/style-rtl.min.css 1.06 kB 0 B
build/styles/block-directory/style.css 1.98 kB 0 B
build/styles/block-directory/style.min.css 1.06 kB 0 B
build/styles/block-editor/content-rtl.css 5.51 kB 0 B
build/styles/block-editor/content-rtl.min.css 4.08 kB 0 B
build/styles/block-editor/content.css 5.52 kB 0 B
build/styles/block-editor/content.min.css 4.07 kB 0 B
build/styles/block-editor/default-editor-styles-rtl.css 697 B 0 B
build/styles/block-editor/default-editor-styles-rtl.min.css 224 B 0 B
build/styles/block-editor/default-editor-styles.css 697 B 0 B
build/styles/block-editor/default-editor-styles.min.css 224 B 0 B
build/styles/block-editor/style-rtl.css 18.7 kB 0 B
build/styles/block-editor/style-rtl.min.css 15.9 kB 0 B
build/styles/block-editor/style.css 18.7 kB 0 B
build/styles/block-editor/style.min.css 15.9 kB 0 B
build/styles/block-library/accordion-heading/style-rtl.css 346 B 0 B
build/styles/block-library/accordion-heading/style-rtl.min.css 325 B 0 B
build/styles/block-library/accordion-heading/style.css 346 B 0 B
build/styles/block-library/accordion-heading/style.min.css 325 B 0 B
build/styles/block-library/accordion-item/style-rtl.css 239 B 0 B
build/styles/block-library/accordion-item/style-rtl.min.css 180 B 0 B
build/styles/block-library/accordion-item/style.css 238 B 0 B
build/styles/block-library/accordion-item/style.min.css 180 B 0 B
build/styles/block-library/accordion-panel/style-rtl.css 110 B 0 B
build/styles/block-library/accordion-panel/style-rtl.min.css 99 B 0 B
build/styles/block-library/accordion-panel/style.css 110 B 0 B
build/styles/block-library/accordion-panel/style.min.css 99 B 0 B
build/styles/block-library/accordion/style-rtl.css 69 B 0 B
build/styles/block-library/accordion/style-rtl.min.css 62 B 0 B
build/styles/block-library/accordion/style.css 69 B 0 B
build/styles/block-library/accordion/style.min.css 62 B 0 B
build/styles/block-library/archives/style-rtl.css 101 B 0 B
build/styles/block-library/archives/style-rtl.min.css 90 B 0 B
build/styles/block-library/archives/style.css 101 B 0 B
build/styles/block-library/archives/style.min.css 90 B 0 B
build/styles/block-library/audio/editor-rtl.css 166 B 0 B
build/styles/block-library/audio/editor-rtl.min.css 149 B 0 B
build/styles/block-library/audio/editor.css 166 B 0 B
build/styles/block-library/audio/editor.min.css 151 B 0 B
build/styles/block-library/audio/style-rtl.css 945 B 0 B
build/styles/block-library/audio/style-rtl.min.css 132 B 0 B
build/styles/block-library/audio/style.css 945 B 0 B
build/styles/block-library/audio/style.min.css 132 B 0 B
build/styles/block-library/audio/theme-rtl.css 967 B 0 B
build/styles/block-library/audio/theme-rtl.min.css 134 B 0 B
build/styles/block-library/audio/theme.css 967 B 0 B
build/styles/block-library/audio/theme.min.css 134 B 0 B
build/styles/block-library/avatar/editor-rtl.css 127 B 0 B
build/styles/block-library/avatar/editor-rtl.min.css 115 B 0 B
build/styles/block-library/avatar/editor.css 127 B 0 B
build/styles/block-library/avatar/editor.min.css 115 B 0 B
build/styles/block-library/avatar/style-rtl.css 117 B 0 B
build/styles/block-library/avatar/style-rtl.min.css 104 B 0 B
build/styles/block-library/avatar/style.css 117 B 0 B
build/styles/block-library/avatar/style.min.css 104 B 0 B
build/styles/block-library/breadcrumbs/style-rtl.css 236 B 0 B
build/styles/block-library/breadcrumbs/style-rtl.min.css 206 B 0 B
build/styles/block-library/breadcrumbs/style.css 236 B 0 B
build/styles/block-library/breadcrumbs/style.min.css 206 B 0 B
build/styles/block-library/button/editor-rtl.css 306 B 0 B
build/styles/block-library/button/editor-rtl.min.css 265 B 0 B
build/styles/block-library/button/editor.css 317 B 0 B
build/styles/block-library/button/editor.min.css 265 B 0 B
build/styles/block-library/button/style-rtl.css 651 B 0 B
build/styles/block-library/button/style-rtl.min.css 596 B 0 B
build/styles/block-library/button/style.css 662 B 0 B
build/styles/block-library/button/style.min.css 596 B 0 B
build/styles/block-library/buttons/editor-rtl.css 391 B 0 B
build/styles/block-library/buttons/editor-rtl.min.css 291 B 0 B
build/styles/block-library/buttons/editor.css 391 B 0 B
build/styles/block-library/buttons/editor.min.css 291 B 0 B
build/styles/block-library/buttons/style-rtl.css 452 B 0 B
build/styles/block-library/buttons/style-rtl.min.css 349 B 0 B
build/styles/block-library/buttons/style.css 453 B 0 B
build/styles/block-library/buttons/style.min.css 349 B 0 B
build/styles/block-library/calendar/style-rtl.css 271 B 0 B
build/styles/block-library/calendar/style-rtl.min.css 239 B 0 B
build/styles/block-library/calendar/style.css 271 B 0 B
build/styles/block-library/calendar/style.min.css 239 B 0 B
build/styles/block-library/categories/editor-rtl.css 171 B 0 B
build/styles/block-library/categories/editor-rtl.min.css 132 B 0 B
build/styles/block-library/categories/editor.css 170 B 0 B
build/styles/block-library/categories/editor.min.css 131 B 0 B
build/styles/block-library/categories/style-rtl.css 226 B 0 B
build/styles/block-library/categories/style-rtl.min.css 169 B 0 B
build/styles/block-library/categories/style.css 235 B 0 B
build/styles/block-library/categories/style.min.css 169 B 0 B
build/styles/block-library/classic-rtl.css 402 B 0 B
build/styles/block-library/classic-rtl.min.css 358 B 0 B
build/styles/block-library/classic.css 402 B 0 B
build/styles/block-library/classic.min.css 358 B 0 B
build/styles/block-library/code/editor-rtl.css 59 B 0 B
build/styles/block-library/code/editor-rtl.min.css 53 B 0 B
build/styles/block-library/code/editor.css 59 B 0 B
build/styles/block-library/code/editor.min.css 53 B 0 B
build/styles/block-library/code/style-rtl.css 158 B 0 B
build/styles/block-library/code/style-rtl.min.css 140 B 0 B
build/styles/block-library/code/style.css 178 B 0 B
build/styles/block-library/code/style.min.css 140 B 0 B
build/styles/block-library/code/theme-rtl.css 135 B 0 B
build/styles/block-library/code/theme-rtl.min.css 122 B 0 B
build/styles/block-library/code/theme.css 135 B 0 B
build/styles/block-library/code/theme.min.css 122 B 0 B
build/styles/block-library/columns/editor-rtl.css 119 B 0 B
build/styles/block-library/columns/editor-rtl.min.css 108 B 0 B
build/styles/block-library/columns/editor.css 119 B 0 B
build/styles/block-library/columns/editor.min.css 108 B 0 B
build/styles/block-library/columns/style-rtl.css 1.3 kB 0 B
build/styles/block-library/columns/style-rtl.min.css 421 B 0 B
build/styles/block-library/columns/style.css 1.3 kB 0 B
build/styles/block-library/columns/style.min.css 421 B 0 B
build/styles/block-library/comment-author-avatar/editor-rtl.css 136 B 0 B
build/styles/block-library/comment-author-avatar/editor-rtl.min.css 124 B 0 B
build/styles/block-library/comment-author-avatar/editor.css 136 B 0 B
build/styles/block-library/comment-author-avatar/editor.min.css 124 B 0 B
build/styles/block-library/comment-author-name/style-rtl.css 79 B 0 B
build/styles/block-library/comment-author-name/style-rtl.min.css 72 B 0 B
build/styles/block-library/comment-author-name/style.css 79 B 0 B
build/styles/block-library/comment-author-name/style.min.css 72 B 0 B
build/styles/block-library/comment-content/style-rtl.css 137 B 0 B
build/styles/block-library/comment-content/style-rtl.min.css 120 B 0 B
build/styles/block-library/comment-content/style.css 137 B 0 B
build/styles/block-library/comment-content/style.min.css 120 B 0 B
build/styles/block-library/comment-date/style-rtl.css 72 B 0 B
build/styles/block-library/comment-date/style-rtl.min.css 65 B 0 B
build/styles/block-library/comment-date/style.css 72 B 0 B
build/styles/block-library/comment-date/style.min.css 65 B 0 B
build/styles/block-library/comment-edit-link/style-rtl.css 77 B 0 B
build/styles/block-library/comment-edit-link/style-rtl.min.css 70 B 0 B
build/styles/block-library/comment-edit-link/style.css 77 B 0 B
build/styles/block-library/comment-edit-link/style.min.css 70 B 0 B
build/styles/block-library/comment-reply-link/style-rtl.css 78 B 0 B
build/styles/block-library/comment-reply-link/style-rtl.min.css 71 B 0 B
build/styles/block-library/comment-reply-link/style.css 78 B 0 B
build/styles/block-library/comment-reply-link/style.min.css 71 B 0 B
build/styles/block-library/comment-template/style-rtl.css 213 B 0 B
build/styles/block-library/comment-template/style-rtl.min.css 191 B 0 B
build/styles/block-library/comment-template/style.css 213 B 0 B
build/styles/block-library/comment-template/style.min.css 191 B 0 B
build/styles/block-library/comments-pagination-numbers/editor-rtl.css 135 B 0 B
build/styles/block-library/comments-pagination-numbers/editor-rtl.min.css 122 B 0 B
build/styles/block-library/comments-pagination-numbers/editor.css 144 B 0 B
build/styles/block-library/comments-pagination-numbers/editor.min.css 121 B 0 B
build/styles/block-library/comments-pagination/editor-rtl.css 184 B 0 B
build/styles/block-library/comments-pagination/editor-rtl.min.css 168 B 0 B
build/styles/block-library/comments-pagination/editor.css 184 B 0 B
build/styles/block-library/comments-pagination/editor.min.css 168 B 0 B
build/styles/block-library/comments-pagination/style-rtl.css 224 B 0 B
build/styles/block-library/comments-pagination/style-rtl.min.css 201 B 0 B
build/styles/block-library/comments-pagination/style.css 236 B 0 B
build/styles/block-library/comments-pagination/style.min.css 201 B 0 B
build/styles/block-library/comments-title/editor-rtl.css 83 B 0 B
build/styles/block-library/comments-title/editor-rtl.min.css 75 B 0 B
build/styles/block-library/comments-title/editor.css 83 B 0 B
build/styles/block-library/comments-title/editor.min.css 75 B 0 B
build/styles/block-library/comments/editor-rtl.css 968 B 0 B
build/styles/block-library/comments/editor-rtl.min.css 842 B 0 B
build/styles/block-library/comments/editor.css 968 B 0 B
build/styles/block-library/comments/editor.min.css 842 B 0 B
build/styles/block-library/comments/style-rtl.css 754 B 0 B
build/styles/block-library/comments/style-rtl.min.css 637 B 0 B
build/styles/block-library/comments/style.css 752 B 0 B
build/styles/block-library/comments/style.min.css 637 B 0 B
build/styles/block-library/common-rtl.css 2.57 kB 0 B
build/styles/block-library/common-rtl.min.css 1.21 kB 0 B
build/styles/block-library/common.css 2.59 kB 0 B
build/styles/block-library/common.min.css 1.2 kB 0 B
build/styles/block-library/cover/editor-rtl.css 1.05 kB 0 B
build/styles/block-library/cover/editor-rtl.min.css 631 B 0 B
build/styles/block-library/cover/editor.css 1.05 kB 0 B
build/styles/block-library/cover/editor.min.css 631 B 0 B
build/styles/block-library/cover/style-rtl.css 2.5 kB 0 B
build/styles/block-library/cover/style-rtl.min.css 1.82 kB 0 B
build/styles/block-library/cover/style.css 2.51 kB 0 B
build/styles/block-library/cover/style.min.css 1.81 kB 0 B
build/styles/block-library/details/editor-rtl.css 72 B 0 B
build/styles/block-library/details/editor-rtl.min.css 65 B 0 B
build/styles/block-library/details/editor.css 72 B 0 B
build/styles/block-library/details/editor.min.css 65 B 0 B
build/styles/block-library/details/style-rtl.css 97 B 0 B
build/styles/block-library/details/style-rtl.min.css 86 B 0 B
build/styles/block-library/details/style.css 97 B 0 B
build/styles/block-library/details/style.min.css 86 B 0 B
build/styles/block-library/editor-elements-rtl.css 117 B 0 B
build/styles/block-library/editor-elements-rtl.min.css 75 B 0 B
build/styles/block-library/editor-elements.css 117 B 0 B
build/styles/block-library/editor-elements.min.css 75 B 0 B
build/styles/block-library/editor-rtl.css 12.5 kB 0 B
build/styles/block-library/editor-rtl.min.css 10.3 kB 0 B
build/styles/block-library/editor.css 12.5 kB 0 B
build/styles/block-library/editor.min.css 10.3 kB 0 B
build/styles/block-library/elements-rtl.css 84 B 0 B
build/styles/block-library/elements-rtl.min.css 54 B 0 B
build/styles/block-library/elements.css 84 B 0 B
build/styles/block-library/elements.min.css 54 B 0 B
build/styles/block-library/embed/editor-rtl.css 391 B 0 B
build/styles/block-library/embed/editor-rtl.min.css 331 B 0 B
build/styles/block-library/embed/editor.css 390 B 0 B
build/styles/block-library/embed/editor.min.css 331 B 0 B
build/styles/block-library/embed/style-rtl.css 1.29 kB 0 B
build/styles/block-library/embed/style-rtl.min.css 448 B 0 B
build/styles/block-library/embed/style.css 1.29 kB 0 B
build/styles/block-library/embed/style.min.css 448 B 0 B
build/styles/block-library/embed/theme-rtl.css 967 B 0 B
build/styles/block-library/embed/theme-rtl.min.css 133 B 0 B
build/styles/block-library/embed/theme.css 967 B 0 B
build/styles/block-library/embed/theme.min.css 133 B 0 B
build/styles/block-library/file/editor-rtl.css 352 B 0 B
build/styles/block-library/file/editor-rtl.min.css 324 B 0 B
build/styles/block-library/file/editor.css 353 B 0 B
build/styles/block-library/file/editor.min.css 324 B 0 B
build/styles/block-library/file/style-rtl.css 318 B 0 B
build/styles/block-library/file/style-rtl.min.css 278 B 0 B
build/styles/block-library/file/style.css 331 B 0 B
build/styles/block-library/file/style.min.css 278 B 0 B
build/styles/block-library/footnotes/style-rtl.css 220 B 0 B
build/styles/block-library/footnotes/style-rtl.min.css 198 B 0 B
build/styles/block-library/footnotes/style.css 219 B 0 B
build/styles/block-library/footnotes/style.min.css 197 B 0 B
build/styles/block-library/form-input/editor-rtl.css 286 B 0 B
build/styles/block-library/form-input/editor-rtl.min.css 265 B 0 B
build/styles/block-library/form-input/editor.css 285 B 0 B
build/styles/block-library/form-input/editor.min.css 264 B 0 B
build/styles/block-library/form-input/style-rtl.css 467 B 0 B
build/styles/block-library/form-input/style-rtl.min.css 366 B 0 B
build/styles/block-library/form-input/style.css 467 B 0 B
build/styles/block-library/form-input/style.min.css 366 B 0 B
build/styles/block-library/form-submission-notification/editor-rtl.css 368 B 0 B
build/styles/block-library/form-submission-notification/editor-rtl.min.css 344 B 0 B
build/styles/block-library/form-submission-notification/editor.css 368 B 0 B
build/styles/block-library/form-submission-notification/editor.min.css 341 B 0 B
build/styles/block-library/form-submit-button/style-rtl.css 77 B 0 B
build/styles/block-library/form-submit-button/style-rtl.min.css 69 B 0 B
build/styles/block-library/form-submit-button/style.css 77 B 0 B
build/styles/block-library/form-submit-button/style.min.css 69 B 0 B
build/styles/block-library/freeform/editor-rtl.css 1.12 kB 0 B
build/styles/block-library/freeform/editor-rtl.min.css 288 B 0 B
build/styles/block-library/freeform/editor.css 1.12 kB 0 B
build/styles/block-library/freeform/editor.min.css 288 B 0 B
build/styles/block-library/gallery/editor-rtl.css 1.46 kB 0 B
build/styles/block-library/gallery/editor-rtl.min.css 561 B 0 B
build/styles/block-library/gallery/editor.css 1.46 kB 0 B
build/styles/block-library/gallery/editor.min.css 564 B 0 B
build/styles/block-library/gallery/style-rtl.css 2.84 kB 0 B
build/styles/block-library/gallery/style-rtl.min.css 1.84 kB 0 B
build/styles/block-library/gallery/style.css 2.84 kB 0 B
build/styles/block-library/gallery/style.min.css 1.84 kB 0 B
build/styles/block-library/gallery/theme-rtl.css 941 B 0 B
build/styles/block-library/gallery/theme-rtl.min.css 108 B 0 B
build/styles/block-library/gallery/theme.css 941 B 0 B
build/styles/block-library/gallery/theme.min.css 108 B 0 B
build/styles/block-library/group/editor-rtl.css 772 B 0 B
build/styles/block-library/group/editor-rtl.min.css 335 B 0 B
build/styles/block-library/group/editor.css 772 B 0 B
build/styles/block-library/group/editor.min.css 335 B 0 B
build/styles/block-library/group/style-rtl.css 120 B 0 B
build/styles/block-library/group/style-rtl.min.css 103 B 0 B
build/styles/block-library/group/style.css 120 B 0 B
build/styles/block-library/group/style.min.css 103 B 0 B
build/styles/block-library/group/theme-rtl.css 468 B 0 B
build/styles/block-library/group/theme-rtl.min.css 79 B 0 B
build/styles/block-library/group/theme.css 468 B 0 B
build/styles/block-library/group/theme.min.css 79 B 0 B
build/styles/block-library/heading/style-rtl.css 604 B 0 B
build/styles/block-library/heading/style-rtl.min.css 205 B 0 B
build/styles/block-library/heading/style.css 604 B 0 B
build/styles/block-library/heading/style.min.css 205 B 0 B
build/styles/block-library/html/editor-rtl.css 1.29 kB 0 B
build/styles/block-library/html/editor-rtl.min.css 464 B 0 B
build/styles/block-library/html/editor.css 1.3 kB 0 B
build/styles/block-library/html/editor.min.css 464 B 0 B
build/styles/block-library/icon/editor-rtl.css 776 B 0 B
build/styles/block-library/icon/editor-rtl.min.css 377 B 0 B
build/styles/block-library/icon/editor.css 776 B 0 B
build/styles/block-library/icon/editor.min.css 377 B 0 B
build/styles/block-library/icon/style-rtl.css 218 B 0 B
build/styles/block-library/icon/style-rtl.min.css 154 B 0 B
build/styles/block-library/icon/style.css 218 B 0 B
build/styles/block-library/icon/style.min.css 154 B 0 B
build/styles/block-library/image/editor-rtl.css 1.64 kB 0 B
build/styles/block-library/image/editor-rtl.min.css 782 B 0 B
build/styles/block-library/image/editor.css 1.64 kB 0 B
build/styles/block-library/image/editor.min.css 780 B 0 B
build/styles/block-library/image/style-rtl.css 2.92 kB 0 B
build/styles/block-library/image/style-rtl.min.css 1.86 kB 0 B
build/styles/block-library/image/style.css 2.92 kB 0 B
build/styles/block-library/image/style.min.css 1.85 kB 0 B
build/styles/block-library/image/theme-rtl.css 971 B 0 B
build/styles/block-library/image/theme-rtl.min.css 137 B 0 B
build/styles/block-library/image/theme.css 971 B 0 B
build/styles/block-library/image/theme.min.css 137 B 0 B
build/styles/block-library/latest-comments/style-rtl.css 392 B 0 B
build/styles/block-library/latest-comments/style-rtl.min.css 352 B 0 B
build/styles/block-library/latest-comments/style.css 390 B 0 B
build/styles/block-library/latest-comments/style.min.css 352 B 0 B
build/styles/block-library/latest-posts/editor-rtl.css 154 B 0 B
build/styles/block-library/latest-posts/editor-rtl.min.css 139 B 0 B
build/styles/block-library/latest-posts/editor.css 153 B 0 B
build/styles/block-library/latest-posts/editor.min.css 138 B 0 B
build/styles/block-library/latest-posts/style-rtl.css 1.36 kB 0 B
build/styles/block-library/latest-posts/style-rtl.min.css 520 B 0 B
build/styles/block-library/latest-posts/style.css 1.37 kB 0 B
build/styles/block-library/latest-posts/style.min.css 520 B 0 B
build/styles/block-library/list/style-rtl.css 498 B 0 B
build/styles/block-library/list/style-rtl.min.css 107 B 0 B
build/styles/block-library/list/style.css 498 B 0 B
build/styles/block-library/list/style.min.css 107 B 0 B
build/styles/block-library/loginout/style-rtl.css 68 B 0 B
build/styles/block-library/loginout/style-rtl.min.css 61 B 0 B
build/styles/block-library/loginout/style.css 68 B 0 B
build/styles/block-library/loginout/style.min.css 61 B 0 B
build/styles/block-library/math/editor-rtl.css 491 B 0 B
build/styles/block-library/math/editor-rtl.min.css 105 B 0 B
build/styles/block-library/math/editor.css 502 B 0 B
build/styles/block-library/math/editor.min.css 105 B 0 B
build/styles/block-library/math/style-rtl.css 70 B 0 B
build/styles/block-library/math/style-rtl.min.css 61 B 0 B
build/styles/block-library/math/style.css 70 B 0 B
build/styles/block-library/math/style.min.css 61 B 0 B
build/styles/block-library/media-text/editor-rtl.css 389 B 0 B
build/styles/block-library/media-text/editor-rtl.min.css 321 B 0 B
build/styles/block-library/media-text/editor.css 389 B 0 B
build/styles/block-library/media-text/editor.min.css 320 B 0 B
build/styles/block-library/media-text/style-rtl.css 873 B 0 B
build/styles/block-library/media-text/style-rtl.min.css 552 B 0 B
build/styles/block-library/media-text/style.css 901 B 0 B
build/styles/block-library/media-text/style.min.css 550 B 0 B
build/styles/block-library/more/editor-rtl.css 796 B 0 B
build/styles/block-library/more/editor-rtl.min.css 393 B 0 B
build/styles/block-library/more/editor.css 798 B 0 B
build/styles/block-library/more/editor.min.css 393 B 0 B
build/styles/block-library/navigation-link/editor-rtl.css 1.28 kB 0 B
build/styles/block-library/navigation-link/editor-rtl.min.css 710 B 0 B
build/styles/block-library/navigation-link/editor.css 1.27 kB 0 B
build/styles/block-library/navigation-link/editor.min.css 713 B 0 B
build/styles/block-library/navigation-link/style-rtl.css 579 B 0 B
build/styles/block-library/navigation-link/style-rtl.min.css 190 B 0 B
build/styles/block-library/navigation-link/style.css 579 B 0 B
build/styles/block-library/navigation-link/style.min.css 188 B 0 B
build/styles/block-library/navigation-overlay-close/style-rtl.css 260 B 0 B
build/styles/block-library/navigation-overlay-close/style-rtl.min.css 237 B 0 B
build/styles/block-library/navigation-overlay-close/style.css 260 B 0 B
build/styles/block-library/navigation-overlay-close/style.min.css 237 B 0 B
build/styles/block-library/navigation-submenu/editor-rtl.css 1.12 kB 0 B
build/styles/block-library/navigation-submenu/editor-rtl.min.css 295 B 0 B
build/styles/block-library/navigation-submenu/editor.css 1.12 kB 0 B
build/styles/block-library/navigation-submenu/editor.min.css 294 B 0 B
build/styles/block-library/navigation/editor-rtl.css 3.28 kB 0 B
build/styles/block-library/navigation/editor-rtl.min.css 2.28 kB 0 B
build/styles/block-library/navigation/editor.css 3.29 kB 0 B
build/styles/block-library/navigation/editor.min.css 2.28 kB 0 B
build/styles/block-library/navigation/style-rtl.css 3.59 kB 0 B
build/styles/block-library/navigation/style-rtl.min.css 2.52 kB 0 B
build/styles/block-library/navigation/style.css 3.59 kB 0 B
build/styles/block-library/navigation/style.min.css 2.5 kB 0 B
build/styles/block-library/nextpage/editor-rtl.css 799 B 0 B
build/styles/block-library/nextpage/editor-rtl.min.css 392 B 0 B
build/styles/block-library/nextpage/editor.css 800 B 0 B
build/styles/block-library/nextpage/editor.min.css 392 B 0 B
build/styles/block-library/page-list/editor-rtl.css 1.18 kB 0 B
build/styles/block-library/page-list/editor-rtl.min.css 356 B 0 B
build/styles/block-library/page-list/editor.css 1.18 kB 0 B
build/styles/block-library/page-list/editor.min.css 356 B 0 B
build/styles/block-library/page-list/style-rtl.css 207 B 0 B
build/styles/block-library/page-list/style-rtl.min.css 192 B 0 B
build/styles/block-library/page-list/style.css 207 B 0 B
build/styles/block-library/page-list/style.min.css 192 B 0 B
build/styles/block-library/paragraph/editor-rtl.css 315 B 0 B
build/styles/block-library/paragraph/editor-rtl.min.css 292 B 0 B
build/styles/block-library/paragraph/editor.css 314 B 0 B
build/styles/block-library/paragraph/editor.min.css 292 B 0 B
build/styles/block-library/paragraph/style-rtl.css 746 B 0 B
build/styles/block-library/paragraph/style-rtl.min.css 341 B 0 B
build/styles/block-library/paragraph/style.css 752 B 0 B
build/styles/block-library/paragraph/style.min.css 340 B 0 B
build/styles/block-library/playlist-track/style-rtl.css 453 B 0 B
build/styles/block-library/playlist-track/style-rtl.min.css 420 B 0 B
build/styles/block-library/playlist-track/style.css 453 B 0 B
build/styles/block-library/playlist-track/style.min.css 420 B 0 B
build/styles/block-library/playlist/editor-rtl.css 120 B 0 B
build/styles/block-library/playlist/editor-rtl.min.css 112 B 0 B
build/styles/block-library/playlist/editor.css 120 B 0 B
build/styles/block-library/playlist/editor.min.css 112 B 0 B
build/styles/block-library/playlist/style-rtl.css 1.52 kB 0 B
build/styles/block-library/playlist/style-rtl.min.css 1.42 kB 0 B
build/styles/block-library/playlist/style.css 1.52 kB 0 B
build/styles/block-library/playlist/style.min.css 1.42 kB 0 B
build/styles/block-library/post-author-biography/style-rtl.css 96 B 0 B
build/styles/block-library/post-author-biography/style-rtl.min.css 86 B 0 B
build/styles/block-library/post-author-biography/style.css 96 B 0 B
build/styles/block-library/post-author-biography/style.min.css 86 B 0 B
build/styles/block-library/post-author-name/style-rtl.css 76 B 0 B
build/styles/block-library/post-author-name/style-rtl.min.css 69 B 0 B
build/styles/block-library/post-author-name/style.css 76 B 0 B
build/styles/block-library/post-author-name/style.min.css 69 B 0 B
build/styles/block-library/post-author/editor-rtl.css 490 B 0 B
build/styles/block-library/post-author/editor-rtl.min.css 104 B 0 B
build/styles/block-library/post-author/editor.css 490 B 0 B
build/styles/block-library/post-author/editor.min.css 104 B 0 B
build/styles/block-library/post-author/style-rtl.css 213 B 0 B
build/styles/block-library/post-author/style-rtl.min.css 188 B 0 B
build/styles/block-library/post-author/style.css 214 B 0 B
build/styles/block-library/post-author/style.min.css 189 B 0 B
build/styles/block-library/post-comments-count/style-rtl.css 79 B 0 B
build/styles/block-library/post-comments-count/style-rtl.min.css 72 B 0 B
build/styles/block-library/post-comments-count/style.css 79 B 0 B
build/styles/block-library/post-comments-count/style.min.css 72 B 0 B
build/styles/block-library/post-comments-form/editor-rtl.css 104 B 0 B
build/styles/block-library/post-comments-form/editor-rtl.min.css 96 B 0 B
build/styles/block-library/post-comments-form/editor.css 104 B 0 B
build/styles/block-library/post-comments-form/editor.min.css 96 B 0 B
build/styles/block-library/post-comments-form/style-rtl.css 585 B 0 B
build/styles/block-library/post-comments-form/style-rtl.min.css 525 B 0 B
build/styles/block-library/post-comments-form/style.css 584 B 0 B
build/styles/block-library/post-comments-form/style.min.css 525 B 0 B
build/styles/block-library/post-comments-link/style-rtl.css 78 B 0 B
build/styles/block-library/post-comments-link/style-rtl.min.css 71 B 0 B
build/styles/block-library/post-comments-link/style.css 78 B 0 B
build/styles/block-library/post-comments-link/style.min.css 71 B 0 B
build/styles/block-library/post-content/style-rtl.css 68 B 0 B
build/styles/block-library/post-content/style-rtl.min.css 61 B 0 B
build/styles/block-library/post-content/style.css 68 B 0 B
build/styles/block-library/post-content/style.min.css 61 B 0 B
build/styles/block-library/post-date/style-rtl.css 69 B 0 B
build/styles/block-library/post-date/style-rtl.min.css 62 B 0 B
build/styles/block-library/post-date/style.css 69 B 0 B
build/styles/block-library/post-date/style.min.css 62 B 0 B
build/styles/block-library/post-excerpt/editor-rtl.css 78 B 0 B
build/styles/block-library/post-excerpt/editor-rtl.min.css 71 B 0 B
build/styles/block-library/post-excerpt/editor.css 78 B 0 B
build/styles/block-library/post-excerpt/editor.min.css 71 B 0 B
build/styles/block-library/post-excerpt/style-rtl.css 171 B 0 B
build/styles/block-library/post-excerpt/style-rtl.min.css 155 B 0 B
build/styles/block-library/post-excerpt/style.css 171 B 0 B
build/styles/block-library/post-excerpt/style.min.css 155 B 0 B
build/styles/block-library/post-featured-image/editor-rtl.css 1.14 kB 0 B
build/styles/block-library/post-featured-image/editor-rtl.min.css 719 B 0 B
build/styles/block-library/post-featured-image/editor.css 1.14 kB 0 B
build/styles/block-library/post-featured-image/editor.min.css 717 B 0 B
build/styles/block-library/post-featured-image/style-rtl.css 392 B 0 B
build/styles/block-library/post-featured-image/style-rtl.min.css 347 B 0 B
build/styles/block-library/post-featured-image/style.css 392 B 0 B
build/styles/block-library/post-featured-image/style.min.css 347 B 0 B
build/styles/block-library/post-navigation-link/style-rtl.css 234 B 0 B
build/styles/block-library/post-navigation-link/style-rtl.min.css 215 B 0 B
build/styles/block-library/post-navigation-link/style.css 245 B 0 B
build/styles/block-library/post-navigation-link/style.min.css 214 B 0 B
build/styles/block-library/post-template/style-rtl.css 1.27 kB 0 B
build/styles/block-library/post-template/style-rtl.min.css 441 B 0 B
build/styles/block-library/post-template/style.css 1.27 kB 0 B
build/styles/block-library/post-template/style.min.css 441 B 0 B
build/styles/block-library/post-terms/style-rtl.css 108 B 0 B
build/styles/block-library/post-terms/style-rtl.min.css 96 B 0 B
build/styles/block-library/post-terms/style.css 108 B 0 B
build/styles/block-library/post-terms/style.min.css 96 B 0 B
build/styles/block-library/post-time-to-read/style-rtl.css 77 B 0 B
build/styles/block-library/post-time-to-read/style-rtl.min.css 70 B 0 B
build/styles/block-library/post-time-to-read/style.css 77 B 0 B
build/styles/block-library/post-time-to-read/style.min.css 70 B 0 B
build/styles/block-library/post-title/style-rtl.css 175 B 0 B
build/styles/block-library/post-title/style-rtl.min.css 162 B 0 B
build/styles/block-library/post-title/style.css 175 B 0 B
build/styles/block-library/post-title/style.min.css 162 B 0 B
build/styles/block-library/preformatted/style-rtl.css 511 B 0 B
build/styles/block-library/preformatted/style-rtl.min.css 125 B 0 B
build/styles/block-library/preformatted/style.css 511 B 0 B
build/styles/block-library/preformatted/style.min.css 125 B 0 B
build/styles/block-library/pullquote/editor-rtl.css 146 B 0 B
build/styles/block-library/pullquote/editor-rtl.min.css 133 B 0 B
build/styles/block-library/pullquote/editor.css 146 B 0 B
build/styles/block-library/pullquote/editor.min.css 133 B 0 B
build/styles/block-library/pullquote/style-rtl.css 765 B 0 B
build/styles/block-library/pullquote/style-rtl.min.css 365 B 0 B
build/styles/block-library/pullquote/style.css 764 B 0 B
build/styles/block-library/pullquote/style.min.css 365 B 0 B
build/styles/block-library/pullquote/theme-rtl.css 195 B 0 B
build/styles/block-library/pullquote/theme-rtl.min.css 176 B 0 B
build/styles/block-library/pullquote/theme.css 195 B 0 B
build/styles/block-library/pullquote/theme.min.css 176 B 0 B
build/styles/block-library/query-pagination-numbers/editor-rtl.css 134 B 0 B
build/styles/block-library/query-pagination-numbers/editor-rtl.min.css 121 B 0 B
build/styles/block-library/query-pagination-numbers/editor.css 144 B 0 B
build/styles/block-library/query-pagination-numbers/editor.min.css 118 B 0 B
build/styles/block-library/query-pagination/editor-rtl.css 168 B 0 B
build/styles/block-library/query-pagination/editor-rtl.min.css 154 B 0 B
build/styles/block-library/query-pagination/editor.css 168 B 0 B
build/styles/block-library/query-pagination/editor.min.css 154 B 0 B
build/styles/block-library/query-pagination/style-rtl.css 254 B 0 B
build/styles/block-library/query-pagination/style-rtl.min.css 237 B 0 B
build/styles/block-library/query-pagination/style.css 265 B 0 B
build/styles/block-library/query-pagination/style.min.css 237 B 0 B
build/styles/block-library/query-title/style-rtl.css 71 B 0 B
build/styles/block-library/query-title/style-rtl.min.css 64 B 0 B
build/styles/block-library/query-title/style.css 71 B 0 B
build/styles/block-library/query-title/style.min.css 64 B 0 B
build/styles/block-library/query-total/style-rtl.css 71 B 0 B
build/styles/block-library/query-total/style-rtl.min.css 64 B 0 B
build/styles/block-library/query-total/style.css 71 B 0 B
build/styles/block-library/query-total/style.min.css 64 B 0 B
build/styles/block-library/query/editor-rtl.css 1.28 kB 0 B
build/styles/block-library/query/editor-rtl.min.css 438 B 0 B
build/styles/block-library/query/editor.css 1.28 kB 0 B
build/styles/block-library/query/editor.min.css 438 B 0 B
build/styles/block-library/quote/style-rtl.css 255 B 0 B
build/styles/block-library/quote/style-rtl.min.css 238 B 0 B
build/styles/block-library/quote/style.css 256 B 0 B
build/styles/block-library/quote/style.min.css 238 B 0 B
build/styles/block-library/quote/theme-rtl.css 253 B 0 B
build/styles/block-library/quote/theme-rtl.min.css 233 B 0 B
build/styles/block-library/quote/theme.css 254 B 0 B
build/styles/block-library/quote/theme.min.css 236 B 0 B
build/styles/block-library/read-more/style-rtl.css 146 B 0 B
build/styles/block-library/read-more/style-rtl.min.css 131 B 0 B
build/styles/block-library/read-more/style.css 146 B 0 B
build/styles/block-library/read-more/style.min.css 131 B 0 B
build/styles/block-library/reset-rtl.css 936 B 0 B
build/styles/block-library/reset-rtl.min.css 467 B 0 B
build/styles/block-library/reset.css 936 B 0 B
build/styles/block-library/reset.min.css 467 B 0 B
build/styles/block-library/rss/editor-rtl.css 144 B 0 B
build/styles/block-library/rss/editor-rtl.min.css 126 B 0 B
build/styles/block-library/rss/editor.css 144 B 0 B
build/styles/block-library/rss/editor.min.css 126 B 0 B
build/styles/block-library/rss/style-rtl.css 1.11 kB 0 B
build/styles/block-library/rss/style-rtl.min.css 284 B 0 B
build/styles/block-library/rss/style.css 1.12 kB 0 B
build/styles/block-library/rss/style.min.css 283 B 0 B
build/styles/block-library/search/editor-rtl.css 217 B 0 B
build/styles/block-library/search/editor-rtl.min.css 199 B 0 B
build/styles/block-library/search/editor.css 217 B 0 B
build/styles/block-library/search/editor.min.css 199 B 0 B
build/styles/block-library/search/style-rtl.css 1.1 kB 0 B
build/styles/block-library/search/style-rtl.min.css 665 B 0 B
build/styles/block-library/search/style.css 1.1 kB 0 B
build/styles/block-library/search/style.min.css 666 B 0 B
build/styles/block-library/search/theme-rtl.css 130 B 0 B
build/styles/block-library/search/theme-rtl.min.css 113 B 0 B
build/styles/block-library/search/theme.css 130 B 0 B
build/styles/block-library/search/theme.min.css 113 B 0 B
build/styles/block-library/separator/editor-rtl.css 106 B 0 B
build/styles/block-library/separator/editor-rtl.min.css 100 B 0 B
build/styles/block-library/separator/editor.css 106 B 0 B
build/styles/block-library/separator/editor.min.css 100 B 0 B
build/styles/block-library/separator/style-rtl.css 284 B 0 B
build/styles/block-library/separator/style-rtl.min.css 248 B 0 B
build/styles/block-library/separator/style.css 297 B 0 B
build/styles/block-library/separator/style.min.css 248 B 0 B
build/styles/block-library/separator/theme-rtl.css 226 B 0 B
build/styles/block-library/separator/theme-rtl.min.css 195 B 0 B
build/styles/block-library/separator/theme.css 226 B 0 B
build/styles/block-library/separator/theme.min.css 195 B 0 B
build/styles/block-library/shortcode/editor-rtl.css 1.1 kB 0 B
build/styles/block-library/shortcode/editor-rtl.min.css 286 B 0 B
build/styles/block-library/shortcode/editor.css 1.1 kB 0 B
build/styles/block-library/shortcode/editor.min.css 286 B 0 B
build/styles/block-library/site-logo/editor-rtl.css 1.12 kB 0 B
build/styles/block-library/site-logo/editor-rtl.min.css 696 B 0 B
build/styles/block-library/site-logo/editor.css 1.12 kB 0 B
build/styles/block-library/site-logo/editor.min.css 692 B 0 B
build/styles/block-library/site-logo/style-rtl.css 239 B 0 B
build/styles/block-library/site-logo/style-rtl.min.css 218 B 0 B
build/styles/block-library/site-logo/style.css 238 B 0 B
build/styles/block-library/site-logo/style.min.css 218 B 0 B
build/styles/block-library/site-tagline/editor-rtl.css 94 B 0 B
build/styles/block-library/site-tagline/editor-rtl.min.css 87 B 0 B
build/styles/block-library/site-tagline/editor.css 94 B 0 B
build/styles/block-library/site-tagline/editor.min.css 87 B 0 B
build/styles/block-library/site-tagline/style-rtl.css 72 B 0 B
build/styles/block-library/site-tagline/style-rtl.min.css 65 B 0 B
build/styles/block-library/site-tagline/style.css 72 B 0 B
build/styles/block-library/site-tagline/style.min.css 65 B 0 B
build/styles/block-library/site-title/editor-rtl.css 93 B 0 B
build/styles/block-library/site-title/editor-rtl.min.css 85 B 0 B
build/styles/block-library/site-title/editor.css 93 B 0 B
build/styles/block-library/site-title/editor.min.css 85 B 0 B
build/styles/block-library/site-title/style-rtl.css 153 B 0 B
build/styles/block-library/site-title/style-rtl.min.css 143 B 0 B
build/styles/block-library/site-title/style.css 153 B 0 B
build/styles/block-library/site-title/style.min.css 143 B 0 B
build/styles/block-library/social-link/editor-rtl.css 346 B 0 B
build/styles/block-library/social-link/editor-rtl.min.css 314 B 0 B
build/styles/block-library/social-link/editor.css 348 B 0 B
build/styles/block-library/social-link/editor.min.css 314 B 0 B
build/styles/block-library/social-links/editor-rtl.css 737 B 0 B
build/styles/block-library/social-links/editor-rtl.min.css 339 B 0 B
build/styles/block-library/social-links/editor.css 738 B 0 B
build/styles/block-library/social-links/editor.min.css 338 B 0 B
build/styles/block-library/social-links/style-rtl.css 1.57 kB 0 B
build/styles/block-library/social-links/style-rtl.min.css 1.51 kB 0 B
build/styles/block-library/social-links/style.css 1.57 kB 0 B
build/styles/block-library/social-links/style.min.css 1.51 kB 0 B
build/styles/block-library/spacer/editor-rtl.css 774 B 0 B
build/styles/block-library/spacer/editor-rtl.min.css 346 B 0 B
build/styles/block-library/spacer/editor.css 774 B 0 B
build/styles/block-library/spacer/editor.min.css 346 B 0 B
build/styles/block-library/spacer/style-rtl.css 55 B 0 B
build/styles/block-library/spacer/style-rtl.min.css 48 B 0 B
build/styles/block-library/spacer/style.css 55 B 0 B
build/styles/block-library/spacer/style.min.css 48 B 0 B
build/styles/block-library/style-rtl.css 21.6 kB 0 B
build/styles/block-library/style-rtl.min.css 18.1 kB 0 B
build/styles/block-library/style.css 21.7 kB 0 B
build/styles/block-library/style.min.css 18 kB 0 B
build/styles/block-library/tab-list/editor-rtl.css 107 B 0 B
build/styles/block-library/tab-list/editor-rtl.min.css 97 B 0 B
build/styles/block-library/tab-list/editor.css 107 B 0 B
build/styles/block-library/tab-list/editor.min.css 97 B 0 B
build/styles/block-library/tab-panel/style-rtl.css 238 B 0 B
build/styles/block-library/tab-panel/style-rtl.min.css 215 B 0 B
build/styles/block-library/tab-panel/style.css 238 B 0 B
build/styles/block-library/tab-panel/style.min.css 215 B 0 B
build/styles/block-library/tab-panels/style-rtl.css 76 B 0 B
build/styles/block-library/tab-panels/style-rtl.min.css 65 B 0 B
build/styles/block-library/tab-panels/style.css 76 B 0 B
build/styles/block-library/tab-panels/style.min.css 65 B 0 B
build/styles/block-library/tab/editor-rtl.css 160 B 0 B
build/styles/block-library/tab/editor-rtl.min.css 148 B 0 B
build/styles/block-library/tab/editor.css 160 B 0 B
build/styles/block-library/tab/editor.min.css 148 B 0 B
build/styles/block-library/tab/style-rtl.css 397 B 0 B
build/styles/block-library/tab/style-rtl.min.css 352 B 0 B
build/styles/block-library/tab/style.css 398 B 0 B
build/styles/block-library/tab/style.min.css 356 B 0 B
build/styles/block-library/table-of-contents/style-rtl.css 89 B 0 B
build/styles/block-library/table-of-contents/style-rtl.min.css 83 B 0 B
build/styles/block-library/table-of-contents/style.css 89 B 0 B
build/styles/block-library/table-of-contents/style.min.css 83 B 0 B
build/styles/block-library/table/editor-rtl.css 1.25 kB 0 B
build/styles/block-library/table/editor-rtl.min.css 394 B 0 B
build/styles/block-library/table/editor.css 1.25 kB 0 B
build/styles/block-library/table/editor.min.css 394 B 0 B
build/styles/block-library/table/style-rtl.css 1.06 kB 0 B
build/styles/block-library/table/style-rtl.min.css 641 B 0 B
build/styles/block-library/table/style.css 1.06 kB 0 B
build/styles/block-library/table/style.min.css 640 B 0 B
build/styles/block-library/table/theme-rtl.css 985 B 0 B
build/styles/block-library/table/theme-rtl.min.css 152 B 0 B
build/styles/block-library/table/theme.css 985 B 0 B
build/styles/block-library/table/theme.min.css 152 B 0 B
build/styles/block-library/tabs/style-rtl.css 64 B 0 B
build/styles/block-library/tabs/style-rtl.min.css 57 B 0 B
build/styles/block-library/tabs/style.css 64 B 0 B
build/styles/block-library/tabs/style.min.css 57 B 0 B
build/styles/block-library/tag-cloud/style-rtl.css 283 B 0 B
build/styles/block-library/tag-cloud/style-rtl.min.css 248 B 0 B
build/styles/block-library/tag-cloud/style.css 283 B 0 B
build/styles/block-library/tag-cloud/style.min.css 248 B 0 B
build/styles/block-library/template-part/editor-rtl.css 1.2 kB 0 B
build/styles/block-library/template-part/editor-rtl.min.css 368 B 0 B
build/styles/block-library/template-part/editor.css 1.2 kB 0 B
build/styles/block-library/template-part/editor.min.css 368 B 0 B
build/styles/block-library/template-part/theme-rtl.css 492 B 0 B
build/styles/block-library/template-part/theme-rtl.min.css 113 B 0 B
build/styles/block-library/template-part/theme.css 492 B 0 B
build/styles/block-library/template-part/theme.min.css 113 B 0 B
build/styles/block-library/term-count/style-rtl.css 70 B 0 B
build/styles/block-library/term-count/style-rtl.min.css 63 B 0 B
build/styles/block-library/term-count/style.css 70 B 0 B
build/styles/block-library/term-count/style.min.css 63 B 0 B
build/styles/block-library/term-description/style-rtl.css 138 B 0 B
build/styles/block-library/term-description/style-rtl.min.css 126 B 0 B
build/styles/block-library/term-description/style.css 138 B 0 B
build/styles/block-library/term-description/style.min.css 126 B 0 B
build/styles/block-library/term-name/style-rtl.css 69 B 0 B
build/styles/block-library/term-name/style-rtl.min.css 62 B 0 B
build/styles/block-library/term-name/style.css 69 B 0 B
build/styles/block-library/term-name/style.min.css 62 B 0 B
build/styles/block-library/term-template/editor-rtl.css 267 B 0 B
build/styles/block-library/term-template/editor-rtl.min.css 225 B 0 B
build/styles/block-library/term-template/editor.css 267 B 0 B
build/styles/block-library/term-template/editor.min.css 225 B 0 B
build/styles/block-library/term-template/style-rtl.css 124 B 0 B
build/styles/block-library/term-template/style-rtl.min.css 114 B 0 B
build/styles/block-library/term-template/style.css 124 B 0 B
build/styles/block-library/term-template/style.min.css 114 B 0 B
build/styles/block-library/text-columns/editor-rtl.css 481 B 0 B
build/styles/block-library/text-columns/editor-rtl.min.css 95 B 0 B
build/styles/block-library/text-columns/editor.css 481 B 0 B
build/styles/block-library/text-columns/editor.min.css 95 B 0 B
build/styles/block-library/text-columns/style-rtl.css 177 B 0 B
build/styles/block-library/text-columns/style-rtl.min.css 165 B 0 B
build/styles/block-library/text-columns/style.css 177 B 0 B
build/styles/block-library/text-columns/style.min.css 165 B 0 B
build/styles/block-library/theme-rtl.css 1.59 kB 0 B
build/styles/block-library/theme-rtl.min.css 715 B 0 B
build/styles/block-library/theme.css 1.6 kB 0 B
build/styles/block-library/theme.min.css 719 B 0 B
build/styles/block-library/verse/style-rtl.css 155 B 0 B
build/styles/block-library/verse/style-rtl.min.css 137 B 0 B
build/styles/block-library/verse/style.css 155 B 0 B
build/styles/block-library/verse/style.min.css 137 B 0 B
build/styles/block-library/video/editor-rtl.css 839 B 0 B
build/styles/block-library/video/editor-rtl.min.css 428 B 0 B
build/styles/block-library/video/editor.css 840 B 0 B
build/styles/block-library/video/editor.min.css 428 B 0 B
build/styles/block-library/video/style-rtl.css 1.02 kB 0 B
build/styles/block-library/video/style-rtl.min.css 202 B 0 B
build/styles/block-library/video/style.css 1.02 kB 0 B
build/styles/block-library/video/style.min.css 202 B 0 B
build/styles/block-library/video/theme-rtl.css 967 B 0 B
build/styles/block-library/video/theme-rtl.min.css 134 B 0 B
build/styles/block-library/video/theme.css 967 B 0 B
build/styles/block-library/video/theme.min.css 134 B 0 B
build/styles/commands/style-rtl.css 2.07 kB 0 B
build/styles/commands/style-rtl.min.css 1.17 kB 0 B
build/styles/commands/style.css 2.06 kB 0 B
build/styles/commands/style.min.css 1.17 kB 0 B
build/styles/components/style-rtl.css 17.8 kB 0 B
build/styles/components/style-rtl.min.css 14.7 kB 0 B
build/styles/components/style.css 17.9 kB 0 B
build/styles/components/style.min.css 14.8 kB 0 B
build/styles/customize-widgets/style-rtl.css 2.35 kB 0 B
build/styles/customize-widgets/style-rtl.min.css 1.44 kB 0 B
build/styles/customize-widgets/style.css 2.35 kB 0 B
build/styles/customize-widgets/style.min.css 1.44 kB 0 B
build/styles/edit-post/classic-rtl.css 1.29 kB 0 B
build/styles/edit-post/classic-rtl.min.css 425 B 0 B
build/styles/edit-post/classic.css 1.31 kB 0 B
build/styles/edit-post/classic.min.css 428 B 0 B
build/styles/edit-post/experimental-admin-bar-in-editor-rtl.css 550 B 0 B
build/styles/edit-post/experimental-admin-bar-in-editor-rtl.min.css 513 B 0 B
build/styles/edit-post/experimental-admin-bar-in-editor.css 551 B 0 B
build/styles/edit-post/experimental-admin-bar-in-editor.min.css 514 B 0 B
build/styles/edit-post/style-rtl.css 3.8 kB 0 B
build/styles/edit-post/style-rtl.min.css 2.48 kB 0 B
build/styles/edit-post/style.css 3.81 kB 0 B
build/styles/edit-post/style.min.css 2.49 kB 0 B
build/styles/edit-site/experimental-admin-bar-in-editor-rtl.css 502 B 0 B
build/styles/edit-site/experimental-admin-bar-in-editor-rtl.min.css 474 B 0 B
build/styles/edit-site/experimental-admin-bar-in-editor.css 502 B 0 B
build/styles/edit-site/experimental-admin-bar-in-editor.min.css 474 B 0 B
build/styles/edit-site/style-rtl.css 21.3 kB 0 B
build/styles/edit-site/style-rtl.min.css 17.4 kB 0 B
build/styles/edit-site/style.css 21.3 kB 0 B
build/styles/edit-site/style.min.css 17.4 kB 0 B
build/styles/edit-widgets/style-rtl.css 4.85 kB 0 B
build/styles/edit-widgets/style-rtl.min.css 3.52 kB 0 B
build/styles/edit-widgets/style.css 4.85 kB 0 B
build/styles/edit-widgets/style.min.css 3.52 kB 0 B
build/styles/editor/style-rtl.css 29.4 kB 0 B
build/styles/editor/style-rtl.min.css 24.9 kB 0 B
build/styles/editor/style.css 29.5 kB 0 B
build/styles/editor/style.min.css 24.9 kB 0 B
build/styles/format-library/style-rtl.css 735 B 0 B
build/styles/format-library/style-rtl.min.css 326 B 0 B
build/styles/format-library/style.css 746 B 0 B
build/styles/format-library/style.min.css 326 B 0 B
build/styles/list-reusable-blocks/style-rtl.css 1.03 kB 0 B
build/styles/list-reusable-blocks/style-rtl.min.css 210 B 0 B
build/styles/list-reusable-blocks/style.css 1.03 kB 0 B
build/styles/list-reusable-blocks/style.min.css 211 B 0 B
build/styles/media-utils/style-rtl.css 2.12 kB 0 B
build/styles/media-utils/style-rtl.min.css 1.2 kB 0 B
build/styles/media-utils/style.css 2.11 kB 0 B
build/styles/media-utils/style.min.css 1.2 kB 0 B
build/styles/notices/style-rtl.css 121 B 0 B
build/styles/notices/style-rtl.min.css 106 B 0 B
build/styles/notices/style.css 121 B 0 B
build/styles/notices/style.min.css 106 B 0 B
build/styles/nux/style-rtl.css 118 B 0 B
build/styles/nux/style-rtl.min.css 0 B 0 B 🆕
build/styles/nux/style.css 118 B 0 B
build/styles/nux/style.min.css 0 B 0 B 🆕
build/styles/patterns/style-rtl.css 1.46 kB 0 B
build/styles/patterns/style-rtl.min.css 611 B 0 B
build/styles/patterns/style.css 1.46 kB 0 B
build/styles/patterns/style.min.css 611 B 0 B
build/styles/preferences/style-rtl.css 1.26 kB 0 B
build/styles/preferences/style-rtl.min.css 415 B 0 B
build/styles/preferences/style.css 1.26 kB 0 B
build/styles/preferences/style.min.css 415 B 0 B
build/styles/reusable-blocks/style-rtl.css 1.11 kB 0 B
build/styles/reusable-blocks/style-rtl.min.css 275 B 0 B
build/styles/reusable-blocks/style.css 1.11 kB 0 B
build/styles/reusable-blocks/style.min.css 275 B 0 B
build/styles/widgets/style-rtl.css 2.05 kB 0 B
build/styles/widgets/style-rtl.min.css 1.16 kB 0 B
build/styles/widgets/style.css 2.06 kB 0 B
build/styles/widgets/style.min.css 1.16 kB 0 B

compressed-size-action

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 fixes the Connectors plugin-install flow when the WordPress filesystem method is not direct (e.g. FTP), by mirroring Core’s standard “Connection Information” modal behavior so users have a clear path to provide credentials and complete installation.

Changes:

  • Enqueues Core’s updates installer script and prints the filesystem credentials modal footer markup on the Connectors admin screen.
  • Exposes a filesystemCredentialsRequired flag to the Connectors script module so the client can choose the correct install mechanism.
  • Updates the Connectors install hook to route through wp.updates.installPlugin() when credentials are required, then activates via REST.

Reviewed changes

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

File Description
lib/experimental/connectors/load.php Adds Connectors-screen setup for updates script + filesystem credentials modal, and exposes a “credentials required” flag to script module data.
routes/connectors-home/use-connector-plugin.ts Adds a wp.updates-based install path when filesystem credentials are needed, and suppresses snackbar on user-cancel.
routes/connectors-home/default-connectors.tsx Reads the new script module flag and passes it into the connector install hook.

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

Comment on lines +110 to +114
jq( document ).one( 'wp-plugin-install-success', onSuccess );
jq( document ).one( 'wp-plugin-install-error', onError );
jq( document ).one( 'credential-modal-cancel', onCancel );

updates.installPlugin( { slug } );

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Per Claude:

Fixed in cfeeea4. installPluginViaWpUpdates() now registers the three handlers with jQuery(document).on(...) (not .one(...)) and tears them down via an explicit shared cleanup() that only runs once the event whose response.slug matches the requested slug arrives. A wp-plugin-install-success/-error for a different plugin no longer consumes a one-shot handler and strands this promise — non-matching events are ignored and the handlers stay registered until the matching one resolves/rejects (or credential-modal-cancel fires). Verified end-to-end under FS_METHOD=ftpsockets (install, cancel, and re-install paths).

Comment on lines +272 to +279
await installPluginViaWpUpdates( pluginSlug );
if ( pluginBasename ) {
await saveEntityRecord(
'root',
'plugin',
{ plugin: pluginBasename, status: 'active' },
{ throwOnError: true }
);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Per Claude:

Thanks — but I believe this concern is based on two incorrect premises, so I'm leaving the code as-is:

  1. The REST endpoint expects the basename without .php. WP_REST_Plugins_Controller::sanitize_plugin_param() is return plugin_basename( sanitize_text_field( $file . '.php' ) ); — it appends .php itself. So passing ai-provider-for-openai/plugin (the stripped pluginBasename) is exactly what the endpoint wants; passing the full …/plugin.php would result in …/plugin.php.php.

  2. The wp-plugin-install-success payload has no top-level response.plugin. Per wp_ajax_install_plugin() in wp-admin/includes/ajax-actions.php, the success $status object carries slug and (conditionally) activateUrl; the plugin file is only embedded inside the activateUrl query string, not exposed as a clean field. So there's nothing reliable to "return from installPluginViaWpUpdates" without fragile URL parsing.

Additionally, the non-FS activatePlugin() path in this same file (pre-existing, not introduced here) already uses plugin: pluginBasename and works, and the FS path was verified end-to-end under FS_METHOD=ftpsockets (install via FTP → REST activate → entity refresh → UI expands), so this isn't a regression.

Happy to revisit if there's a connector whose configured plugin.file doesn't match its actual installed main file, but that gap (if it exists) would predate this PR and affect the existing activate path equally.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@copilot thoughts on the above?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I agree with your analysis: keeping pluginBasename is the correct REST value for this flow, and wp-plugin-install-success doesn’t provide a reliable top-level plugin file to switch to. So no additional code change is needed here beyond cfeeea4.

Comment on lines +310 to +316
} catch ( error ) {
// A user-initiated cancel of the filesystem credentials dialog
// should silently abort the install without a snackbar.
const message = error instanceof Error ? error.message : '';
if ( message === 'Filesystem credentials request canceled.' ) {
return;
}

This comment was marked as resolved.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Per Claude:

Fixed in cfeeea4. The hard-coded error-message string match is gone. installPluginViaWpUpdates() now resolves with a discriminated 'installed' | 'canceled' value: closing the credentials modal (credential-modal-cancel) resolves 'canceled', a genuine failure still reject()s with an Error. The caller branches on outcome === 'canceled' to silently abort, so cancellation is a normal control-flow outcome rather than an exception identified by message text. (And re: the message-string note — you were right, that string was an invention with no basis in wordpress-develop; it no longer exists anywhere now.) Verified the cancel path leaves no error snackbar and the button returns to "Install".

Comment thread lib/experimental/connectors/load.php Outdated
@github-actions

github-actions Bot commented May 15, 2026 •

Copy link
Copy Markdown

Flaky tests detected in 530755e.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/26016663464
📝 Reported issues:

westonruter and others added 3 commits May 15, 2026 13:31
- Register the install event handlers with on()/off() instead of one()
  and filter by slug. With one(), a concurrent install of a different
  plugin firing first would consume the one-shot handler and leave the
  promise pending. on() + explicit cleanup() only tears down once the
  matching slug's event arrives.
- Replace the brittle "cancel via hard-coded error message string"
  detection. installPluginViaWpUpdates() now resolves with
  'installed' | 'canceled' so cancel is a normal control-flow outcome
  the caller branches on, while genuine install failures still reject.

Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Copilot AI commented May 15, 2026

Copy link
Copy Markdown
Contributor

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • translate.wordpress.org
    • Triggering command: /usr/local/bin/node node i18n-cache/index.js (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

@ekamran

ekamran commented May 16, 2026

Copy link
Copy Markdown
Contributor

I tested this PR locally with FS_METHOD set to ftpext.

The provider connector cards now open the standard “Connection Information” filesystem credentials modal as expected. I tested this with the OpenAI connector, and canceling the modal returned the button back to Install without the previous generic install failure.

I noticed one related case that still seems to use the old REST install path: the top “Install the AI plugin” callout. When clicking that button on the same Connectors screen, it shows “Failed to install the AI plugin.” instead of opening the filesystem credentials modal.

Steps:

  1. Use an environment with FS_METHOD=ftpext.
  2. Visit Settings > Connectors.
  3. Click “Install the AI plugin”.

Expected: the standard filesystem credentials modal opens, matching the provider connector install flow.

Actual: the install fails with the generic AI plugin install error.

westonruter and others added 2 commits May 15, 2026 20:44
The "Install the AI plugin" button in AiPluginCallout had the same
silent-failure bug as the connector install: it POSTed directly to the
REST plugin endpoint, which rejects the install up front when
FS_METHOD is not 'direct' and credentials have not been stored.

Reuse the now-exported installPluginViaWpUpdates() helper so the AI
plugin install routes through wp.updates.installPlugin() (surfacing the
standard "Connection Information" modal) when credentials are required,
then activates via REST. The non-FS path is unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…-fs-credentials

Resolved a modify/delete conflict from trunk #78228, which relocated the
Connectors loader out of lib/experimental/connectors/load.php and
consolidated it into lib/compat/wordpress-7.0/connectors.php. The
filesystem-credentials additions (the load-{hook} screen setup, the
credentials-modal printer, and the script-module-data filter) were
ported into the new connectors.php location; the obsolete
experimental/connectors/load.php was removed (its default-connectors.php
require is now handled directly by lib/load.php).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@westonruter

westonruter commented May 16, 2026 •

Copy link
Copy Markdown
Member Author

I noticed one related case that still seems to use the old REST install path: the top “Install the AI plugin” callout. When clicking that button on the same Connectors screen, it shows “Failed to install the AI plugin.” instead of opening the filesystem credentials modal.

@ekamran Great catch! This is fixed now in 8d3b1c2.

The merge conflicts are also fixed in 9ae49aa after #78228, although this may cause problems if this is cherry-picked to wp/7.0? /cc @t-hamano

@westonruter

Copy link
Copy Markdown
Member Author

Here's a screen recording showing the installation of the AI plugin via FTP:

AI.plugin.install.via.FTP.mov

(Ignore Chrome's warning about an insecure FTP password! ☺️)

@t-hamano

Copy link
Copy Markdown
Contributor

The merge conflicts are also fixed in 9ae49aa after #78228, although this may cause problems if this is cherry-picked to wp/7.0? /cc @t-hamano

If we intend to backport this PR to WP 7.0, we can backport #78228 to the wp/7.0 branch to resolve conflicts beforehand. #78228 will not have any impact on WordPress core releases.

request_filesystem_credentials() returns true (no credentials needed),
false (needed but not provided), or an array (needed and provided).
Only the explicit false sentinel should set filesystemCredentialsRequired,
so compare with `false === $stored` instead of `! $stored`. This avoids
coercing a (theoretical) empty-array return to "required" and matches
the strict-boolean expectation of PHPStan, mirroring the Performance Lab
counterpart which already uses the strict comparison.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ekamran

ekamran commented May 17, 2026

Copy link
Copy Markdown
Contributor

Thanks for the follow-up fix here.

I pulled the latest PR head (675d2eb1e824a7d462172ed541133aa84d8c12a4) and re-tested locally with FS_METHOD set to ftpext.

After rebuilding the Gutenberg assets, I can confirm the UI behavior looks fixed for both paths I checked:

  • Provider connector cards open the standard “Connection Information” modal.
  • The top “Install the AI plugin” callout also opens the same filesystem credentials modal now.

One thing I noticed while checking the latest branch: the targeted unit test for the AI plugin callout is still failing locally:

npm run test:unit -- routes/connectors-home/test/ai-plugin-callout.tsx --runInBand

Failure:

AiPluginCallout › installs the AI plugin using the plugin slug

Expected mockSaveEntityRecord to be called with:
"root", "plugin", { "slug": "ai", "status": "active" }, { "throwOnError": true }

Number of calls: 0

From a quick look, AiPluginCallout now imports getRequiresFilesystemCredentials() from ../default-connectors, but the test mock for ../default-connectors only provides getConnectorData(). The existing non-filesystem test may just need the mock to return false for getRequiresFilesystemCredentials(), and it may be worth adding a companion test for the filesystem-credentials path.

@ekamran

ekamran commented May 17, 2026

Copy link
Copy Markdown
Contributor

I was also able to get the failing AiPluginCallout unit test passing locally with a small test-only change.

The patch updates the ../default-connectors mock to include getRequiresFilesystemCredentials() with a default false return value, and adds a focused test for the filesystem-credentials path where the AI plugin install is routed through installPluginViaWpUpdates() before activating ai/ai.

After that change, the focused test passes locally:

npm run test:unit -- routes/connectors-home/test/ai-plugin-callout.tsx --runInBand
PASS routes/connectors-home/test/ai-plugin-callout.tsx
Tests: 3 passed

Happy to share the patch or open a small follow-up PR if that would be helpful.

…rage

The AiPluginCallout test mocked ../default-connectors with only
getConnectorData(), so once the component started importing
getRequiresFilesystemCredentials() that call was undefined and the
"installs the AI plugin using the plugin slug" test failed (the install
threw before reaching saveEntityRecord).

Add getRequiresFilesystemCredentials() to the mock (defaulting to
false) and mock installPluginViaWpUpdates from ../use-connector-plugin,
then add companion tests covering the filesystem-credentials path:
install routed through wp.updates then activating ai/ai, and a silent
abort when the credentials modal is canceled.

Co-authored-by: ekamran <ekamran@git.wordpress.org>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@westonruter

Copy link
Copy Markdown
Member Author

@ekamran How does 516b74b look?

BTW, if you add ekamran@git.wordpress.org to your GitHub email addresses, then you'll show up as a co-author properly.

The wp.updates fallback in use-connector-plugin.ts declared its own
minimal `jQuery?: ( target: Document ) => { on, off }` shape. Depend on
@types/jquery (pinned to 3.5.34, matching wordpress-develop core) and
reuse the canonical JQueryStatic / JQuery.TriggeredEvent types instead
of reinventing them.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment thread package.json Outdated
"@inquirer/prompts": "7.2.0",
"@octokit/rest": "16.26.0",
"@testing-library/jest-dom": "6.9.1",
"@types/jquery": "3.5.34",

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@ekamran

ekamran commented May 18, 2026

Copy link
Copy Markdown
Contributor

@ekamran How does 516b74b look?

BTW, if you add ekamran@git.wordpress.org to your GitHub email addresses, then you'll show up as a co-author properly.

Thanks! I added ekamran@git.wordpress.org to my GitHub email addresses as suggested.

The latest changes look good to me, and I see the checks are now in much better shape.

@t-hamano t-hamano added the Backport to WP Minor Release Pull request that needs to be backported to a WordPress minor release label May 18, 2026

@t-hamano t-hamano left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Instead of defining filesystemCredentialsRequired on the server-side, could we check if the REST API response's error code is fs_unavailable on the client-side? This might significantly reduce the amount of code.

Although I haven't tested it, the AI suggested the following diff.

Details
diff --git a/lib/compat/wordpress-7.0/connectors.php b/lib/compat/wordpress-7.0/connectors.php
index d8d3a03b769..f3cf682353c 100644
--- a/lib/compat/wordpress-7.0/connectors.php
+++ b/lib/compat/wordpress-7.0/connectors.php
@@ -270,36 +270,3 @@ function _gutenberg_connectors_print_filesystem_credentials_modal(): void {
 	wp_print_request_filesystem_credentials_modal();
 	wp_print_admin_notice_templates();
 }
-
-/**
- * Exposes whether the current request can install plugins directly to the
- * Connectors script module.
- *
- * When the filesystem method is anything other than `direct` and stored
- * credentials are not yet available, the REST plugin endpoint will reject
- * an install attempt up front. Surfacing this state to the client lets the
- * React layer pre-emptively route through `wp.updates.installPlugin()`,
- * which displays the standard filesystem credentials modal.
- *
- * @access private
- *
- * @param array<string, mixed> $data Existing script module data.
- * @return array<string, mixed> Script module data with the filesystem flag added.
- */
-function _gutenberg_connectors_add_filesystem_data_to_script_module_data( array $data ): array {
-	require_once ABSPATH . 'wp-admin/includes/file.php';
-
-	$method = get_filesystem_method();
-	if ( 'direct' === $method ) {
-		$data['filesystemCredentialsRequired'] = false;
-		return $data;
-	}
-
-	ob_start();
-	$stored = request_filesystem_credentials( self_admin_url() );
-	ob_end_clean();
-
-	$data['filesystemCredentialsRequired'] = false === $stored;
-	return $data;
-}
-add_filter( 'script_module_data_options-connectors-wp-admin', '_gutenberg_connectors_add_filesystem_data_to_script_module_data' );
diff --git a/routes/connectors-home/ai-plugin-callout.tsx b/routes/connectors-home/ai-plugin-callout.tsx
index 1b9a2d70d59..2b9e4192cb3 100644
--- a/routes/connectors-home/ai-plugin-callout.tsx
+++ b/routes/connectors-home/ai-plugin-callout.tsx
@@ -17,10 +17,7 @@ import { addQueryArgs } from '@wordpress/url';
 /**
  * Internal dependencies
  */
-import {
-	getConnectorData,
-	getRequiresFilesystemCredentials,
-} from './default-connectors';
+import { getConnectorData } from './default-connectors';
 import { WpLogoDecoration } from './wp-logo-decoration';
 import { installPluginViaWpUpdates } from './use-connector-plugin';
 
@@ -134,12 +131,27 @@ export function AiPluginCallout() {
 	const installPlugin = async () => {
 		setIsBusy( true );
 		try {
-			if ( getRequiresFilesystemCredentials() ) {
-				// The REST plugin endpoint rejects installs up front when
-				// FS_METHOD is not 'direct' and credentials have not been
-				// stored. Route through wp.updates.installPlugin() so the
-				// standard "Connection Information" modal handles credential
-				// entry, then activate via REST.
+			try {
+				// Standard install via the REST plugin endpoint.
+				await saveEntityRecord(
+					'root',
+					'plugin',
+					{ slug: AI_PLUGIN_SLUG, status: 'active' },
+					{ throwOnError: true }
+				);
+			} catch ( error ) {
+				// The REST plugin endpoint rejects installs up front with
+				// the `fs_unavailable` error code when FS_METHOD is not
+				// 'direct' and credentials have not been stored. Route
+				// through wp.updates.installPlugin() so the standard
+				// "Connection Information" modal handles credential entry,
+				// then activate via REST.
+				console.log( error );
+				if (
+					( error as { code?: string } )?.code !== 'fs_unavailable'
+				) {
+					throw error;
+				}
 				const outcome =
 					await installPluginViaWpUpdates( AI_PLUGIN_SLUG );
 				if ( outcome === 'canceled' ) {
@@ -152,13 +164,6 @@ export function AiPluginCallout() {
 					{ plugin: AI_PLUGIN_ID, status: 'active' },
 					{ throwOnError: true }
 				);
-			} else {
-				await saveEntityRecord(
-					'root',
-					'plugin',
-					{ slug: AI_PLUGIN_SLUG, status: 'active' },
-					{ throwOnError: true }
-				);
 			}
 			setJustActivated( true );
 			createSuccessNotice(
diff --git a/routes/connectors-home/default-connectors.tsx b/routes/connectors-home/default-connectors.tsx
index 00824f0bce0..2f2dec2db0e 100644
--- a/routes/connectors-home/default-connectors.tsx
+++ b/routes/connectors-home/default-connectors.tsx
@@ -46,7 +46,6 @@ interface ConnectorData {
 interface ConnectorScriptModuleData {
 	connectors?: Record< string, ConnectorData >;
 	isFileModDisabled?: boolean;
-	filesystemCredentialsRequired?: boolean;
 }
 
 function getConnectorScriptModuleData(): ConnectorScriptModuleData {
@@ -72,10 +71,6 @@ export function getIsFileModDisabled(): boolean {
 	return !! getConnectorScriptModuleData().isFileModDisabled;
 }
 
-export function getRequiresFilesystemCredentials(): boolean {
-	return !! getConnectorScriptModuleData().filesystemCredentialsRequired;
-}
-
 const CONNECTOR_LOGOS: Record< string, React.ComponentType > = {
 	google: GeminiLogo,
 	openai: OpenAILogo,
@@ -175,7 +170,6 @@ function ApiKeyConnector( {
 		isActivated: plugin?.isActivated,
 		keySource: auth?.keySource,
 		initialIsConnected: auth?.isConnected,
-		requiresFilesystemCredentials: getRequiresFilesystemCredentials(),
 	} );
 	const isExternallyConfigured =
 		keySource === 'env' || keySource === 'constant';
diff --git a/routes/connectors-home/test/ai-plugin-callout.tsx b/routes/connectors-home/test/ai-plugin-callout.tsx
index 50af7dd98fb..7223e04df9f 100644
--- a/routes/connectors-home/test/ai-plugin-callout.tsx
+++ b/routes/connectors-home/test/ai-plugin-callout.tsx
@@ -13,7 +13,6 @@ import { useDispatch, useSelect } from '@wordpress/data';
  * Internal dependencies
  */
 import { AiPluginCallout } from '../ai-plugin-callout';
-import { getRequiresFilesystemCredentials } from '../default-connectors';
 import { installPluginViaWpUpdates } from '../use-connector-plugin';
 
 jest.mock( '@wordpress/a11y', () => ( {
@@ -95,7 +94,6 @@ jest.mock( '../default-connectors', () => ( {
 			},
 		},
 	} ) ),
-	getRequiresFilesystemCredentials: jest.fn( () => false ),
 } ) );
 
 jest.mock( '../use-connector-plugin', () => ( {
@@ -165,10 +163,6 @@ describe( 'AiPluginCallout', () => {
 				mapSelect( () => selectorStore )
 		);
 
-		( getRequiresFilesystemCredentials as jest.Mock ).mockReset();
-		( getRequiresFilesystemCredentials as jest.Mock ).mockReturnValue(
-			false
-		);
 		( installPluginViaWpUpdates as jest.Mock ).mockReset();
 		( installPluginViaWpUpdates as jest.Mock ).mockResolvedValue(
 			'installed'
@@ -218,12 +212,21 @@ describe( 'AiPluginCallout', () => {
 		expect( installPluginViaWpUpdates ).not.toHaveBeenCalled();
 	} );
 
-	it( 'installs via wp.updates then activates when filesystem credentials are required', async () => {
+	it( 'falls back to wp.updates then activates when the REST install reports fs_unavailable', async () => {
 		const user = userEvent.setup();
 
 		storeState.plugin = undefined;
-		( getRequiresFilesystemCredentials as jest.Mock ).mockReturnValue(
-			true
+		// The REST install (using `slug`) is rejected up front; the
+		// subsequent activate (using `plugin`) succeeds.
+		mockSaveEntityRecord.mockImplementation(
+			(
+				_kind: string,
+				_name: string,
+				record: Record< string, unknown >
+			) =>
+				'slug' in record
+					? Promise.reject( { code: 'fs_unavailable' } )
+					: Promise.resolve( undefined )
 		);
 		( installPluginViaWpUpdates as jest.Mock ).mockResolvedValue(
 			'installed'
@@ -247,15 +250,15 @@ describe( 'AiPluginCallout', () => {
 				{ throwOnError: true }
 			);
 		} );
+
+		expect( mockCreateErrorNotice ).not.toHaveBeenCalled();
 	} );
 
 	it( 'aborts silently when the filesystem credentials modal is canceled', async () => {
 		const user = userEvent.setup();
 
 		storeState.plugin = undefined;
-		( getRequiresFilesystemCredentials as jest.Mock ).mockReturnValue(
-			true
-		);
+		mockSaveEntityRecord.mockRejectedValue( { code: 'fs_unavailable' } );
 		( installPluginViaWpUpdates as jest.Mock ).mockResolvedValue(
 			'canceled'
 		);
@@ -270,7 +273,31 @@ describe( 'AiPluginCallout', () => {
 			expect( installPluginViaWpUpdates ).toHaveBeenCalledWith( 'ai' );
 		} );
 
-		expect( mockSaveEntityRecord ).not.toHaveBeenCalled();
+		// Only the initial REST install attempt ran — no activate follow-up,
+		// no error notice.
+		expect( mockSaveEntityRecord ).toHaveBeenCalledTimes( 1 );
 		expect( mockCreateErrorNotice ).not.toHaveBeenCalled();
 	} );
+
+	it( 'shows an error notice when the REST install fails for another reason', async () => {
+		const user = userEvent.setup();
+
+		storeState.plugin = undefined;
+		mockSaveEntityRecord.mockRejectedValue( {
+			code: 'rest_cannot_install',
+		} );
+
+		render( <AiPluginCallout /> );
+
+		await user.click(
+			screen.getByRole( 'button', { name: 'Install the AI plugin' } )
+		);
+
+		await waitFor( () => {
+			expect( mockCreateErrorNotice ).toHaveBeenCalled();
+		} );
+
+		// A non-filesystem error must not trigger the wp.updates fallback.
+		expect( installPluginViaWpUpdates ).not.toHaveBeenCalled();
+	} );
 } );
diff --git a/routes/connectors-home/use-connector-plugin.ts b/routes/connectors-home/use-connector-plugin.ts
index 0cd16228f31..7d4a9cdb431 100644
--- a/routes/connectors-home/use-connector-plugin.ts
+++ b/routes/connectors-home/use-connector-plugin.ts
@@ -19,7 +19,6 @@ interface UseConnectorPluginOptions {
 	isActivated?: boolean;
 	keySource?: ApiKeySource;
 	initialIsConnected?: boolean;
-	requiresFilesystemCredentials?: boolean;
 }
 
 interface WpUpdatesInstallPluginResponse {
@@ -38,7 +37,7 @@ interface WpUpdates {
 }
 
 type JQueryHandler = (
-	event: unknown,
+	event: JQuery.TriggeredEvent,
 	response:
 		| WpUpdatesInstallPluginResponse
 		| WpUpdatesInstallPluginErrorResponse
@@ -48,10 +47,7 @@ interface WpUpdatesGlobal {
 	wp?: {
 		updates?: WpUpdates;
 	};
-	jQuery?: ( target: Document ) => {
-		on: ( event: string, handler: JQueryHandler ) => unknown;
-		off: ( event: string, handler: JQueryHandler ) => unknown;
-	};
+	jQuery?: JQueryStatic;
 }
 
 /**
@@ -145,7 +141,6 @@ export function useConnectorPlugin( {
 	isActivated,
 	keySource = 'none',
 	initialIsConnected = false,
-	requiresFilesystemCredentials = false,
 }: UseConnectorPluginOptions ): UseConnectorPluginReturn {
 	const [ isExpanded, setIsExpanded ] = useState( false );
 	const [ isBusy, setIsBusy ] = useState( false );
@@ -269,12 +264,26 @@ export function useConnectorPlugin( {
 		}
 		setIsBusy( true );
 		try {
-			if ( requiresFilesystemCredentials ) {
-				// The REST plugin endpoint rejects installs up front when
-				// FS_METHOD is not 'direct' and credentials have not been
-				// stored. Route through wp.updates.installPlugin() so the
-				// standard "Connection Information" modal handles credential
-				// entry, then activate via REST.
+			try {
+				// Standard install via the REST plugin endpoint.
+				await saveEntityRecord(
+					'root',
+					'plugin',
+					{ slug: pluginSlug, status: 'active' },
+					{ throwOnError: true }
+				);
+			} catch ( error ) {
+				// The REST plugin endpoint rejects installs up front with
+				// the `fs_unavailable` error code when FS_METHOD is not
+				// 'direct' and credentials have not been stored. Route
+				// through wp.updates.installPlugin() so the standard
+				// "Connection Information" modal handles credential entry,
+				// then activate via REST.
+				if (
+					( error as { code?: string } )?.code !== 'fs_unavailable'
+				) {
+					throw error;
+				}
 				const outcome = await installPluginViaWpUpdates( pluginSlug );
 				if ( outcome === 'canceled' ) {
 					// User closed the credentials modal; abort silently.
@@ -294,13 +303,6 @@ export function useConnectorPlugin( {
 					'plugin',
 					pluginBasename,
 				] );
-			} else {
-				await saveEntityRecord(
-					'root',
-					'plugin',
-					{ slug: pluginSlug, status: 'active' },
-					{ throwOnError: true }
-				);
 			}
 			setPluginStatusOverride( 'active' );
 			// Re-fetch settings since the new plugin may register new settings.

}

type JQueryHandler = (
event: JQuery.TriggeredEvent,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Personally, I would like to avoid installing @types/jquery just for this. In the future, we will rarely use jQuery in TypeScript. Could we perhaps emulate a minimal custom type as follows?

type JQueryHandler = (
	event: unknown,
	response:
		| WpUpdatesInstallPluginResponse
		| WpUpdatesInstallPluginErrorResponse
) => void;

interface WpUpdatesGlobal {
	wp?: {
		updates?: {
			installPlugin: ( args: { slug: string } ) => void;
		};
	};
	jQuery?: ( target: Document ) => {
		on: ( events: string, handler: JQueryHandler ) => void;
		off: ( events: string, handler: JQueryHandler ) => void;
	};
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@t-hamano So you want to revert 530755e?

Note that core uses this same @types/jquery package.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Note that core uses this same @types/jquery package.

Oh, I didn't realize that. I just felt uneasy adding jQuery types to the root package.json solely to fix one issue, but I don't have a strong opinion.

@ekamran

ekamran commented May 26, 2026

Copy link
Copy Markdown
Contributor

I tested the latest PR head again locally.

Current implementation

The focused AiPluginCallout unit test passes for me:

npm run test:unit -- routes/connectors-home/test/ai-plugin-callout.tsx --runInBand

Result: 4 tests passed.

Alternative fs_unavailable approach

I also tried the alternative idea mentioned above, where we first try the REST plugin install, catch fs_unavailable, and then fall back to wp.updates.installPlugin().

At the unit-test level, that approach also looks workable. I added coverage for:

  • normal REST install
  • fs_unavailable fallback to wp.updates.installPlugin()
  • cancelling the filesystem credentials modal
  • another REST error, to make sure it does not incorrectly fall back

Result: 5 tests passed.

I also ran lint on the touched JS/TS files. It passed with 0 errors. There are 2 existing hook dependency warnings, but no new lint errors from this change.

Notes

From my testing, the current PR looks good. The alternative fs_unavailable approach also seems workable, and may be simpler because it uses the REST API error as the source of truth instead of exposing filesystemCredentialsRequired from PHP.

One important note: even with that alternative, the Connectors screen still needs to enqueue the updates script and print the filesystem credentials modal markup, otherwise wp.updates.installPlugin() cannot show the modal.

I also checked the failing checks:

  • the compressed-size job looks unrelated; the log shows the runner received a shutdown signal during the base build
  • the Core backport changelog check looks expected until there is a wordpress-develop backport PR/changelog entry

@gziolo
gziolo removed their request for review July 1, 2026 04:30
@cbravobernal cbravobernal moved this from 🔎 Needs Review to 🦵 Punted to 7.1 in WordPress 7.0 Editor Tasks Jul 7, 2026
@github-project-automation github-project-automation Bot moved this to 🔎 Needs Review in WordPress 7.2 Editor Tasks Aug 13, 2026
@t-hamano t-hamano removed the Backport to WP Minor Release Pull request that needs to be backported to a WordPress minor release label Aug 26, 2026
@ekamran

ekamran commented Oct 14, 2026

Copy link
Copy Markdown
Contributor

@westonruter would it help if I build t-hamano's fs_unavailable approach on current trunk, with the conflicts fixed and the tests updated, so you can pull it into this PR or review it separately? I tested both approaches in May and both worked. cc @t-hamano

@github-actions

github-actions Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

🤖 PR meta 🤖

🎉 Props

Unlinked Accounts

The following contributors have not linked their GitHub and WordPress.org accounts: @claude.

Contributors, please read how to link your accounts to ensure your work is properly credited in WordPress releases.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Unlinked contributors: claude.

Co-authored-by: westonruter <westonruter@git.wordpress.org>
Co-authored-by: t-hamano <wildworks@git.wordpress.org>
Co-authored-by: ekamran <ekamran@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

Updated as activity occurs, without notifying anyone named here. Add the props-bot label to refresh.

📦 Bundle size

Size Change: 0 B

Total Size: 8.64 MB

cc37947 Run

⚡ Performance

Show the results

Client side metrics exclude the server response time.

front-end-block-theme

Metric fbbd311 trunk % Change
timeToFirstByte 54.9 ms +14.12% -3.28% 54.8 ms +12.23% -3.38% 0.18%
largestContentfulPaint 100 ms +4% -12% 92 ms +4.35% -4.35% 8.7%
lcpMinusTtfb 41.85 ms +7.89% -15.53% 35.05 ms +3.28% -4.28% 19.4%
wpBeforeTemplate 30.21 ms +12.28% -6.82% 28.72 ms +18.59% -0.49% 5.19%
wpTemplate 21.07 ms +5.89% -3.99% 20.78 ms +9.29% -2.84% 1.4%
wpTotal 51.01 ms +13.06% -3.43% 50.8 ms +12.15% -3.56% 0.41%
wpMemoryUsage 7.59 MB +0% -0% 7.55 MB +0% -0% 0.46%
wpDbQueries 17 +0% -0% 17 +0% -0% 0%

front-end-classic-theme

Metric fbbd311 trunk % Change
timeToFirstByte 42.3 ms +6.26% -2.6% 45.95 ms +3.7% -2.07% -7.94%
largestContentfulPaint 92 ms +2.17% -0% 100 ms +4% -0% -8%
lcpMinusTtfb 49.5 ms +1.31% -4.14% 55.05 ms +2.54% -1.73% -10.08%
wpBeforeTemplate 26.51 ms +4.26% -0.98% 26.83 ms +1.94% -2.27% -1.19%
wpTemplate 12.12 ms +5.69% -1.98% 16.1 ms +1.55% -1.93% -24.72%
wpTotal 39.33 ms +6.94% -2.87% 42.93 ms +3.98% -2.28% -8.39%
wpMemoryUsage 6.14 MB +0% -0% 6.21 MB +0% -0% -1.07%
wpDbQueries 10 +0% -0% 14 +0% -0% -28.57%

media-processing

Metric fbbd311 trunk % Change
mediaProcessingJpeg 407.88 ms +0.74% -0.52% 401.61 ms +0.12% -0.63% 1.56%
mediaProcessingAvif 6036.9 ms +0.23% -0.2% 6040.9 ms +0.33% -0.02% -0.07%
mediaProcessingJpegToAvif 4162.72 ms +0.41% -0.28% 4174.84 ms +0.26% -0.25% -0.29%

media-upload

Metric fbbd311 trunk % Change
jpegUploadProcessing 1465.92 ms +32.9% -3.15% 1428.32 ms +1.36% -0.8% 2.63%
pngUploadProcessing 183.11 ms +12.8% -3.09% 190.55 ms +16.44% -8.51% -3.9%
largeJpegUploadProcessing 1419.02 ms +0.41% -0.51% 1421.45 ms +0.46% -0.76% -0.17%
multipleImageUploadProcessing 1557.72 ms +1.04% -1.1% 1598.95 ms +1.77% -0.83% -2.58%

post-editor

Metric fbbd311 trunk % Change
serverResponse 498.2 ms +2.24% -7.21% 520.33 ms +1.44% -10.78% -4.25%
firstPaint 283.81 ms +78.22% -13.01% 277.04 ms +55.54% -8.12% 2.44%
domContentLoaded 1166.01 ms +2.34% -1.72% 1175.59 ms +2.23% -5.1% -0.81%
loaded 1167.34 ms +2.36% -1.7% 1176.86 ms +2.24% -5.07% -0.81%
firstContentfulPaint 509.07 ms +2.11% -6.67% 501.7 ms +1.98% -14.11% 1.47%
firstBlock 3509.96 ms +1.14% -1.84% 3496.61 ms +0.59% -0.6% 0.38%
type 18.4 ms +2.99% -5.6% 18.38 ms +2.39% -7.24% 0.11%
typeWithoutInspector 17.86 ms +10.02% -2.63% 17.5 ms +1.83% -3.26% 2.06%
typeWithTopToolbar 23.19 ms +4.27% -7.76% 22.34 ms +4.39% -2.33% 3.8%
typeContainer 8.71 ms +20.09% -5.97% 8.46 ms +10.17% -5.91% 2.96%
focus 120.18 ms +5.9% -5.08% 112.43 ms +11.81% -2.92% 6.89%
firstFocus 214.43 ms +0% -0% 231.25 ms +0% -0% -7.27%
selectAll 560.68 ms +9.64% -1.47% 565.58 ms +6.02% -5.51% -0.87%
listViewOpen 75.78 ms +10.68% -3.15% 72.82 ms +7.8% -2.16% 4.06%
inserterOpen 42.93 ms +6.85% -6.87% 41.28 ms +3.44% -8.16% 4%
inserterHover 11.96 ms +12.29% -8.28% 11.55 ms +10.13% -9.78% 3.55%
inserterSearch 9.08 ms +5.51% -10.68% 8.45 ms +17.28% -6.04% 7.46%
loadPatterns 619.13 ms +0.63% -4.83% 626.15 ms +6.03% -4.36% -1.12%
wpTotal 486.14 ms +2.27% -7.18% 507.96 ms +1.69% -10.92% -4.3%
wpMemoryUsage 13.34 MB +0% -0% 13.31 MB +0% -0% 0.26%
wpDbQueries 54 +0% -1.85% 53.5 +0.93% -0.93% 0.93%

site-editor

Metric fbbd311 trunk % Change
serverResponse 534.72 ms +2.68% -7.33% 532.12 ms +2.41% -2.47% 0.49%
firstPaint 298.05 ms +99.69% -7.88% 297.02 ms +19.04% -17.73% 0.35%
domContentLoaded 1348.36 ms +0.88% -0.27% 1379.38 ms +1.33% -1.85% -2.25%
loaded 1349.82 ms +0.89% -0.26% 1381.18 ms +1.3% -1.88% -2.27%
firstContentfulPaint 570.07 ms +4.4% -19.05% 554.84 ms +3.19% -34.14% 2.74%
firstBlock 5051.88 ms +1.25% -0.51% 5109.57 ms +0.46% -1.21% -1.13%
type 23.87 ms +14.54% -4.15% 22.4 ms +12.23% -3.88% 6.56%
navigate 144.61 ms +12.7% -1.21% 144.54 ms +10.03% -1.36% 0.05%
loadPatterns 1432.51 ms +21.32% -3.52% 1513.29 ms +11.05% -8.65% -5.34%
loadPages 1091.11 ms +4.55% -2.59% 1066.63 ms +1.23% -1.29% 2.3%
wpTotal 523.17 ms +2.78% -7.52% 520.78 ms +2.4% -2.45% 0.46%
wpMemoryUsage 12.39 MB +0% -0% 12.35 MB +0% -0% 0.31%
wpDbQueries 43 +2.33% -0% 43 +2.33% -0% 0%

cc37947 Run

🏁 Flaky tests

Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

default to a list of pages if there are no menus in /test/e2e/specs/editor/blocks/navigation.spec.js, passed after 1 failed attempt.
Error: expect(locator).toBeVisible() failed

Locator: locator('[name="editor-canvas"]').contentFrame().getByRole('document', { name: 'Block: Page List' })
Expected: visible
Timeout: 10000ms
Error: element(s) not found

Call log:
  - Expect "toBeVisible" locator('[name="editor-canvas"]').contentFrame().getByRole('document', { name: 'Block: Page List' }) with timeout 10000ms
  - waiting for locator('[name="editor-canvas"]').contentFrame().getByRole('document', { name: 'Block: Page List' })

    at /home/runner/work/gutenberg/gutenberg/test/e2e/specs/editor/blocks/navigation.spec.js:34:34

cc37947 Run

claude and others added 3 commits October 14, 2026 09:48
Resolve conflicts after trunk bumped the minimum WordPress version to 7.0
and moved to Vitest:

- Move the Connectors filesystem credentials setup from the removed
  lib/compat/wordpress-7.0/connectors.php into lib/compat/plugin/connectors.php.
- Drop the root @types/jquery devDependency, since root devDependencies were
  moved into workspaces, and use minimal local jQuery types instead.
- Port the AiPluginCallout filesystem credentials tests to Vitest.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FVht4racQmKuYmELHoLXdx
…le error

Instead of computing `filesystemCredentialsRequired` on the server and
exposing it through script module data, attempt the standard REST plugin
install first and fall back to `wp.updates.installPlugin()` only when the
endpoint rejects with the `fs_unavailable` error code. The REST response
becomes the single source of truth, and the PHP flag is removed.

The Connectors screen still enqueues the `updates` script and prints the
filesystem credentials modal so the fallback can show it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FVht4racQmKuYmELHoLXdx
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: t-hamano <54422211+t-hamano@users.noreply.github.com>
Claude-Session: https://claude.ai/code/session_01FVht4racQmKuYmELHoLXdx
@t-hamano

Copy link
Copy Markdown
Contributor

@ekamran Thanks for the offer! I've resolved the conflicts with trunk and pushed the fs_unavailable approach directly to this PR. The install now tries the REST endpoint first and falls back to wp.updates.installPlugin() only when it rejects with fs_unavailable, so the filesystemCredentialsRequired flag is no longer needed. I've also linked the core backport PR WordPress/wordpress-develop#13953 and updated the PR description.

@westonruter, could you take a look when you have a chance?

@ekamran

ekamran commented Oct 14, 2026

Copy link
Copy Markdown
Contributor

Thanks for picking this up. I tested the latest head (cc37947) on wordpress-develop trunk with Weston's FTP setup (FS_METHOD set to ftpsockets). I also tested the core side with WordPress/wordpress-develop#13953, and that report goes on the Trac ticket.

The main flows work:

  • The AI plugin callout and the connector cards open the Connection Information modal. Correct credentials install and activate the plugin, and for a connector the API key form opens.
  • On the callout, Cancel aborts with no error snackbar, and a wrong password shows the FTP error and keeps the modal open.
  • A second install on the same page load reuses the entered credentials without a modal.
  • With FTP_HOST, FTP_USER and FTP_PASS defined, and on direct, the REST install succeeds and the fallback is not used.
  • npm run test:unit -- routes/connectors-home: 7 tests pass.

I found three issues:

  1. The button can stay on "Installing…". installPluginViaWpUpdates() only settles on wp-plugin-install-success, wp-plugin-install-error or credential-modal-cancel. Core's wp.updates.installPluginError() returns early when isValidResponse() fails, without firing wp-plugin-install-error. I reproduced it two ways: an expired nonce (wp.updates.ajaxNonce = 'x' in the console, so admin-ajax returns 403 -1), and an install request that returns an HTML 500 page, as a PHP fatal error or a host timeout would. In both cases core adds an "Installation failed" notice that is not visible on this screen, and the button was still on "Installing…" after 30 seconds. With the same HTML 500, Plugins > Add Plugin shows the notice and marks the button as failed.

  2. The modal opens with an error already shown. The first admin-ajax request goes out with empty credentials and returns unable_to_connect_to_filesystem, so the modal opens with "Unable to connect to the filesystem. Please confirm your credentials." before anything is typed. Plugins > Add Plugin calls wp.updates.requestFilesystemCredentials( event ) before installPlugin(), so its modal opens empty and nothing is sent until Proceed.

  3. Focus is lost when the modal closes. After Cancel, Escape or Proceed, focus goes to <body>. On Add Plugin it returns to the Install Now button, because the click event is passed to requestFilesystemCredentials().

For 2 and 3, I tried calling wp.updates.requestFilesystemCredentials( { target: document.activeElement } ) before installPlugin(), patched from the console, only when shouldRequestFilesystemCredentials is true and ajaxLocked is false. The modal opened empty with no request sent, Escape returned focus to the Install button, and Proceed installed the plugin with a single admin-ajax request.

This branch has not been deployed

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

Labels

[Feature] Connectors screen Tracks connectors screen related tasks [Type] Bug An existing feature does not function as intended

Projects

Status: 🔎 Needs Review

Development

Successfully merging this pull request may close these issues.

7 participants