*/ private static function get_settings_feature_groups(): array { $default_groups = array( Experiment_Category::EDITOR => array( 'label' => __( 'Editor Experiments', 'ai' ), 'description' => __( 'Early-stage AI tools for the block editor, including content generation and enhancement tools. Experiments may change or be removed in any release.', 'ai' ), 'order' => 10, ), Experiment_Category::ADMIN => array( 'label' => __( 'Admin Experiments', 'ai' ), 'description' => __( 'Early-stage AI tools for the WordPress admin area, including exploration and testing tools. Experiments may change or be removed in any release.', 'ai' ), 'order' => 20, ), Feature_Category::OTHER => array( 'label' => __( 'Other Features', 'ai' ), 'description' => __( 'Additional AI-powered features.', 'ai' ), 'order' => 90, ), ); /** * Filters feature group metadata used by the settings UI. * * @since 0.7.0 * * @param array $default_groups Feature group metadata keyed by category. */ $filtered_groups = apply_filters( 'wpai_settings_feature_groups', $default_groups ); return is_array( $filtered_groups ) ? $filtered_groups : $default_groups; } /** * Gets the documentation URL for one of the plugin's own features. * * @since 1.5.0 * * @param \WordPress\AI\Contracts\Feature $feature Feature instance. * @return string Documentation URL, or an empty string for features registered by other plugins. */ private static function get_feature_docs_url( Feature $feature ): string { $class = get_class( $feature ); if ( 0 !== strpos( $class, 'WordPress\\AI\\Experiments\\' ) && 0 !== strpos( $class, 'WordPress\\AI\\Features\\' ) ) { return ''; } $folder = 'stable' === $feature->get_stability() ? 'features' : 'experiments'; return sprintf( 'https://github.com/WordPress/ai/blob/trunk/docs/%1$s/%2$s.md#at-a-glance', $folder, rawurlencode( $feature::get_id() ) ); } /** * Builds feature metadata used by the settings route UI. * * @since 0.8.0 * * @param \WordPress\AI\Features\Registry $registry Feature registry instance. * @return array{ * groups: list, * features: list, * isValid?: array{min?: int, max?: int} * }>, * usage: array{trigger: 'automatic'|'on_demand'|'none', runs: string, sends: string, requests: string, details: string}|array{}, * docsUrl: string * }> * } */ private static function get_settings_feature_metadata( Registry $registry ): array { $group_definitions = self::get_settings_feature_groups(); $categories_in_use = array(); $features = array(); foreach ( $registry->get_all_features() as $feature ) { $feature_id = $feature::get_id(); $category = $feature->get_category(); if ( ! is_string( $category ) || '' === $category ) { $category = Feature_Category::OTHER; } if ( ! isset( $group_definitions[ $category ] ) ) { $group_definitions[ $category ] = array( 'label' => ucwords( str_replace( array( '-', '_' ), ' ', $category ) ), 'description' => '', 'order' => 100, ); } $categories_in_use[ $category ] = true; $features[] = array( 'id' => $feature_id, 'settingName' => "wpai_feature_{$feature_id}_enabled", 'label' => $feature->get_label(), 'description' => wp_strip_all_tags( $feature->get_description() ), 'category' => $category, 'settingsFields' => $feature->get_settings_fields_metadata(), 'stability' => $feature->get_stability(), 'image' => esc_url( $feature->get_image() ), 'capability' => $feature->get_capability(), 'usage' => $feature instanceof Abstract_Feature ? $feature->get_usage() : array(), 'docsUrl' => self::get_feature_docs_url( $feature ), ); } $groups = array(); foreach ( array_keys( $categories_in_use ) as $category ) { $group = $group_definitions[ $category ] ?? array(); $groups[] = array( 'id' => $category, 'label' => isset( $group['label'] ) && is_string( $group['label'] ) && '' !== $group['label'] ? $group['label'] : ucwords( str_replace( array( '-', '_' ), ' ', $category ) ), 'description' => isset( $group['description'] ) && is_string( $group['description'] ) ? $group['description'] : '', 'order' => isset( $group['order'] ) ? (int) $group['order'] : 100, ); } usort( $groups, static function ( array $first, array $second ): int { if ( $first['order'] === $second['order'] ) { return strcasecmp( (string) $first['label'], (string) $second['label'] ); } return $first['order'] <=> $second['order']; } ); $groups = array_values( array_map( static function ( array $group ): array { unset( $group['order'] ); return $group; }, $groups ) ); $metadata = array( 'groups' => $groups, 'features' => $features, ); /** * Filters settings metadata passed to the settings route client. * * @since 0.7.0 * * @param array{ * groups: list, * features: list, * isValid?: array{min?: int, max?: int} * }>, * usage: array{trigger: 'automatic'|'on_demand'|'none', runs: string, sends: string, requests: string, details: string}|array{}, * docsUrl: string * }> * } $metadata Settings UI metadata. * @param \WordPress\AI\Features\Registry $registry Feature registry instance. */ $filtered_metadata = apply_filters( 'wpai_settings_feature_metadata', $metadata, $registry ); return is_array( $filtered_metadata ) ? $filtered_metadata : $metadata; } }