diff --git a/wordpress.org/public_html/wp-content/plugins/wporg-gp-customizations/inc/class-plugin.php b/wordpress.org/public_html/wp-content/plugins/wporg-gp-customizations/inc/class-plugin.php
index 161c3e090e..d068a15547 100644
--- a/wordpress.org/public_html/wp-content/plugins/wporg-gp-customizations/inc/class-plugin.php
+++ b/wordpress.org/public_html/wp-content/plugins/wporg-gp-customizations/inc/class-plugin.php
@@ -67,6 +67,7 @@ private function __construct() {
function plugins_loaded() {
add_filter( 'gp_url_profile', array( $this, 'worg_profile_url' ), 10, 2 );
add_filter( 'gp_projects', array( $this, 'natural_sort_projects' ), 10, 2 );
+ add_filter( 'gp_breadcrumb_items', array( $this, 'merge_directory_breadcrumbs' ), 5 );
add_action( 'gp_project_created', array( $this, 'update_projects_last_updated' ) );
add_action( 'gp_project_saved', array( $this, 'update_projects_last_updated' ) );
add_filter( 'pre_handle_404', array( $this, 'short_circuit_handle_404' ) );
@@ -758,6 +759,43 @@ public function worg_profile_url( $url, $nicename ) {
return 'https://profiles.wordpress.org/' . $nicename . '/';
}
+ /**
+ * Merges the Plugins and Themes breadcrumbs into the project breadcrumb that follows them.
+ *
+ * The wp-plugins and wp-themes archives redirect to the homepage, so they shouldn't be linked.
+ * Without a project link to merge into, the label is kept as plain text.
+ *
+ * @param string[] $items Breadcrumb items as HTML strings.
+ * @return string[] Filtered breadcrumb items.
+ */
+ public function merge_directory_breadcrumbs( $items ) {
+ $items = array_values( $items );
+
+ foreach ( $items as $index => $item ) {
+ if ( ! isset( $items[ $index + 1 ] ) ) {
+ break;
+ }
+
+ foreach ( array( 'wp-plugins', 'wp-themes' ) as $path ) {
+ $url = esc_url( gp_url_project( $path ) );
+
+ if ( ! str_starts_with( $item, '