diff --git a/.github/workflows/reusable-check-built-files.yml b/.github/workflows/reusable-check-built-files.yml index 10f4dbb76d8c0..cc91c4bc96ada 100644 --- a/.github/workflows/reusable-check-built-files.yml +++ b/.github/workflows/reusable-check-built-files.yml @@ -25,9 +25,10 @@ jobs: # - Builds Emoji files. # - Builds bundled Root Certificate files. # - Builds WordPress. - # - Checks for changes to versioned files. - # - Displays the result of git diff for debugging purposes. - # - Saves the diff to a patch file. + # - Checks for uncommitted changes. + # - Stages all uncommitted changes and adds any unversioned files. + # - Displays a diff of all staged changes. + # - Saves staged changes to a .diff file. # - Uploads the patch file as an artifact. update-built-files: name: Check and update built files @@ -85,22 +86,26 @@ jobs: - name: Build WordPress run: npm run build:dev - - name: Check for changes to versioned files + - name: Check for uncommitted changes id: built-file-check run: | - if git diff --quiet; then + if [ -z "$(git status --porcelain)" ]; then echo "uncommitted_changes=false" >> "$GITHUB_OUTPUT" else echo "uncommitted_changes=true" >> "$GITHUB_OUTPUT" fi - - name: Display changes to versioned files + - name: Stage all changes for diff generation if: ${{ steps.built-file-check.outputs.uncommitted_changes == 'true' }} - run: git diff + run: git add -A + + - name: Display all uncommitted changes + if: ${{ steps.built-file-check.outputs.uncommitted_changes == 'true' }} + run: git diff --cached - name: Save diff to a file if: ${{ steps.built-file-check.outputs.uncommitted_changes == 'true' }} - run: git diff > ./changes.diff + run: git diff --cached --binary > ./changes.diff # Uploads the diff file as an artifact. - name: Upload diff file as artifact diff --git a/.github/workflows/reusable-coding-standards-javascript.yml b/.github/workflows/reusable-coding-standards-javascript.yml index eac5bbdc352f2..b15a5bacf6d46 100644 --- a/.github/workflows/reusable-coding-standards-javascript.yml +++ b/.github/workflows/reusable-coding-standards-javascript.yml @@ -24,7 +24,7 @@ jobs: # - Logs debug information about the GitHub Action runner. # - Installs npm dependencies. # - Run the WordPress JSHint checks. - # - Ensures version-controlled files are not modified or deleted. + # - Checks for any uncommitted changes. jshint: name: JavaScript checks runs-on: ubuntu-24.04 @@ -57,5 +57,10 @@ jobs: - name: Run JSHint run: npm run grunt jshint - - name: Ensure version-controlled files are not modified or deleted - run: git diff --exit-code + - name: Check for uncommitted changes + run: | + if [ -n "$(git status --porcelain)" ]; then + echo "Uncommitted changes detected:" + git status --porcelain + exit 1 + fi diff --git a/.github/workflows/reusable-coding-standards-php.yml b/.github/workflows/reusable-coding-standards-php.yml index 709c598872b23..bff2282fb1296 100644 --- a/.github/workflows/reusable-coding-standards-php.yml +++ b/.github/workflows/reusable-coding-standards-php.yml @@ -36,7 +36,7 @@ jobs: # - Generate a report for displaying issues as pull request annotations. # - Runs PHPCS on the `tests` directory without (warnings included). # - Generate a report for displaying `test` directory issues as pull request annotations. - # - Ensures version-controlled files are not modified or deleted. + # - Checks for any uncommitted changes. phpcs: name: PHP checks runs-on: ubuntu-24.04 @@ -104,5 +104,10 @@ jobs: if: ${{ inputs.old-branch }} run: phpcbf - - name: Ensure version-controlled files are not modified during the tests - run: git diff --exit-code + - name: Check for uncommitted changes + run: | + if [ -n "$(git status --porcelain)" ]; then + echo "Uncommitted changes detected:" + git status --porcelain + exit 1 + fi diff --git a/.github/workflows/reusable-end-to-end-tests.yml b/.github/workflows/reusable-end-to-end-tests.yml index 87f90f1b53039..c39e03a6c0ab0 100644 --- a/.github/workflows/reusable-end-to-end-tests.yml +++ b/.github/workflows/reusable-end-to-end-tests.yml @@ -61,7 +61,7 @@ jobs: # - Install additional languages. # - Run the E2E tests. # - Uploads screenshots and HTML snapshots as an artifact. - # - Ensures version-controlled files are not modified or deleted. + # - Checks for any uncommitted changes. e2e-tests: name: SCRIPT_DEBUG ${{ inputs.LOCAL_SCRIPT_DEBUG && 'enabled' || 'disabled' }} runs-on: ubuntu-24.04 @@ -153,5 +153,10 @@ jobs: if-no-files-found: ignore include-hidden-files: true - - name: Ensure version-controlled files are not modified or deleted - run: git diff --exit-code + - name: Check for uncommitted changes + run: | + if [ -n "$(git status --porcelain)" ]; then + echo "Uncommitted changes detected:" + git status --porcelain + exit 1 + fi diff --git a/.github/workflows/reusable-javascript-tests.yml b/.github/workflows/reusable-javascript-tests.yml index 3988ec9d6b055..d260dd71c4c11 100644 --- a/.github/workflows/reusable-javascript-tests.yml +++ b/.github/workflows/reusable-javascript-tests.yml @@ -25,7 +25,7 @@ jobs: # - Logs debug information about the GitHub Action runner. # - Installs npm dependencies. # - Run the WordPress QUnit tests. - # - Ensures version-controlled files are not modified or deleted. + # - Checks for any uncommitted changes. test-js: name: Run QUnit tests runs-on: ubuntu-24.04 @@ -67,5 +67,10 @@ jobs: - name: Run QUnit tests run: npm run grunt qunit:compiled - - name: Ensure version-controlled files are not modified or deleted - run: git diff --exit-code + - name: Check for uncommitted changes + run: | + if [ -n "$(git status --porcelain)" ]; then + echo "Uncommitted changes detected:" + git status --porcelain + exit 1 + fi diff --git a/.github/workflows/reusable-javascript-type-checking-v1.yml b/.github/workflows/reusable-javascript-type-checking-v1.yml index 9dabd01e27fa0..d1f484c39c36c 100644 --- a/.github/workflows/reusable-javascript-type-checking-v1.yml +++ b/.github/workflows/reusable-javascript-type-checking-v1.yml @@ -23,7 +23,7 @@ jobs: # - Configures caching for TypeScript build info. # - Runs JavaScript type checking. # - Saves the TypeScript build info. - # - Ensures version-controlled files are not modified or deleted. + # - Checks for any uncommitted changes. typecheck: name: Run JavaScript type checking runs-on: ubuntu-24.04 @@ -72,5 +72,10 @@ jobs: *.tsbuildinfo key: "ts-build-info-${{ github.run_id }}" - - name: Ensure version-controlled files are not modified or deleted - run: git diff --exit-code + - name: Check for uncommitted changes + run: | + if [ -n "$(git status --porcelain)" ]; then + echo "Uncommitted changes detected:" + git status --porcelain + exit 1 + fi diff --git a/.github/workflows/reusable-performance-test-v2.yml b/.github/workflows/reusable-performance-test-v2.yml index c0279c37fe64b..7bafb8fff4894 100644 --- a/.github/workflows/reusable-performance-test-v2.yml +++ b/.github/workflows/reusable-performance-test-v2.yml @@ -102,7 +102,7 @@ jobs: # - Install MU plugin. # - Run performance tests. # - Archive artifacts. - # - Ensure version-controlled files are not modified or deleted. + # - Checks for any uncommitted changes. performance: name: Test ${{ inputs.subject == 'base' && inputs.BASE_TAG || inputs.subject }} runs-on: ubuntu-24.04 @@ -272,5 +272,10 @@ jobs: if-no-files-found: error include-hidden-files: true - - name: Ensure version-controlled files are not modified or deleted - run: git diff --exit-code + - name: Check for uncommitted changes + run: | + if [ -n "$(git status --porcelain)" ]; then + echo "Uncommitted changes detected:" + git status --porcelain + exit 1 + fi diff --git a/.github/workflows/reusable-performance.yml b/.github/workflows/reusable-performance.yml index e10022c65380f..28f1f7dd61823 100644 --- a/.github/workflows/reusable-performance.yml +++ b/.github/workflows/reusable-performance.yml @@ -112,7 +112,7 @@ jobs: # - Set the base sha. # - Set commit details. # - Publish performance results. - # - Ensure version-controlled files are not modified or deleted. + # - Checks for any uncommitted changes. performance: name: ${{ inputs.multisite && 'Multisite' || 'Single site' }} / ${{ inputs.memcached && 'Memcached' || 'Default' }} runs-on: ubuntu-24.04 @@ -353,5 +353,10 @@ jobs: COMMITTED_AT="$(git show -s "$GITHUB_SHA" --format='%cI')" node ./tests/performance/log-results.js "$CODEVITALS_PROJECT_TOKEN" trunk "$GITHUB_SHA" "$BASE_SHA" "$COMMITTED_AT" "$HOST_NAME" - - name: Ensure version-controlled files are not modified or deleted - run: git diff --exit-code + - name: Check for uncommitted changes + run: | + if [ -n "$(git status --porcelain)" ]; then + echo "Uncommitted changes detected:" + git status --porcelain + exit 1 + fi diff --git a/.github/workflows/reusable-php-compatibility.yml b/.github/workflows/reusable-php-compatibility.yml index 7756330282e6f..ed7cf3abbfae2 100644 --- a/.github/workflows/reusable-php-compatibility.yml +++ b/.github/workflows/reusable-php-compatibility.yml @@ -30,7 +30,7 @@ jobs: # - Make Composer packages available globally. # - Runs the PHP compatibility tests. # - Generate a report for displaying issues as pull request annotations. - # - Ensures version-controlled files are not modified or deleted. + # - Checks for any uncommitted changes. php-compatibility: name: Run compatibility checks runs-on: ubuntu-24.04 @@ -86,5 +86,10 @@ jobs: if: ${{ always() && steps.phpcs.outcome == 'failure' }} run: cs2pr ./.cache/phpcs-compat-report.xml - - name: Ensure version-controlled files are not modified or deleted - run: git diff --exit-code + - name: Check for uncommitted changes + run: | + if [ -n "$(git status --porcelain)" ]; then + echo "Uncommitted changes detected:" + git status --porcelain + exit 1 + fi diff --git a/.github/workflows/reusable-phpstan-static-analysis-v1.yml b/.github/workflows/reusable-phpstan-static-analysis-v1.yml index 775500b5b2905..4efc43e013deb 100644 --- a/.github/workflows/reusable-phpstan-static-analysis-v1.yml +++ b/.github/workflows/reusable-phpstan-static-analysis-v1.yml @@ -30,7 +30,7 @@ jobs: # - Make Composer packages available globally. # - Runs PHPStan static analysis (with Pull Request annotations). # - Saves the PHPStan result cache. - # - Ensures version-controlled files are not modified or deleted. + # - Checks for any uncommitted changes. phpstan: name: Run PHP static analysis runs-on: ubuntu-24.04 @@ -105,5 +105,10 @@ jobs: path: .cache key: "phpstan-result-cache-${{ github.run_id }}" - - name: Ensure version-controlled files are not modified or deleted - run: git diff --exit-code + - name: Check for uncommitted changes + run: | + if [ -n "$(git status --porcelain)" ]; then + echo "Uncommitted changes detected:" + git status --porcelain + exit 1 + fi diff --git a/.github/workflows/reusable-phpunit-tests-v2.yml b/.github/workflows/reusable-phpunit-tests-v2.yml index 5e078b6ef0c2e..21f71546bdb2d 100644 --- a/.github/workflows/reusable-phpunit-tests-v2.yml +++ b/.github/workflows/reusable-phpunit-tests-v2.yml @@ -84,7 +84,7 @@ jobs: # - Logs debug information from inside the WordPress Docker container. # - Install WordPress within the Docker container. # - Run the PHPUnit tests. - # - Ensures version-controlled files are not modified or deleted. + # - Checks for any uncommitted changes. test-php: name: PHP ${{ inputs.php }} / ${{ inputs.multisite && ' Multisite' || 'Single Site' }}${{ inputs.split_slow && ' slow tests' || '' }}${{ inputs.memcached && ' with memcached' || '' }} runs-on: ${{ inputs.os }} @@ -208,5 +208,10 @@ jobs: if: ${{ ! inputs.split_slow }} run: LOCAL_PHP_XDEBUG=true npm run "test:${PHPUNIT_SCRIPT}" -- -v --group xdebug --exclude-group __fakegroup__ - - name: Ensure version-controlled files are not modified or deleted - run: git diff --exit-code + - name: Check for uncommitted changes + run: | + if [ -n "$(git status --porcelain)" ]; then + echo "Uncommitted changes detected:" + git status --porcelain + exit 1 + fi diff --git a/.github/workflows/reusable-phpunit-tests-v3.yml b/.github/workflows/reusable-phpunit-tests-v3.yml index bcad042dfe559..81ce4acd3d54e 100644 --- a/.github/workflows/reusable-phpunit-tests-v3.yml +++ b/.github/workflows/reusable-phpunit-tests-v3.yml @@ -113,7 +113,7 @@ jobs: # - Install WordPress within the Docker container. # - Run the PHPUnit tests. # - Upload the code coverage report to Codecov.io. - # - Ensures version-controlled files are not modified or deleted. + # - Checks for any uncommitted changes. # - Checks out the WordPress Test reporter repository. # - Submit the test results to the WordPress.org host test results. phpunit-tests: @@ -268,8 +268,13 @@ jobs: flags: ${{ inputs.multisite && 'multisite' || 'single' }},php fail_ci_if_error: true - - name: Ensure version-controlled files are not modified or deleted - run: git diff --exit-code + - name: Check for uncommitted changes + run: | + if [ -n "$(git status --porcelain)" ]; then + echo "Uncommitted changes detected:" + git status --porcelain + exit 1 + fi - name: Checkout the WordPress Test Reporter if: ${{ github.ref == 'refs/heads/trunk' && inputs.report }} diff --git a/.github/workflows/reusable-test-core-build-process.yml b/.github/workflows/reusable-test-core-build-process.yml index 1566d1583a807..ec702da6df376 100644 --- a/.github/workflows/reusable-test-core-build-process.yml +++ b/.github/workflows/reusable-test-core-build-process.yml @@ -54,10 +54,10 @@ jobs: # - Logs debug information about the GitHub Action runner. # - Installs npm dependencies. # - Builds WordPress to run from the desired location (src or build). - # - Ensures version-controlled files are not modified or deleted. + # - Checks for any uncommitted changes after building. # - Creates a ZIP of the built WordPress files (when building to the build directory). # - Cleans up after building WordPress. - # - Ensures version-controlled files are not modified or deleted. + # - Checks for any uncommitted changes after cleaning. # - Uploads the ZIP as a GitHub Actions artifact (when building to the build directory). # - Saves the pull request number to a text file. # - Uploads the pull request number as an artifact. @@ -119,8 +119,13 @@ jobs: - name: Build WordPress to run from ${{ inputs.directory }} run: npm run ${{ inputs.directory == 'src' && 'build:dev' || 'build' }} - - name: Ensure version-controlled files are not modified or deleted during building - run: git diff --exit-code + - name: Check for uncommitted changes after building + run: | + if [ -n "$(git status --porcelain)" ]; then + echo "Uncommitted changes detected:" + git status --porcelain + exit 1 + fi - name: Create ZIP of built files if: ${{ inputs.directory == 'build' && contains( inputs.os, 'ubuntu-' ) }} @@ -129,8 +134,13 @@ jobs: - name: Clean after building to run from ${{ inputs.directory }} run: npm run grunt ${{ inputs.directory == 'src' && 'clean -- --dev' || 'clean' }} - - name: Ensure version-controlled files are not modified or deleted during cleaning - run: git diff --exit-code + - name: Check for uncommitted changes after cleaning + run: | + if [ -n "$(git status --porcelain)" ]; then + echo "Uncommitted changes detected:" + git status --porcelain + exit 1 + fi - name: Upload ZIP as a GitHub Actions artifact uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 diff --git a/.github/workflows/reusable-test-gutenberg-build-process.yml b/.github/workflows/reusable-test-gutenberg-build-process.yml index 4a780d08ee07f..635d8dc0c473f 100644 --- a/.github/workflows/reusable-test-gutenberg-build-process.yml +++ b/.github/workflows/reusable-test-gutenberg-build-process.yml @@ -39,7 +39,7 @@ jobs: # - Installs Core npm dependencies. # - Builds WordPress to run from the relevant location (src or build). # - Builds Gutenberg. - # - Ensures version-controlled files are not modified or deleted. + # - Checks for any uncommitted changes after building. build-process-tests: name: ${{ contains( inputs.os, 'macos-' ) && 'MacOS' || contains( inputs.os, 'windows-' ) && 'Windows' || 'Linux' }} permissions: @@ -96,5 +96,10 @@ jobs: run: npm run build working-directory: ${{ env.GUTENBERG_DIRECTORY }} - - name: Ensure version-controlled files are not modified or deleted during building - run: git diff --exit-code + - name: Check for uncommitted changes after building + run: | + if [ -n "$(git status --porcelain)" ]; then + echo "Uncommitted changes detected:" + git status --porcelain + exit 1 + fi diff --git a/.github/workflows/reusable-test-local-docker-environment-v1.yml b/.github/workflows/reusable-test-local-docker-environment-v1.yml index 8f1a556afa2b4..50118d45045ee 100644 --- a/.github/workflows/reusable-test-local-docker-environment-v1.yml +++ b/.github/workflows/reusable-test-local-docker-environment-v1.yml @@ -71,7 +71,7 @@ jobs: # - Runs a WP CLI command. # - Tests the logs command. # - Tests the reset command. - # - Ensures version-controlled files are not modified or deleted. + # - Checks for any uncommitted changes. local-docker-environment-tests: name: ${{ 'mariadb' == inputs.db-type && 'MariaDB' || 'MySQL' }} ${{ inputs.db-version }}${{ inputs.memcached && ' with memcached' || '' }}${{ 'example.org' != inputs.tests-domain && format( ' {0}', inputs.tests-domain ) || '' }} permissions: @@ -166,5 +166,10 @@ jobs: - name: Reset the Docker environment run: npm run env:reset - - name: Ensure version-controlled files are not modified or deleted - run: git diff --exit-code + - name: Check for uncommitted changes + run: | + if [ -n "$(git status --porcelain)" ]; then + echo "Uncommitted changes detected:" + git status --porcelain + exit 1 + fi diff --git a/.github/workflows/test-and-zip-default-themes.yml b/.github/workflows/test-and-zip-default-themes.yml index 1a44a8ff12e3a..d80a1d9731af2 100644 --- a/.github/workflows/test-and-zip-default-themes.yml +++ b/.github/workflows/test-and-zip-default-themes.yml @@ -112,7 +112,12 @@ jobs: # - Sets up Node.js. # - Installs npm dependencies. # - Runs the theme build script. - # - Ensures version-controlled files are not modified or deleted. + # - Checks for uncommitted changes. + # - Stages all uncommitted changes and adds any unversioned files. + # - Displays a diff of all staged changes. + # - Saves staged changes to a .diff file. + # - Uploads the diff file as an artifact. + # - Fails the job when uncommitted changes are detected. test-build-scripts: name: Test ${{ matrix.theme }} build script runs-on: ubuntu-24.04 @@ -156,23 +161,27 @@ jobs: - name: Build theme run: npm run build - - name: Check for changes to versioned files + - name: Check for uncommitted changes id: built-file-check if: ${{ github.event_name == 'pull_request' }} run: | - if git diff --quiet; then + if [ -z "$(git status --porcelain)" ]; then echo "uncommitted_changes=false" >> "$GITHUB_OUTPUT" else echo "uncommitted_changes=true" >> "$GITHUB_OUTPUT" fi - - name: Display changes to versioned files + - name: Stage all changes for diff generation if: ${{ steps.built-file-check.outputs.uncommitted_changes == 'true' }} - run: git diff + run: git add -A + + - name: Display all uncommitted changes + if: ${{ steps.built-file-check.outputs.uncommitted_changes == 'true' }} + run: git diff --cached - name: Save diff to a file if: ${{ steps.built-file-check.outputs.uncommitted_changes == 'true' }} - run: git diff > ./changes.diff + run: git diff --cached --binary > ./changes.diff # Uploads the diff file as an artifact. - name: Upload diff file as artifact @@ -183,12 +192,20 @@ jobs: path: src/wp-content/themes/${{ matrix.theme }}/changes.diff - name: Ensure version-controlled files are not modified or deleted - run: git diff --exit-code + run: | + if [ -n "$(git status --porcelain)" ]; then + echo "Uncommitted changes detected after build:" + git status --porcelain + exit 1 + fi # Prepares bundled themes for release. # # Performs the following steps: # - Checks out the repository. + # - Sets up Node.js. + # - Installs npm dependencies. + # - Runs the theme build script. # - Uploads the theme files as a workflow artifact (files uploaded as an artifact are automatically zipped). bundle-theme: name: Create ${{ matrix.theme }} ZIP file diff --git a/.gitignore b/.gitignore index 15876fa47fee8..871c49e39ba6a 100644 --- a/.gitignore +++ b/.gitignore @@ -48,6 +48,7 @@ wp-tests-config.php /artifacts /setup.log /coverage +wordpress.zip # Files and folders that get created in wp-content /src/wp-content/blogs.dir diff --git a/src/wp-includes/blocks/navigation-link/shared/build-css-font-sizes.php b/src/wp-includes/blocks/navigation-link/shared/build-css-font-sizes.php new file mode 100644 index 0000000000000..38fd82d12dac8 --- /dev/null +++ b/src/wp-includes/blocks/navigation-link/shared/build-css-font-sizes.php @@ -0,0 +1,43 @@ + array(), + 'inline_styles' => '', + ); + + $has_named_font_size = array_key_exists( 'fontSize', $context ); + $has_custom_font_size = isset( $context['style']['typography']['fontSize'] ); + + if ( $has_named_font_size ) { + // Add the font size class. + $font_sizes['css_classes'][] = sprintf( 'has-%s-font-size', $context['fontSize'] ); + } elseif ( $has_custom_font_size ) { + // Add the custom font size inline style. + $font_sizes['inline_styles'] = sprintf( + 'font-size: %s;', + wp_get_typography_font_size_value( + array( + 'size' => $context['style']['typography']['fontSize'], + ) + ) + ); + } + + return $font_sizes; +} diff --git a/src/wp-includes/build/pages/content-types/loader.js b/src/wp-includes/build/pages/content-types/loader.js new file mode 100644 index 0000000000000..9f7e5db19d51d --- /dev/null +++ b/src/wp-includes/build/pages/content-types/loader.js @@ -0,0 +1 @@ +// Empty module loader for page dependencies diff --git a/src/wp-includes/build/pages/content-types/page-wp-admin.php b/src/wp-includes/build/pages/content-types/page-wp-admin.php new file mode 100644 index 0000000000000..f59c8b4f34c80 --- /dev/null +++ b/src/wp-includes/build/pages/content-types/page-wp-admin.php @@ -0,0 +1,294 @@ + $path ); + if ( ! empty( $content_module ) ) { + $route['content_module'] = $content_module; + } + if ( ! empty( $route_module ) ) { + $route['route_module'] = $route_module; + } + + $wp_content_types_wp_admin_routes[] = $route; +} + +/** + * Register a menu item for the content-types-wp-admin page. + * Note: Menu items are registered but not displayed in single-page mode. + * + * @param string $id Menu item ID. + * @param string $label Display label. + * @param string $to Route path to navigate to. + * @param string $parent_id Optional. Parent menu item ID. + */ +function wp_register_content_types_wp_admin_menu_item( $id, $label, $to, $parent_id = '' ) { + global $wp_content_types_wp_admin_menu_items; + + $menu_item = array( + 'id' => $id, + 'label' => $label, + 'to' => $to, + ); + + if ( ! empty( $parent_id ) ) { + $menu_item['parent'] = $parent_id; + } + + $wp_content_types_wp_admin_menu_items[] = $menu_item; +} + +/** + * Get all registered routes for the content-types-wp-admin page. + * + * @return array Array of route objects. + */ +function wp_get_content_types_wp_admin_routes() { + global $wp_content_types_wp_admin_routes; + return $wp_content_types_wp_admin_routes ?? array(); +} + +/** + * Get all registered menu items for the content-types-wp-admin page. + * + * @return array Array of menu item objects. + */ +function wp_get_content_types_wp_admin_menu_items() { + global $wp_content_types_wp_admin_menu_items; + return $wp_content_types_wp_admin_menu_items ?? array(); +} + +/** + * Preload REST API data for the content-types-wp-admin page. + * Automatically called during page rendering. + */ +function wp_content_types_wp_admin_preload_data() { + // Define paths to preload - same for all pages + // Please also change packages/core-data/src/entities.js when changing this. + $preload_paths = array( + '/?_fields=description,gmt_offset,home,image_sizes,image_size_threshold,name,site_icon,site_icon_url,site_logo,timezone_string,url,page_for_posts,page_on_front,show_on_front', + array( '/wp/v2/settings', 'OPTIONS' ), + ); + + // Use rest_preload_api_request to gather the preloaded data + $preload_data = array_reduce( + $preload_paths, + 'rest_preload_api_request', + array() + ); + + // Register the preloading middleware with wp-api-fetch + wp_add_inline_script( + 'wp-api-fetch', + sprintf( + 'wp.apiFetch.use( wp.apiFetch.createPreloadingMiddleware( %s ) );', + wp_json_encode( $preload_data ) + ), + 'after' + ); +} + +/** + * Enqueue scripts and styles for the content-types-wp-admin page. + * Hooked to admin_enqueue_scripts. + * + * @param string $hook_suffix The current admin page. + */ +function wp_content_types_wp_admin_enqueue_scripts( $hook_suffix ) { + // Check all possible ways this page can be accessed: + // 1. Menu page via admin.php?page=content-types-wp-admin (plugin) + // 2. Direct file via content-types.php (Core) - screen ID will be 'content-types' + $current_screen = get_current_screen(); + $is_our_page = ( + ( isset( $_GET['page'] ) && 'content-types-wp-admin' === $_GET['page'] ) || // phpcs:ignore WordPress.Security.NonceVerification.Recommended + ( $current_screen && 'content-types' === $current_screen->id ) + ); + + if ( ! $is_our_page ) { + return; + } + + // Load build constants + $build_constants = require __DIR__ . '/../../constants.php'; + + // Fire init action for extensions to register routes and menu items + do_action( 'content-types-wp-admin_init' ); + + // Preload REST API data + wp_content_types_wp_admin_preload_data(); + + // Get all registered routes + $routes = wp_get_content_types_wp_admin_routes(); + + // Get boot module asset file for dependencies + $asset_file = ABSPATH . WPINC . '/js/dist/script-modules/boot/index.min.asset.php'; + if ( file_exists( $asset_file ) ) { + $asset = require $asset_file; + + // This script serves two purposes: + // 1. It ensures all the globals that are made available to the modules are loaded. + // 2. It initializes the boot module as an inline script. + wp_register_script( 'content-types-wp-admin-prerequisites', '', $asset['dependencies'], $asset['version'], true ); + + // Add inline script to initialize the app using initSinglePage (no menuItems) + wp_add_inline_script( + 'content-types-wp-admin-prerequisites', + sprintf( + 'import("@wordpress/boot").then(mod => mod.initSinglePage({mountId: "%s", routes: %s}));', + 'content-types-wp-admin-app', + wp_json_encode( $routes, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ) + ) + ); + + // Register prerequisites style by filtering script dependencies to find registered styles + $style_dependencies = array_filter( + $asset['dependencies'], + function ( $handle ) { + return wp_style_is( $handle, 'registered' ); + } + ); + wp_register_style( 'content-types-wp-admin-prerequisites', false, $style_dependencies, $asset['version'] ); + + // Build dependencies for content-types-wp-admin module + $boot_dependencies = array( + array( + 'import' => 'static', + 'id' => '@wordpress/boot', + ), + ); + + // Add all registered routes as dependencies + foreach ( $routes as $route ) { + if ( isset( $route['route_module'] ) ) { + $boot_dependencies[] = array( + 'import' => 'static', + 'id' => $route['route_module'], + ); + } + if ( isset( $route['content_module'] ) ) { + $boot_dependencies[] = array( + 'import' => 'dynamic', + 'id' => $route['content_module'], + ); + } + } + + /** + * Filters the boot script-module dependencies for the + * content-types-wp-admin page. + * + * Surfaces extending this page can append entries to the boot + * dependency list. Each entry is an array with 'import' (string + * 'static' or 'dynamic') and 'id' (script-module handle) keys. + * + * @param array $boot_dependencies Boot dependencies for the page. + */ + $boot_dependencies = apply_filters( + 'content-types-wp-admin_boot_dependencies', + $boot_dependencies + ); + + // Dummy script module to ensure dependencies are loaded + wp_register_script_module( + 'content-types-wp-admin', + $build_constants['build_url'] . 'pages/content-types/loader.js', + $boot_dependencies + ); + + // Enqueue the boot scripts and styles + wp_enqueue_script( 'content-types-wp-admin-prerequisites' ); + wp_enqueue_script_module( 'content-types-wp-admin' ); + wp_enqueue_style( 'content-types-wp-admin-prerequisites' ); + } +} + +/** + * Render the content-types-wp-admin page. + * Call this function from add_menu_page or add_submenu_page. + * This renders within the normal WordPress admin interface. + */ +function wp_content_types_wp_admin_render_page() { + ?> + +
+ $path ); + if ( ! empty( $content_module ) ) { + $route['content_module'] = $content_module; + } + if ( ! empty( $route_module ) ) { + $route['route_module'] = $route_module; + } + + $wp_content_types_routes[] = $route; +} + +/** + * Register a menu item for the content-types page. + * + * @param string $id Menu item ID. + * @param string $label Display label. + * @param string $to Route path to navigate to. + * @param string $parent_id Optional. Parent menu item ID. + * @param string $parent_type Optional. Parent type: 'drilldown' or 'dropdown'. + */ +function wp_register_content_types_menu_item( $id, $label, $to, $parent_id = '', $parent_type = '' ) { + global $wp_content_types_menu_items; + + $menu_item = array( + 'id' => $id, + 'label' => $label, + 'to' => $to, + ); + + if ( ! empty( $parent_id ) ) { + $menu_item['parent'] = $parent_id; + } + + if ( ! empty( $parent_type ) && in_array( $parent_type, array( 'drilldown', 'dropdown' ), true ) ) { + $menu_item['parent_type'] = $parent_type; + } + + $wp_content_types_menu_items[] = $menu_item; +} + +/** + * Get all registered routes for the content-types page. + * + * @return array Array of route objects. + */ +function wp_get_content_types_routes() { + global $wp_content_types_routes; + return $wp_content_types_routes ?? array(); +} + +/** + * Get all registered menu items for the content-types page. + * + * @return array Array of menu item objects. + */ +function wp_get_content_types_menu_items() { + global $wp_content_types_menu_items; + return $wp_content_types_menu_items ?? array(); +} + +/** + * Preload REST API data for the content-types page. + * Automatically called during page rendering. + */ +function wp_content_types_preload_data() { + // Define paths to preload - same for all pages + // Please also change packages/core-data/src/entities.js when changing this. + $preload_paths = array( + '/?_fields=description,gmt_offset,home,image_sizes,image_size_threshold,name,site_icon,site_icon_url,site_logo,timezone_string,url,page_for_posts,page_on_front,show_on_front', + array( '/wp/v2/settings', 'OPTIONS' ), + ); + + // Use rest_preload_api_request to gather the preloaded data + $preload_data = array_reduce( + $preload_paths, + 'rest_preload_api_request', + array() + ); + + // Register the preloading middleware with wp-api-fetch + wp_add_inline_script( + 'wp-api-fetch', + sprintf( + 'wp.apiFetch.use( wp.apiFetch.createPreloadingMiddleware( %s ) );', + wp_json_encode( $preload_data ) + ), + 'after' + ); +} + +/** + * Render the content-types page. + * Call this function from add_menu_page or add_submenu_page. + */ +function wp_content_types_render_page() { + // Load build constants + $build_constants = require __DIR__ . '/../../constants.php'; + + // Set current screen + set_current_screen(); + + // Remove unwanted deprecated handler + remove_action( 'admin_head', 'wp_admin_bar_header' ); + + // Remove unwanted scripts and styles that were enqueued during `admin_init` + foreach ( wp_scripts()->queue as $script ) { + wp_dequeue_script( $script ); + } + foreach ( wp_styles()->queue as $style ) { + wp_dequeue_style( $style ); + } + + // Fire init action for extensions to register routes and menu items + do_action( 'content-types_init' ); + + // Enqueue command palette assets for boot-based pages + if ( function_exists( 'wp_enqueue_command_palette_assets' ) ) { + wp_enqueue_command_palette_assets(); + } + + // Preload REST API data + wp_content_types_preload_data(); + + // Get all registered routes and menu items + $menu_items = wp_get_content_types_menu_items(); + $routes = wp_get_content_types_routes(); + + // Get boot module asset file for dependencies + $asset_file = ABSPATH . WPINC . '/js/dist/script-modules/boot/index.min.asset.php'; + if ( file_exists( $asset_file ) ) { + $asset = require $asset_file; + + // This script serves two purposes: + // 1. It ensures all the globals that are made available to the modules are loaded. + // 2. It initializes the boot module as an inline script. + wp_register_script( 'content-types-prerequisites', '', $asset['dependencies'], $asset['version'], true ); + + // Add inline script to initialize the app + $init_modules = []; + wp_add_inline_script( + 'content-types-prerequisites', + sprintf( + 'import("@wordpress/boot").then(mod => mod.init({mountId: "%s", menuItems: %s, routes: %s, initModules: %s, dashboardLink: "%s"}));', + 'content-types-app', + wp_json_encode( $menu_items, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ), + wp_json_encode( $routes, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ), + wp_json_encode( $init_modules, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ), + esc_url( admin_url( '/' ) ) + ) + ); + + // Register prerequisites style by filtering script dependencies to find registered styles + $style_dependencies = array_filter( + $asset['dependencies'], + function ( $handle ) { + return wp_style_is( $handle, 'registered' ); + } + ); + wp_register_style( 'content-types-prerequisites', false, $style_dependencies, $asset['version'] ); + + // Build dependencies for content-types module + $boot_dependencies = array( + array( + 'import' => 'static', + 'id' => '@wordpress/boot', + ), + ); + + // Add init modules as static dependencies + // No init modules configured + + // Add all registered routes as dependencies + foreach ( $routes as $route ) { + if ( isset( $route['route_module'] ) ) { + $boot_dependencies[] = array( + 'import' => 'static', + 'id' => $route['route_module'], + ); + } + if ( isset( $route['content_module'] ) ) { + $boot_dependencies[] = array( + 'import' => 'dynamic', + 'id' => $route['content_module'], + ); + } + } + + /** + * Filters the boot script-module dependencies for the + * content-types page. + * + * Surfaces extending this page can append entries to the boot + * dependency list. Each entry is an array with 'import' (string + * 'static' or 'dynamic') and 'id' (script-module handle) keys. + * + * @param array $boot_dependencies Boot dependencies for the page. + */ + $boot_dependencies = apply_filters( + 'content-types_boot_dependencies', + $boot_dependencies + ); + + // Dummy script module to ensure dependencies are loaded + wp_register_script_module( + 'content-types', + $build_constants['build_url'] . 'pages/content-types/loader.js', + $boot_dependencies + ); + + // Enqueue the boot scripts and styles + wp_enqueue_script( 'content-types-prerequisites' ); + wp_enqueue_script_module( 'content-types' ); + wp_enqueue_style( 'content-types-prerequisites' ); + } + + // Output the HTML + ?> + + > + + + + <?php echo esc_html( get_admin_page_title() ); ?> + + + + +
+ print_import_map(); + print_footer_scripts(); + wp_script_modules()->print_enqueued_script_modules(); + wp_script_modules()->print_script_module_preloads(); + wp_script_modules()->print_script_module_data(); + + /** + * Prints scripts or data after the default footer scripts. + * + * @since 2.8.0 + */ + do_action( "admin_footer-{$hook_suffix}" ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores + // END see wp-admin/admin-footer.php + ?> + + + $path ); + if ( ! empty( $content_module ) ) { + $route['content_module'] = $content_module; + } + if ( ! empty( $route_module ) ) { + $route['route_module'] = $route_module; + } + + $wp_dashboard_wp_admin_routes[] = $route; +} + +/** + * Register a menu item for the dashboard-wp-admin page. + * Note: Menu items are registered but not displayed in single-page mode. + * + * @param string $id Menu item ID. + * @param string $label Display label. + * @param string $to Route path to navigate to. + * @param string $parent_id Optional. Parent menu item ID. + */ +function wp_register_dashboard_wp_admin_menu_item( $id, $label, $to, $parent_id = '' ) { + global $wp_dashboard_wp_admin_menu_items; + + $menu_item = array( + 'id' => $id, + 'label' => $label, + 'to' => $to, + ); + + if ( ! empty( $parent_id ) ) { + $menu_item['parent'] = $parent_id; + } + + $wp_dashboard_wp_admin_menu_items[] = $menu_item; +} + +/** + * Get all registered routes for the dashboard-wp-admin page. + * + * @return array Array of route objects. + */ +function wp_get_dashboard_wp_admin_routes() { + global $wp_dashboard_wp_admin_routes; + return $wp_dashboard_wp_admin_routes ?? array(); +} + +/** + * Get all registered menu items for the dashboard-wp-admin page. + * + * @return array Array of menu item objects. + */ +function wp_get_dashboard_wp_admin_menu_items() { + global $wp_dashboard_wp_admin_menu_items; + return $wp_dashboard_wp_admin_menu_items ?? array(); +} + +/** + * Preload REST API data for the dashboard-wp-admin page. + * Automatically called during page rendering. + */ +function wp_dashboard_wp_admin_preload_data() { + // Define paths to preload - same for all pages + // Please also change packages/core-data/src/entities.js when changing this. + $preload_paths = array( + '/?_fields=description,gmt_offset,home,image_sizes,image_size_threshold,name,site_icon,site_icon_url,site_logo,timezone_string,url,page_for_posts,page_on_front,show_on_front', + array( '/wp/v2/settings', 'OPTIONS' ), + ); + + // Use rest_preload_api_request to gather the preloaded data + $preload_data = array_reduce( + $preload_paths, + 'rest_preload_api_request', + array() + ); + + // Register the preloading middleware with wp-api-fetch + wp_add_inline_script( + 'wp-api-fetch', + sprintf( + 'wp.apiFetch.use( wp.apiFetch.createPreloadingMiddleware( %s ) );', + wp_json_encode( $preload_data ) + ), + 'after' + ); +} + +/** + * Enqueue scripts and styles for the dashboard-wp-admin page. + * Hooked to admin_enqueue_scripts. + * + * @param string $hook_suffix The current admin page. + */ +function wp_dashboard_wp_admin_enqueue_scripts( $hook_suffix ) { + // Check all possible ways this page can be accessed: + // 1. Menu page via admin.php?page=dashboard-wp-admin (plugin) + // 2. Direct file via dashboard.php (Core) - screen ID will be 'dashboard' + $current_screen = get_current_screen(); + $is_our_page = ( + ( isset( $_GET['page'] ) && 'dashboard-wp-admin' === $_GET['page'] ) || // phpcs:ignore WordPress.Security.NonceVerification.Recommended + ( $current_screen && 'dashboard' === $current_screen->id ) + ); + + if ( ! $is_our_page ) { + return; + } + + // Load build constants + $build_constants = require __DIR__ . '/../../constants.php'; + + // Fire init action for extensions to register routes and menu items + do_action( 'dashboard-wp-admin_init' ); + + // Preload REST API data + wp_dashboard_wp_admin_preload_data(); + + // Get all registered routes + $routes = wp_get_dashboard_wp_admin_routes(); + + // Get boot module asset file for dependencies + $asset_file = ABSPATH . WPINC . '/js/dist/script-modules/boot/index.min.asset.php'; + if ( file_exists( $asset_file ) ) { + $asset = require $asset_file; + + // This script serves two purposes: + // 1. It ensures all the globals that are made available to the modules are loaded. + // 2. It initializes the boot module as an inline script. + wp_register_script( 'dashboard-wp-admin-prerequisites', '', $asset['dependencies'], $asset['version'], true ); + + // Add inline script to initialize the app using initSinglePage (no menuItems) + wp_add_inline_script( + 'dashboard-wp-admin-prerequisites', + sprintf( + 'import("@wordpress/boot").then(mod => mod.initSinglePage({mountId: "%s", routes: %s}));', + 'dashboard-wp-admin-app', + wp_json_encode( $routes, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ) + ) + ); + + // Register prerequisites style by filtering script dependencies to find registered styles + $style_dependencies = array_filter( + $asset['dependencies'], + function ( $handle ) { + return wp_style_is( $handle, 'registered' ); + } + ); + wp_register_style( 'dashboard-wp-admin-prerequisites', false, $style_dependencies, $asset['version'] ); + + // Build dependencies for dashboard-wp-admin module + $boot_dependencies = array( + array( + 'import' => 'static', + 'id' => '@wordpress/boot', + ), + ); + + // Add all registered routes as dependencies + foreach ( $routes as $route ) { + if ( isset( $route['route_module'] ) ) { + $boot_dependencies[] = array( + 'import' => 'static', + 'id' => $route['route_module'], + ); + } + if ( isset( $route['content_module'] ) ) { + $boot_dependencies[] = array( + 'import' => 'dynamic', + 'id' => $route['content_module'], + ); + } + } + + /** + * Filters the boot script-module dependencies for the + * dashboard-wp-admin page. + * + * Surfaces extending this page can append entries to the boot + * dependency list. Each entry is an array with 'import' (string + * 'static' or 'dynamic') and 'id' (script-module handle) keys. + * + * @param array $boot_dependencies Boot dependencies for the page. + */ + $boot_dependencies = apply_filters( + 'dashboard-wp-admin_boot_dependencies', + $boot_dependencies + ); + + // Dummy script module to ensure dependencies are loaded + wp_register_script_module( + 'dashboard-wp-admin', + $build_constants['build_url'] . 'pages/dashboard/loader.js', + $boot_dependencies + ); + + // Enqueue the boot scripts and styles + wp_enqueue_script( 'dashboard-wp-admin-prerequisites' ); + wp_enqueue_script_module( 'dashboard-wp-admin' ); + wp_enqueue_style( 'dashboard-wp-admin-prerequisites' ); + } +} + +/** + * Render the dashboard-wp-admin page. + * Call this function from add_menu_page or add_submenu_page. + * This renders within the normal WordPress admin interface. + */ +function wp_dashboard_wp_admin_render_page() { + ?> + +
+ $path ); + if ( ! empty( $content_module ) ) { + $route['content_module'] = $content_module; + } + if ( ! empty( $route_module ) ) { + $route['route_module'] = $route_module; + } + + $wp_dashboard_routes[] = $route; +} + +/** + * Register a menu item for the dashboard page. + * + * @param string $id Menu item ID. + * @param string $label Display label. + * @param string $to Route path to navigate to. + * @param string $parent_id Optional. Parent menu item ID. + * @param string $parent_type Optional. Parent type: 'drilldown' or 'dropdown'. + */ +function wp_register_dashboard_menu_item( $id, $label, $to, $parent_id = '', $parent_type = '' ) { + global $wp_dashboard_menu_items; + + $menu_item = array( + 'id' => $id, + 'label' => $label, + 'to' => $to, + ); + + if ( ! empty( $parent_id ) ) { + $menu_item['parent'] = $parent_id; + } + + if ( ! empty( $parent_type ) && in_array( $parent_type, array( 'drilldown', 'dropdown' ), true ) ) { + $menu_item['parent_type'] = $parent_type; + } + + $wp_dashboard_menu_items[] = $menu_item; +} + +/** + * Get all registered routes for the dashboard page. + * + * @return array Array of route objects. + */ +function wp_get_dashboard_routes() { + global $wp_dashboard_routes; + return $wp_dashboard_routes ?? array(); +} + +/** + * Get all registered menu items for the dashboard page. + * + * @return array Array of menu item objects. + */ +function wp_get_dashboard_menu_items() { + global $wp_dashboard_menu_items; + return $wp_dashboard_menu_items ?? array(); +} + +/** + * Preload REST API data for the dashboard page. + * Automatically called during page rendering. + */ +function wp_dashboard_preload_data() { + // Define paths to preload - same for all pages + // Please also change packages/core-data/src/entities.js when changing this. + $preload_paths = array( + '/?_fields=description,gmt_offset,home,image_sizes,image_size_threshold,name,site_icon,site_icon_url,site_logo,timezone_string,url,page_for_posts,page_on_front,show_on_front', + array( '/wp/v2/settings', 'OPTIONS' ), + ); + + // Use rest_preload_api_request to gather the preloaded data + $preload_data = array_reduce( + $preload_paths, + 'rest_preload_api_request', + array() + ); + + // Register the preloading middleware with wp-api-fetch + wp_add_inline_script( + 'wp-api-fetch', + sprintf( + 'wp.apiFetch.use( wp.apiFetch.createPreloadingMiddleware( %s ) );', + wp_json_encode( $preload_data ) + ), + 'after' + ); +} + +/** + * Render the dashboard page. + * Call this function from add_menu_page or add_submenu_page. + */ +function wp_dashboard_render_page() { + // Load build constants + $build_constants = require __DIR__ . '/../../constants.php'; + + // Set current screen + set_current_screen(); + + // Remove unwanted deprecated handler + remove_action( 'admin_head', 'wp_admin_bar_header' ); + + // Remove unwanted scripts and styles that were enqueued during `admin_init` + foreach ( wp_scripts()->queue as $script ) { + wp_dequeue_script( $script ); + } + foreach ( wp_styles()->queue as $style ) { + wp_dequeue_style( $style ); + } + + // Fire init action for extensions to register routes and menu items + do_action( 'dashboard_init' ); + + // Enqueue command palette assets for boot-based pages + if ( function_exists( 'wp_enqueue_command_palette_assets' ) ) { + wp_enqueue_command_palette_assets(); + } + + // Preload REST API data + wp_dashboard_preload_data(); + + // Get all registered routes and menu items + $menu_items = wp_get_dashboard_menu_items(); + $routes = wp_get_dashboard_routes(); + + // Get boot module asset file for dependencies + $asset_file = ABSPATH . WPINC . '/js/dist/script-modules/boot/index.min.asset.php'; + if ( file_exists( $asset_file ) ) { + $asset = require $asset_file; + + // This script serves two purposes: + // 1. It ensures all the globals that are made available to the modules are loaded. + // 2. It initializes the boot module as an inline script. + wp_register_script( 'dashboard-prerequisites', '', $asset['dependencies'], $asset['version'], true ); + + // Add inline script to initialize the app + $init_modules = []; + wp_add_inline_script( + 'dashboard-prerequisites', + sprintf( + 'import("@wordpress/boot").then(mod => mod.init({mountId: "%s", menuItems: %s, routes: %s, initModules: %s, dashboardLink: "%s"}));', + 'dashboard-app', + wp_json_encode( $menu_items, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ), + wp_json_encode( $routes, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ), + wp_json_encode( $init_modules, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ), + esc_url( admin_url( '/' ) ) + ) + ); + + // Register prerequisites style by filtering script dependencies to find registered styles + $style_dependencies = array_filter( + $asset['dependencies'], + function ( $handle ) { + return wp_style_is( $handle, 'registered' ); + } + ); + wp_register_style( 'dashboard-prerequisites', false, $style_dependencies, $asset['version'] ); + + // Build dependencies for dashboard module + $boot_dependencies = array( + array( + 'import' => 'static', + 'id' => '@wordpress/boot', + ), + ); + + // Add init modules as static dependencies + // No init modules configured + + // Add all registered routes as dependencies + foreach ( $routes as $route ) { + if ( isset( $route['route_module'] ) ) { + $boot_dependencies[] = array( + 'import' => 'static', + 'id' => $route['route_module'], + ); + } + if ( isset( $route['content_module'] ) ) { + $boot_dependencies[] = array( + 'import' => 'dynamic', + 'id' => $route['content_module'], + ); + } + } + + /** + * Filters the boot script-module dependencies for the + * dashboard page. + * + * Surfaces extending this page can append entries to the boot + * dependency list. Each entry is an array with 'import' (string + * 'static' or 'dynamic') and 'id' (script-module handle) keys. + * + * @param array $boot_dependencies Boot dependencies for the page. + */ + $boot_dependencies = apply_filters( + 'dashboard_boot_dependencies', + $boot_dependencies + ); + + // Dummy script module to ensure dependencies are loaded + wp_register_script_module( + 'dashboard', + $build_constants['build_url'] . 'pages/dashboard/loader.js', + $boot_dependencies + ); + + // Enqueue the boot scripts and styles + wp_enqueue_script( 'dashboard-prerequisites' ); + wp_enqueue_script_module( 'dashboard' ); + wp_enqueue_style( 'dashboard-prerequisites' ); + } + + // Output the HTML + ?> + + > + + + + <?php echo esc_html( get_admin_page_title() ); ?> + + + + +
+ print_import_map(); + print_footer_scripts(); + wp_script_modules()->print_enqueued_script_modules(); + wp_script_modules()->print_script_module_preloads(); + wp_script_modules()->print_script_module_data(); + + /** + * Prints scripts or data after the default footer scripts. + * + * @since 2.8.0 + */ + do_action( "admin_footer-{$hook_suffix}" ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores + // END see wp-admin/admin-footer.php + ?> + + + $path ); + if ( ! empty( $content_module ) ) { + $route['content_module'] = $content_module; + } + if ( ! empty( $route_module ) ) { + $route['route_module'] = $route_module; + } + + $wp_experiments_wp_admin_routes[] = $route; +} + +/** + * Register a menu item for the experiments-wp-admin page. + * Note: Menu items are registered but not displayed in single-page mode. + * + * @param string $id Menu item ID. + * @param string $label Display label. + * @param string $to Route path to navigate to. + * @param string $parent_id Optional. Parent menu item ID. + */ +function wp_register_experiments_wp_admin_menu_item( $id, $label, $to, $parent_id = '' ) { + global $wp_experiments_wp_admin_menu_items; + + $menu_item = array( + 'id' => $id, + 'label' => $label, + 'to' => $to, + ); + + if ( ! empty( $parent_id ) ) { + $menu_item['parent'] = $parent_id; + } + + $wp_experiments_wp_admin_menu_items[] = $menu_item; +} + +/** + * Get all registered routes for the experiments-wp-admin page. + * + * @return array Array of route objects. + */ +function wp_get_experiments_wp_admin_routes() { + global $wp_experiments_wp_admin_routes; + return $wp_experiments_wp_admin_routes ?? array(); +} + +/** + * Get all registered menu items for the experiments-wp-admin page. + * + * @return array Array of menu item objects. + */ +function wp_get_experiments_wp_admin_menu_items() { + global $wp_experiments_wp_admin_menu_items; + return $wp_experiments_wp_admin_menu_items ?? array(); +} + +/** + * Preload REST API data for the experiments-wp-admin page. + * Automatically called during page rendering. + */ +function wp_experiments_wp_admin_preload_data() { + // Define paths to preload - same for all pages + // Please also change packages/core-data/src/entities.js when changing this. + $preload_paths = array( + '/?_fields=description,gmt_offset,home,image_sizes,image_size_threshold,name,site_icon,site_icon_url,site_logo,timezone_string,url,page_for_posts,page_on_front,show_on_front', + array( '/wp/v2/settings', 'OPTIONS' ), + ); + + // Use rest_preload_api_request to gather the preloaded data + $preload_data = array_reduce( + $preload_paths, + 'rest_preload_api_request', + array() + ); + + // Register the preloading middleware with wp-api-fetch + wp_add_inline_script( + 'wp-api-fetch', + sprintf( + 'wp.apiFetch.use( wp.apiFetch.createPreloadingMiddleware( %s ) );', + wp_json_encode( $preload_data ) + ), + 'after' + ); +} + +/** + * Enqueue scripts and styles for the experiments-wp-admin page. + * Hooked to admin_enqueue_scripts. + * + * @param string $hook_suffix The current admin page. + */ +function wp_experiments_wp_admin_enqueue_scripts( $hook_suffix ) { + // Check all possible ways this page can be accessed: + // 1. Menu page via admin.php?page=experiments-wp-admin (plugin) + // 2. Direct file via experiments.php (Core) - screen ID will be 'experiments' + $current_screen = get_current_screen(); + $is_our_page = ( + ( isset( $_GET['page'] ) && 'experiments-wp-admin' === $_GET['page'] ) || // phpcs:ignore WordPress.Security.NonceVerification.Recommended + ( $current_screen && 'experiments' === $current_screen->id ) + ); + + if ( ! $is_our_page ) { + return; + } + + // Load build constants + $build_constants = require __DIR__ . '/../../constants.php'; + + // Fire init action for extensions to register routes and menu items + do_action( 'experiments-wp-admin_init' ); + + // Preload REST API data + wp_experiments_wp_admin_preload_data(); + + // Get all registered routes + $routes = wp_get_experiments_wp_admin_routes(); + + // Get boot module asset file for dependencies + $asset_file = ABSPATH . WPINC . '/js/dist/script-modules/boot/index.min.asset.php'; + if ( file_exists( $asset_file ) ) { + $asset = require $asset_file; + + // This script serves two purposes: + // 1. It ensures all the globals that are made available to the modules are loaded. + // 2. It initializes the boot module as an inline script. + wp_register_script( 'experiments-wp-admin-prerequisites', '', $asset['dependencies'], $asset['version'], true ); + + // Add inline script to initialize the app using initSinglePage (no menuItems) + wp_add_inline_script( + 'experiments-wp-admin-prerequisites', + sprintf( + 'import("@wordpress/boot").then(mod => mod.initSinglePage({mountId: "%s", routes: %s}));', + 'experiments-wp-admin-app', + wp_json_encode( $routes, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ) + ) + ); + + // Register prerequisites style by filtering script dependencies to find registered styles + $style_dependencies = array_filter( + $asset['dependencies'], + function ( $handle ) { + return wp_style_is( $handle, 'registered' ); + } + ); + wp_register_style( 'experiments-wp-admin-prerequisites', false, $style_dependencies, $asset['version'] ); + + // Build dependencies for experiments-wp-admin module + $boot_dependencies = array( + array( + 'import' => 'static', + 'id' => '@wordpress/boot', + ), + ); + + // Add all registered routes as dependencies + foreach ( $routes as $route ) { + if ( isset( $route['route_module'] ) ) { + $boot_dependencies[] = array( + 'import' => 'static', + 'id' => $route['route_module'], + ); + } + if ( isset( $route['content_module'] ) ) { + $boot_dependencies[] = array( + 'import' => 'dynamic', + 'id' => $route['content_module'], + ); + } + } + + /** + * Filters the boot script-module dependencies for the + * experiments-wp-admin page. + * + * Surfaces extending this page can append entries to the boot + * dependency list. Each entry is an array with 'import' (string + * 'static' or 'dynamic') and 'id' (script-module handle) keys. + * + * @param array $boot_dependencies Boot dependencies for the page. + */ + $boot_dependencies = apply_filters( + 'experiments-wp-admin_boot_dependencies', + $boot_dependencies + ); + + // Dummy script module to ensure dependencies are loaded + wp_register_script_module( + 'experiments-wp-admin', + $build_constants['build_url'] . 'pages/experiments/loader.js', + $boot_dependencies + ); + + // Enqueue the boot scripts and styles + wp_enqueue_script( 'experiments-wp-admin-prerequisites' ); + wp_enqueue_script_module( 'experiments-wp-admin' ); + wp_enqueue_style( 'experiments-wp-admin-prerequisites' ); + } +} + +/** + * Render the experiments-wp-admin page. + * Call this function from add_menu_page or add_submenu_page. + * This renders within the normal WordPress admin interface. + */ +function wp_experiments_wp_admin_render_page() { + ?> + +
+ $path ); + if ( ! empty( $content_module ) ) { + $route['content_module'] = $content_module; + } + if ( ! empty( $route_module ) ) { + $route['route_module'] = $route_module; + } + + $wp_experiments_routes[] = $route; +} + +/** + * Register a menu item for the experiments page. + * + * @param string $id Menu item ID. + * @param string $label Display label. + * @param string $to Route path to navigate to. + * @param string $parent_id Optional. Parent menu item ID. + * @param string $parent_type Optional. Parent type: 'drilldown' or 'dropdown'. + */ +function wp_register_experiments_menu_item( $id, $label, $to, $parent_id = '', $parent_type = '' ) { + global $wp_experiments_menu_items; + + $menu_item = array( + 'id' => $id, + 'label' => $label, + 'to' => $to, + ); + + if ( ! empty( $parent_id ) ) { + $menu_item['parent'] = $parent_id; + } + + if ( ! empty( $parent_type ) && in_array( $parent_type, array( 'drilldown', 'dropdown' ), true ) ) { + $menu_item['parent_type'] = $parent_type; + } + + $wp_experiments_menu_items[] = $menu_item; +} + +/** + * Get all registered routes for the experiments page. + * + * @return array Array of route objects. + */ +function wp_get_experiments_routes() { + global $wp_experiments_routes; + return $wp_experiments_routes ?? array(); +} + +/** + * Get all registered menu items for the experiments page. + * + * @return array Array of menu item objects. + */ +function wp_get_experiments_menu_items() { + global $wp_experiments_menu_items; + return $wp_experiments_menu_items ?? array(); +} + +/** + * Preload REST API data for the experiments page. + * Automatically called during page rendering. + */ +function wp_experiments_preload_data() { + // Define paths to preload - same for all pages + // Please also change packages/core-data/src/entities.js when changing this. + $preload_paths = array( + '/?_fields=description,gmt_offset,home,image_sizes,image_size_threshold,name,site_icon,site_icon_url,site_logo,timezone_string,url,page_for_posts,page_on_front,show_on_front', + array( '/wp/v2/settings', 'OPTIONS' ), + ); + + // Use rest_preload_api_request to gather the preloaded data + $preload_data = array_reduce( + $preload_paths, + 'rest_preload_api_request', + array() + ); + + // Register the preloading middleware with wp-api-fetch + wp_add_inline_script( + 'wp-api-fetch', + sprintf( + 'wp.apiFetch.use( wp.apiFetch.createPreloadingMiddleware( %s ) );', + wp_json_encode( $preload_data ) + ), + 'after' + ); +} + +/** + * Render the experiments page. + * Call this function from add_menu_page or add_submenu_page. + */ +function wp_experiments_render_page() { + // Load build constants + $build_constants = require __DIR__ . '/../../constants.php'; + + // Set current screen + set_current_screen(); + + // Remove unwanted deprecated handler + remove_action( 'admin_head', 'wp_admin_bar_header' ); + + // Remove unwanted scripts and styles that were enqueued during `admin_init` + foreach ( wp_scripts()->queue as $script ) { + wp_dequeue_script( $script ); + } + foreach ( wp_styles()->queue as $style ) { + wp_dequeue_style( $style ); + } + + // Fire init action for extensions to register routes and menu items + do_action( 'experiments_init' ); + + // Enqueue command palette assets for boot-based pages + if ( function_exists( 'wp_enqueue_command_palette_assets' ) ) { + wp_enqueue_command_palette_assets(); + } + + // Preload REST API data + wp_experiments_preload_data(); + + // Get all registered routes and menu items + $menu_items = wp_get_experiments_menu_items(); + $routes = wp_get_experiments_routes(); + + // Get boot module asset file for dependencies + $asset_file = ABSPATH . WPINC . '/js/dist/script-modules/boot/index.min.asset.php'; + if ( file_exists( $asset_file ) ) { + $asset = require $asset_file; + + // This script serves two purposes: + // 1. It ensures all the globals that are made available to the modules are loaded. + // 2. It initializes the boot module as an inline script. + wp_register_script( 'experiments-prerequisites', '', $asset['dependencies'], $asset['version'], true ); + + // Add inline script to initialize the app + $init_modules = []; + wp_add_inline_script( + 'experiments-prerequisites', + sprintf( + 'import("@wordpress/boot").then(mod => mod.init({mountId: "%s", menuItems: %s, routes: %s, initModules: %s, dashboardLink: "%s"}));', + 'experiments-app', + wp_json_encode( $menu_items, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ), + wp_json_encode( $routes, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ), + wp_json_encode( $init_modules, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ), + esc_url( admin_url( '/' ) ) + ) + ); + + // Register prerequisites style by filtering script dependencies to find registered styles + $style_dependencies = array_filter( + $asset['dependencies'], + function ( $handle ) { + return wp_style_is( $handle, 'registered' ); + } + ); + wp_register_style( 'experiments-prerequisites', false, $style_dependencies, $asset['version'] ); + + // Build dependencies for experiments module + $boot_dependencies = array( + array( + 'import' => 'static', + 'id' => '@wordpress/boot', + ), + ); + + // Add init modules as static dependencies + // No init modules configured + + // Add all registered routes as dependencies + foreach ( $routes as $route ) { + if ( isset( $route['route_module'] ) ) { + $boot_dependencies[] = array( + 'import' => 'static', + 'id' => $route['route_module'], + ); + } + if ( isset( $route['content_module'] ) ) { + $boot_dependencies[] = array( + 'import' => 'dynamic', + 'id' => $route['content_module'], + ); + } + } + + /** + * Filters the boot script-module dependencies for the + * experiments page. + * + * Surfaces extending this page can append entries to the boot + * dependency list. Each entry is an array with 'import' (string + * 'static' or 'dynamic') and 'id' (script-module handle) keys. + * + * @param array $boot_dependencies Boot dependencies for the page. + */ + $boot_dependencies = apply_filters( + 'experiments_boot_dependencies', + $boot_dependencies + ); + + // Dummy script module to ensure dependencies are loaded + wp_register_script_module( + 'experiments', + $build_constants['build_url'] . 'pages/experiments/loader.js', + $boot_dependencies + ); + + // Enqueue the boot scripts and styles + wp_enqueue_script( 'experiments-prerequisites' ); + wp_enqueue_script_module( 'experiments' ); + wp_enqueue_style( 'experiments-prerequisites' ); + } + + // Output the HTML + ?> + + > + + + + <?php echo esc_html( get_admin_page_title() ); ?> + + + + +
+ print_import_map(); + print_footer_scripts(); + wp_script_modules()->print_enqueued_script_modules(); + wp_script_modules()->print_script_module_preloads(); + wp_script_modules()->print_script_module_data(); + + /** + * Prints scripts or data after the default footer scripts. + * + * @since 2.8.0 + */ + do_action( "admin_footer-{$hook_suffix}" ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores + // END see wp-admin/admin-footer.php + ?> + + + $path ); + if ( ! empty( $content_module ) ) { + $route['content_module'] = $content_module; + } + if ( ! empty( $route_module ) ) { + $route['route_module'] = $route_module; + } + + $wp_guidelines_wp_admin_routes[] = $route; +} + +/** + * Register a menu item for the guidelines-wp-admin page. + * Note: Menu items are registered but not displayed in single-page mode. + * + * @param string $id Menu item ID. + * @param string $label Display label. + * @param string $to Route path to navigate to. + * @param string $parent_id Optional. Parent menu item ID. + */ +function wp_register_guidelines_wp_admin_menu_item( $id, $label, $to, $parent_id = '' ) { + global $wp_guidelines_wp_admin_menu_items; + + $menu_item = array( + 'id' => $id, + 'label' => $label, + 'to' => $to, + ); + + if ( ! empty( $parent_id ) ) { + $menu_item['parent'] = $parent_id; + } + + $wp_guidelines_wp_admin_menu_items[] = $menu_item; +} + +/** + * Get all registered routes for the guidelines-wp-admin page. + * + * @return array Array of route objects. + */ +function wp_get_guidelines_wp_admin_routes() { + global $wp_guidelines_wp_admin_routes; + return $wp_guidelines_wp_admin_routes ?? array(); +} + +/** + * Get all registered menu items for the guidelines-wp-admin page. + * + * @return array Array of menu item objects. + */ +function wp_get_guidelines_wp_admin_menu_items() { + global $wp_guidelines_wp_admin_menu_items; + return $wp_guidelines_wp_admin_menu_items ?? array(); +} + +/** + * Preload REST API data for the guidelines-wp-admin page. + * Automatically called during page rendering. + */ +function wp_guidelines_wp_admin_preload_data() { + // Define paths to preload - same for all pages + // Please also change packages/core-data/src/entities.js when changing this. + $preload_paths = array( + '/?_fields=description,gmt_offset,home,image_sizes,image_size_threshold,name,site_icon,site_icon_url,site_logo,timezone_string,url,page_for_posts,page_on_front,show_on_front', + array( '/wp/v2/settings', 'OPTIONS' ), + ); + + // Use rest_preload_api_request to gather the preloaded data + $preload_data = array_reduce( + $preload_paths, + 'rest_preload_api_request', + array() + ); + + // Register the preloading middleware with wp-api-fetch + wp_add_inline_script( + 'wp-api-fetch', + sprintf( + 'wp.apiFetch.use( wp.apiFetch.createPreloadingMiddleware( %s ) );', + wp_json_encode( $preload_data ) + ), + 'after' + ); +} + +/** + * Enqueue scripts and styles for the guidelines-wp-admin page. + * Hooked to admin_enqueue_scripts. + * + * @param string $hook_suffix The current admin page. + */ +function wp_guidelines_wp_admin_enqueue_scripts( $hook_suffix ) { + // Check all possible ways this page can be accessed: + // 1. Menu page via admin.php?page=guidelines-wp-admin (plugin) + // 2. Direct file via guidelines.php (Core) - screen ID will be 'guidelines' + $current_screen = get_current_screen(); + $is_our_page = ( + ( isset( $_GET['page'] ) && 'guidelines-wp-admin' === $_GET['page'] ) || // phpcs:ignore WordPress.Security.NonceVerification.Recommended + ( $current_screen && 'guidelines' === $current_screen->id ) + ); + + if ( ! $is_our_page ) { + return; + } + + // Load build constants + $build_constants = require __DIR__ . '/../../constants.php'; + + // Fire init action for extensions to register routes and menu items + do_action( 'guidelines-wp-admin_init' ); + + // Preload REST API data + wp_guidelines_wp_admin_preload_data(); + + // Get all registered routes + $routes = wp_get_guidelines_wp_admin_routes(); + + // Get boot module asset file for dependencies + $asset_file = ABSPATH . WPINC . '/js/dist/script-modules/boot/index.min.asset.php'; + if ( file_exists( $asset_file ) ) { + $asset = require $asset_file; + + // This script serves two purposes: + // 1. It ensures all the globals that are made available to the modules are loaded. + // 2. It initializes the boot module as an inline script. + wp_register_script( 'guidelines-wp-admin-prerequisites', '', $asset['dependencies'], $asset['version'], true ); + + // Add inline script to initialize the app using initSinglePage (no menuItems) + wp_add_inline_script( + 'guidelines-wp-admin-prerequisites', + sprintf( + 'import("@wordpress/boot").then(mod => mod.initSinglePage({mountId: "%s", routes: %s}));', + 'guidelines-wp-admin-app', + wp_json_encode( $routes, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ) + ) + ); + + // Register prerequisites style by filtering script dependencies to find registered styles + $style_dependencies = array_filter( + $asset['dependencies'], + function ( $handle ) { + return wp_style_is( $handle, 'registered' ); + } + ); + wp_register_style( 'guidelines-wp-admin-prerequisites', false, $style_dependencies, $asset['version'] ); + + // Build dependencies for guidelines-wp-admin module + $boot_dependencies = array( + array( + 'import' => 'static', + 'id' => '@wordpress/boot', + ), + ); + + // Add all registered routes as dependencies + foreach ( $routes as $route ) { + if ( isset( $route['route_module'] ) ) { + $boot_dependencies[] = array( + 'import' => 'static', + 'id' => $route['route_module'], + ); + } + if ( isset( $route['content_module'] ) ) { + $boot_dependencies[] = array( + 'import' => 'dynamic', + 'id' => $route['content_module'], + ); + } + } + + /** + * Filters the boot script-module dependencies for the + * guidelines-wp-admin page. + * + * Surfaces extending this page can append entries to the boot + * dependency list. Each entry is an array with 'import' (string + * 'static' or 'dynamic') and 'id' (script-module handle) keys. + * + * @param array $boot_dependencies Boot dependencies for the page. + */ + $boot_dependencies = apply_filters( + 'guidelines-wp-admin_boot_dependencies', + $boot_dependencies + ); + + // Dummy script module to ensure dependencies are loaded + wp_register_script_module( + 'guidelines-wp-admin', + $build_constants['build_url'] . 'pages/guidelines/loader.js', + $boot_dependencies + ); + + // Enqueue the boot scripts and styles + wp_enqueue_script( 'guidelines-wp-admin-prerequisites' ); + wp_enqueue_script_module( 'guidelines-wp-admin' ); + wp_enqueue_style( 'guidelines-wp-admin-prerequisites' ); + } +} + +/** + * Render the guidelines-wp-admin page. + * Call this function from add_menu_page or add_submenu_page. + * This renders within the normal WordPress admin interface. + */ +function wp_guidelines_wp_admin_render_page() { + ?> + +
+ $path ); + if ( ! empty( $content_module ) ) { + $route['content_module'] = $content_module; + } + if ( ! empty( $route_module ) ) { + $route['route_module'] = $route_module; + } + + $wp_guidelines_routes[] = $route; +} + +/** + * Register a menu item for the guidelines page. + * + * @param string $id Menu item ID. + * @param string $label Display label. + * @param string $to Route path to navigate to. + * @param string $parent_id Optional. Parent menu item ID. + * @param string $parent_type Optional. Parent type: 'drilldown' or 'dropdown'. + */ +function wp_register_guidelines_menu_item( $id, $label, $to, $parent_id = '', $parent_type = '' ) { + global $wp_guidelines_menu_items; + + $menu_item = array( + 'id' => $id, + 'label' => $label, + 'to' => $to, + ); + + if ( ! empty( $parent_id ) ) { + $menu_item['parent'] = $parent_id; + } + + if ( ! empty( $parent_type ) && in_array( $parent_type, array( 'drilldown', 'dropdown' ), true ) ) { + $menu_item['parent_type'] = $parent_type; + } + + $wp_guidelines_menu_items[] = $menu_item; +} + +/** + * Get all registered routes for the guidelines page. + * + * @return array Array of route objects. + */ +function wp_get_guidelines_routes() { + global $wp_guidelines_routes; + return $wp_guidelines_routes ?? array(); +} + +/** + * Get all registered menu items for the guidelines page. + * + * @return array Array of menu item objects. + */ +function wp_get_guidelines_menu_items() { + global $wp_guidelines_menu_items; + return $wp_guidelines_menu_items ?? array(); +} + +/** + * Preload REST API data for the guidelines page. + * Automatically called during page rendering. + */ +function wp_guidelines_preload_data() { + // Define paths to preload - same for all pages + // Please also change packages/core-data/src/entities.js when changing this. + $preload_paths = array( + '/?_fields=description,gmt_offset,home,image_sizes,image_size_threshold,name,site_icon,site_icon_url,site_logo,timezone_string,url,page_for_posts,page_on_front,show_on_front', + array( '/wp/v2/settings', 'OPTIONS' ), + ); + + // Use rest_preload_api_request to gather the preloaded data + $preload_data = array_reduce( + $preload_paths, + 'rest_preload_api_request', + array() + ); + + // Register the preloading middleware with wp-api-fetch + wp_add_inline_script( + 'wp-api-fetch', + sprintf( + 'wp.apiFetch.use( wp.apiFetch.createPreloadingMiddleware( %s ) );', + wp_json_encode( $preload_data ) + ), + 'after' + ); +} + +/** + * Render the guidelines page. + * Call this function from add_menu_page or add_submenu_page. + */ +function wp_guidelines_render_page() { + // Load build constants + $build_constants = require __DIR__ . '/../../constants.php'; + + // Set current screen + set_current_screen(); + + // Remove unwanted deprecated handler + remove_action( 'admin_head', 'wp_admin_bar_header' ); + + // Remove unwanted scripts and styles that were enqueued during `admin_init` + foreach ( wp_scripts()->queue as $script ) { + wp_dequeue_script( $script ); + } + foreach ( wp_styles()->queue as $style ) { + wp_dequeue_style( $style ); + } + + // Fire init action for extensions to register routes and menu items + do_action( 'guidelines_init' ); + + // Enqueue command palette assets for boot-based pages + if ( function_exists( 'wp_enqueue_command_palette_assets' ) ) { + wp_enqueue_command_palette_assets(); + } + + // Preload REST API data + wp_guidelines_preload_data(); + + // Get all registered routes and menu items + $menu_items = wp_get_guidelines_menu_items(); + $routes = wp_get_guidelines_routes(); + + // Get boot module asset file for dependencies + $asset_file = ABSPATH . WPINC . '/js/dist/script-modules/boot/index.min.asset.php'; + if ( file_exists( $asset_file ) ) { + $asset = require $asset_file; + + // This script serves two purposes: + // 1. It ensures all the globals that are made available to the modules are loaded. + // 2. It initializes the boot module as an inline script. + wp_register_script( 'guidelines-prerequisites', '', $asset['dependencies'], $asset['version'], true ); + + // Add inline script to initialize the app + $init_modules = []; + wp_add_inline_script( + 'guidelines-prerequisites', + sprintf( + 'import("@wordpress/boot").then(mod => mod.init({mountId: "%s", menuItems: %s, routes: %s, initModules: %s, dashboardLink: "%s"}));', + 'guidelines-app', + wp_json_encode( $menu_items, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ), + wp_json_encode( $routes, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ), + wp_json_encode( $init_modules, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ), + esc_url( admin_url( '/' ) ) + ) + ); + + // Register prerequisites style by filtering script dependencies to find registered styles + $style_dependencies = array_filter( + $asset['dependencies'], + function ( $handle ) { + return wp_style_is( $handle, 'registered' ); + } + ); + wp_register_style( 'guidelines-prerequisites', false, $style_dependencies, $asset['version'] ); + + // Build dependencies for guidelines module + $boot_dependencies = array( + array( + 'import' => 'static', + 'id' => '@wordpress/boot', + ), + ); + + // Add init modules as static dependencies + // No init modules configured + + // Add all registered routes as dependencies + foreach ( $routes as $route ) { + if ( isset( $route['route_module'] ) ) { + $boot_dependencies[] = array( + 'import' => 'static', + 'id' => $route['route_module'], + ); + } + if ( isset( $route['content_module'] ) ) { + $boot_dependencies[] = array( + 'import' => 'dynamic', + 'id' => $route['content_module'], + ); + } + } + + /** + * Filters the boot script-module dependencies for the + * guidelines page. + * + * Surfaces extending this page can append entries to the boot + * dependency list. Each entry is an array with 'import' (string + * 'static' or 'dynamic') and 'id' (script-module handle) keys. + * + * @param array $boot_dependencies Boot dependencies for the page. + */ + $boot_dependencies = apply_filters( + 'guidelines_boot_dependencies', + $boot_dependencies + ); + + // Dummy script module to ensure dependencies are loaded + wp_register_script_module( + 'guidelines', + $build_constants['build_url'] . 'pages/guidelines/loader.js', + $boot_dependencies + ); + + // Enqueue the boot scripts and styles + wp_enqueue_script( 'guidelines-prerequisites' ); + wp_enqueue_script_module( 'guidelines' ); + wp_enqueue_style( 'guidelines-prerequisites' ); + } + + // Output the HTML + ?> + + > + + + + <?php echo esc_html( get_admin_page_title() ); ?> + + + + +
+ print_import_map(); + print_footer_scripts(); + wp_script_modules()->print_enqueued_script_modules(); + wp_script_modules()->print_script_module_preloads(); + wp_script_modules()->print_script_module_data(); + + /** + * Prints scripts or data after the default footer scripts. + * + * @since 2.8.0 + */ + do_action( "admin_footer-{$hook_suffix}" ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores + // END see wp-admin/admin-footer.php + ?> + + + $path ); + if ( ! empty( $content_module ) ) { + $route['content_module'] = $content_module; + } + if ( ! empty( $route_module ) ) { + $route['route_module'] = $route_module; + } + + $wp_media_editor_wp_admin_routes[] = $route; +} + +/** + * Register a menu item for the media-editor-wp-admin page. + * Note: Menu items are registered but not displayed in single-page mode. + * + * @param string $id Menu item ID. + * @param string $label Display label. + * @param string $to Route path to navigate to. + * @param string $parent_id Optional. Parent menu item ID. + */ +function wp_register_media_editor_wp_admin_menu_item( $id, $label, $to, $parent_id = '' ) { + global $wp_media_editor_wp_admin_menu_items; + + $menu_item = array( + 'id' => $id, + 'label' => $label, + 'to' => $to, + ); + + if ( ! empty( $parent_id ) ) { + $menu_item['parent'] = $parent_id; + } + + $wp_media_editor_wp_admin_menu_items[] = $menu_item; +} + +/** + * Get all registered routes for the media-editor-wp-admin page. + * + * @return array Array of route objects. + */ +function wp_get_media_editor_wp_admin_routes() { + global $wp_media_editor_wp_admin_routes; + return $wp_media_editor_wp_admin_routes ?? array(); +} + +/** + * Get all registered menu items for the media-editor-wp-admin page. + * + * @return array Array of menu item objects. + */ +function wp_get_media_editor_wp_admin_menu_items() { + global $wp_media_editor_wp_admin_menu_items; + return $wp_media_editor_wp_admin_menu_items ?? array(); +} + +/** + * Preload REST API data for the media-editor-wp-admin page. + * Automatically called during page rendering. + */ +function wp_media_editor_wp_admin_preload_data() { + // Define paths to preload - same for all pages + // Please also change packages/core-data/src/entities.js when changing this. + $preload_paths = array( + '/?_fields=description,gmt_offset,home,image_sizes,image_size_threshold,name,site_icon,site_icon_url,site_logo,timezone_string,url,page_for_posts,page_on_front,show_on_front', + array( '/wp/v2/settings', 'OPTIONS' ), + ); + + // Use rest_preload_api_request to gather the preloaded data + $preload_data = array_reduce( + $preload_paths, + 'rest_preload_api_request', + array() + ); + + // Register the preloading middleware with wp-api-fetch + wp_add_inline_script( + 'wp-api-fetch', + sprintf( + 'wp.apiFetch.use( wp.apiFetch.createPreloadingMiddleware( %s ) );', + wp_json_encode( $preload_data ) + ), + 'after' + ); +} + +/** + * Enqueue scripts and styles for the media-editor-wp-admin page. + * Hooked to admin_enqueue_scripts. + * + * @param string $hook_suffix The current admin page. + */ +function wp_media_editor_wp_admin_enqueue_scripts( $hook_suffix ) { + // Check all possible ways this page can be accessed: + // 1. Menu page via admin.php?page=media-editor-wp-admin (plugin) + // 2. Direct file via media-editor.php (Core) - screen ID will be 'media-editor' + $current_screen = get_current_screen(); + $is_our_page = ( + ( isset( $_GET['page'] ) && 'media-editor-wp-admin' === $_GET['page'] ) || // phpcs:ignore WordPress.Security.NonceVerification.Recommended + ( $current_screen && 'media-editor' === $current_screen->id ) + ); + + if ( ! $is_our_page ) { + return; + } + + // Load build constants + $build_constants = require __DIR__ . '/../../constants.php'; + + // Fire init action for extensions to register routes and menu items + do_action( 'media-editor-wp-admin_init' ); + + // Preload REST API data + wp_media_editor_wp_admin_preload_data(); + + // Get all registered routes + $routes = wp_get_media_editor_wp_admin_routes(); + + // Get boot module asset file for dependencies + $asset_file = ABSPATH . WPINC . '/js/dist/script-modules/boot/index.min.asset.php'; + if ( file_exists( $asset_file ) ) { + $asset = require $asset_file; + + // This script serves two purposes: + // 1. It ensures all the globals that are made available to the modules are loaded. + // 2. It initializes the boot module as an inline script. + wp_register_script( 'media-editor-wp-admin-prerequisites', '', $asset['dependencies'], $asset['version'], true ); + + // Add inline script to initialize the app using initSinglePage (no menuItems) + wp_add_inline_script( + 'media-editor-wp-admin-prerequisites', + sprintf( + 'import("@wordpress/boot").then(mod => mod.initSinglePage({mountId: "%s", routes: %s}));', + 'media-editor-wp-admin-app', + wp_json_encode( $routes, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ) + ) + ); + + // Register prerequisites style by filtering script dependencies to find registered styles + $style_dependencies = array_filter( + $asset['dependencies'], + function ( $handle ) { + return wp_style_is( $handle, 'registered' ); + } + ); + wp_register_style( 'media-editor-wp-admin-prerequisites', false, $style_dependencies, $asset['version'] ); + + // Build dependencies for media-editor-wp-admin module + $boot_dependencies = array( + array( + 'import' => 'static', + 'id' => '@wordpress/boot', + ), + ); + + // Add all registered routes as dependencies + foreach ( $routes as $route ) { + if ( isset( $route['route_module'] ) ) { + $boot_dependencies[] = array( + 'import' => 'static', + 'id' => $route['route_module'], + ); + } + if ( isset( $route['content_module'] ) ) { + $boot_dependencies[] = array( + 'import' => 'dynamic', + 'id' => $route['content_module'], + ); + } + } + + /** + * Filters the boot script-module dependencies for the + * media-editor-wp-admin page. + * + * Surfaces extending this page can append entries to the boot + * dependency list. Each entry is an array with 'import' (string + * 'static' or 'dynamic') and 'id' (script-module handle) keys. + * + * @param array $boot_dependencies Boot dependencies for the page. + */ + $boot_dependencies = apply_filters( + 'media-editor-wp-admin_boot_dependencies', + $boot_dependencies + ); + + // Dummy script module to ensure dependencies are loaded + wp_register_script_module( + 'media-editor-wp-admin', + $build_constants['build_url'] . 'pages/media-editor/loader.js', + $boot_dependencies + ); + + // Enqueue the boot scripts and styles + wp_enqueue_script( 'media-editor-wp-admin-prerequisites' ); + wp_enqueue_script_module( 'media-editor-wp-admin' ); + wp_enqueue_style( 'media-editor-wp-admin-prerequisites' ); + } +} + +/** + * Render the media-editor-wp-admin page. + * Call this function from add_menu_page or add_submenu_page. + * This renders within the normal WordPress admin interface. + */ +function wp_media_editor_wp_admin_render_page() { + ?> + +
+ $path ); + if ( ! empty( $content_module ) ) { + $route['content_module'] = $content_module; + } + if ( ! empty( $route_module ) ) { + $route['route_module'] = $route_module; + } + + $wp_media_editor_routes[] = $route; +} + +/** + * Register a menu item for the media-editor page. + * + * @param string $id Menu item ID. + * @param string $label Display label. + * @param string $to Route path to navigate to. + * @param string $parent_id Optional. Parent menu item ID. + * @param string $parent_type Optional. Parent type: 'drilldown' or 'dropdown'. + */ +function wp_register_media_editor_menu_item( $id, $label, $to, $parent_id = '', $parent_type = '' ) { + global $wp_media_editor_menu_items; + + $menu_item = array( + 'id' => $id, + 'label' => $label, + 'to' => $to, + ); + + if ( ! empty( $parent_id ) ) { + $menu_item['parent'] = $parent_id; + } + + if ( ! empty( $parent_type ) && in_array( $parent_type, array( 'drilldown', 'dropdown' ), true ) ) { + $menu_item['parent_type'] = $parent_type; + } + + $wp_media_editor_menu_items[] = $menu_item; +} + +/** + * Get all registered routes for the media-editor page. + * + * @return array Array of route objects. + */ +function wp_get_media_editor_routes() { + global $wp_media_editor_routes; + return $wp_media_editor_routes ?? array(); +} + +/** + * Get all registered menu items for the media-editor page. + * + * @return array Array of menu item objects. + */ +function wp_get_media_editor_menu_items() { + global $wp_media_editor_menu_items; + return $wp_media_editor_menu_items ?? array(); +} + +/** + * Preload REST API data for the media-editor page. + * Automatically called during page rendering. + */ +function wp_media_editor_preload_data() { + // Define paths to preload - same for all pages + // Please also change packages/core-data/src/entities.js when changing this. + $preload_paths = array( + '/?_fields=description,gmt_offset,home,image_sizes,image_size_threshold,name,site_icon,site_icon_url,site_logo,timezone_string,url,page_for_posts,page_on_front,show_on_front', + array( '/wp/v2/settings', 'OPTIONS' ), + ); + + // Use rest_preload_api_request to gather the preloaded data + $preload_data = array_reduce( + $preload_paths, + 'rest_preload_api_request', + array() + ); + + // Register the preloading middleware with wp-api-fetch + wp_add_inline_script( + 'wp-api-fetch', + sprintf( + 'wp.apiFetch.use( wp.apiFetch.createPreloadingMiddleware( %s ) );', + wp_json_encode( $preload_data ) + ), + 'after' + ); +} + +/** + * Render the media-editor page. + * Call this function from add_menu_page or add_submenu_page. + */ +function wp_media_editor_render_page() { + // Load build constants + $build_constants = require __DIR__ . '/../../constants.php'; + + // Set current screen + set_current_screen(); + + // Remove unwanted deprecated handler + remove_action( 'admin_head', 'wp_admin_bar_header' ); + + // Remove unwanted scripts and styles that were enqueued during `admin_init` + foreach ( wp_scripts()->queue as $script ) { + wp_dequeue_script( $script ); + } + foreach ( wp_styles()->queue as $style ) { + wp_dequeue_style( $style ); + } + + // Fire init action for extensions to register routes and menu items + do_action( 'media-editor_init' ); + + // Enqueue command palette assets for boot-based pages + if ( function_exists( 'wp_enqueue_command_palette_assets' ) ) { + wp_enqueue_command_palette_assets(); + } + + // Preload REST API data + wp_media_editor_preload_data(); + + // Get all registered routes and menu items + $menu_items = wp_get_media_editor_menu_items(); + $routes = wp_get_media_editor_routes(); + + // Get boot module asset file for dependencies + $asset_file = ABSPATH . WPINC . '/js/dist/script-modules/boot/index.min.asset.php'; + if ( file_exists( $asset_file ) ) { + $asset = require $asset_file; + + // This script serves two purposes: + // 1. It ensures all the globals that are made available to the modules are loaded. + // 2. It initializes the boot module as an inline script. + wp_register_script( 'media-editor-prerequisites', '', $asset['dependencies'], $asset['version'], true ); + + // Add inline script to initialize the app + $init_modules = []; + wp_add_inline_script( + 'media-editor-prerequisites', + sprintf( + 'import("@wordpress/boot").then(mod => mod.init({mountId: "%s", menuItems: %s, routes: %s, initModules: %s, dashboardLink: "%s"}));', + 'media-editor-app', + wp_json_encode( $menu_items, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ), + wp_json_encode( $routes, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ), + wp_json_encode( $init_modules, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ), + esc_url( admin_url( '/' ) ) + ) + ); + + // Register prerequisites style by filtering script dependencies to find registered styles + $style_dependencies = array_filter( + $asset['dependencies'], + function ( $handle ) { + return wp_style_is( $handle, 'registered' ); + } + ); + wp_register_style( 'media-editor-prerequisites', false, $style_dependencies, $asset['version'] ); + + // Build dependencies for media-editor module + $boot_dependencies = array( + array( + 'import' => 'static', + 'id' => '@wordpress/boot', + ), + ); + + // Add init modules as static dependencies + // No init modules configured + + // Add all registered routes as dependencies + foreach ( $routes as $route ) { + if ( isset( $route['route_module'] ) ) { + $boot_dependencies[] = array( + 'import' => 'static', + 'id' => $route['route_module'], + ); + } + if ( isset( $route['content_module'] ) ) { + $boot_dependencies[] = array( + 'import' => 'dynamic', + 'id' => $route['content_module'], + ); + } + } + + /** + * Filters the boot script-module dependencies for the + * media-editor page. + * + * Surfaces extending this page can append entries to the boot + * dependency list. Each entry is an array with 'import' (string + * 'static' or 'dynamic') and 'id' (script-module handle) keys. + * + * @param array $boot_dependencies Boot dependencies for the page. + */ + $boot_dependencies = apply_filters( + 'media-editor_boot_dependencies', + $boot_dependencies + ); + + // Dummy script module to ensure dependencies are loaded + wp_register_script_module( + 'media-editor', + $build_constants['build_url'] . 'pages/media-editor/loader.js', + $boot_dependencies + ); + + // Enqueue the boot scripts and styles + wp_enqueue_script( 'media-editor-prerequisites' ); + wp_enqueue_script_module( 'media-editor' ); + wp_enqueue_style( 'media-editor-prerequisites' ); + } + + // Output the HTML + ?> + + > + + + + <?php echo esc_html( get_admin_page_title() ); ?> + + + + +
+ print_import_map(); + print_footer_scripts(); + wp_script_modules()->print_enqueued_script_modules(); + wp_script_modules()->print_script_module_preloads(); + wp_script_modules()->print_script_module_data(); + + /** + * Prints scripts or data after the default footer scripts. + * + * @since 2.8.0 + */ + do_action( "admin_footer-{$hook_suffix}" ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores + // END see wp-admin/admin-footer.php + ?> + + + function __require() { + return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); + +// package-external:@wordpress/i18n +var require_i18n = __commonJS({ + "package-external:@wordpress/i18n"(exports, module) { + module.exports = window.wp.i18n; + } +}); + +// routes/content-types/route.ts +var import_i18n = __toESM(require_i18n()); +import { POST_TYPES_PATH } from "@wordpress/content-types"; +import { redirect } from "@wordpress/route"; +var route = { + beforeLoad: () => { + throw redirect({ to: POST_TYPES_PATH }); + }, + title: () => (0, import_i18n.__)("Content Types") +}; +export { + route +}; diff --git a/src/wp-includes/build/routes/content-types/route.min.asset.php b/src/wp-includes/build/routes/content-types/route.min.asset.php new file mode 100644 index 0000000000000..f3b9dbce6843b --- /dev/null +++ b/src/wp-includes/build/routes/content-types/route.min.asset.php @@ -0,0 +1 @@ + array('wp-i18n'), 'module_dependencies' => array(array('id' => '@wordpress/content-types', 'import' => 'static'), array('id' => '@wordpress/route', 'import' => 'static')), 'version' => '8795d28ffed87f0ea7f0'); \ No newline at end of file diff --git a/src/wp-includes/build/routes/content-types/route.min.js b/src/wp-includes/build/routes/content-types/route.min.js new file mode 100644 index 0000000000000..cf7445c15d0f3 --- /dev/null +++ b/src/wp-includes/build/routes/content-types/route.min.js @@ -0,0 +1 @@ +var w=Object.create;var p=Object.defineProperty;var T=Object.getOwnPropertyDescriptor;var _=Object.getOwnPropertyNames;var d=Object.getPrototypeOf,s=Object.prototype.hasOwnProperty;var P=(t,o)=>()=>(o||t((o={exports:{}}).exports,o),o.exports);var c=(t,o,r,i)=>{if(o&&typeof o=="object"||typeof o=="function")for(let e of _(o))!s.call(t,e)&&e!==r&&p(t,e,{get:()=>o[e],enumerable:!(i=T(o,e))||i.enumerable});return t};var x=(t,o,r)=>(r=t!=null?w(d(t)):{},c(o||!t||!t.__esModule?p(r,"default",{value:t,enumerable:!0}):r,t));var n=P((h,m)=>{m.exports=window.wp.i18n});var f=x(n());import{POST_TYPES_PATH as S}from"@wordpress/content-types";import{redirect as a}from"@wordpress/route";var y={beforeLoad:()=>{throw a({to:S})},title:()=>(0,f.__)("Content Types")};export{y as route}; diff --git a/src/wp-includes/build/routes/dashboard/content.js b/src/wp-includes/build/routes/dashboard/content.js new file mode 100644 index 0000000000000..2928297529b84 --- /dev/null +++ b/src/wp-includes/build/routes/dashboard/content.js @@ -0,0 +1,38249 @@ +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __commonJS = (cb, mod) => function __require() { + return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; +}; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key2 of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key2) && key2 !== except) + __defProp(to, key2, { get: () => from[key2], enumerable: !(desc = __getOwnPropDesc(from, key2)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); + +// package-external:@wordpress/i18n +var require_i18n = __commonJS({ + "package-external:@wordpress/i18n"(exports, module) { + module.exports = window.wp.i18n; + } +}); + +// package-external:@wordpress/element +var require_element = __commonJS({ + "package-external:@wordpress/element"(exports, module) { + module.exports = window.wp.element; + } +}); + +// vendor-external:react +var require_react = __commonJS({ + "vendor-external:react"(exports, module) { + module.exports = window.React; + } +}); + +// vendor-external:react/jsx-runtime +var require_jsx_runtime = __commonJS({ + "vendor-external:react/jsx-runtime"(exports, module) { + module.exports = window.ReactJSXRuntime; + } +}); + +// vendor-external:react-dom +var require_react_dom = __commonJS({ + "vendor-external:react-dom"(exports, module) { + module.exports = window.ReactDOM; + } +}); + +// node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.development.js +var require_use_sync_external_store_shim_development = __commonJS({ + "node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.development.js"(exports) { + "use strict"; + (function() { + function is(x2, y2) { + return x2 === y2 && (0 !== x2 || 1 / x2 === 1 / y2) || x2 !== x2 && y2 !== y2; + } + function useSyncExternalStore$2(subscribe2, getSnapshot) { + didWarnOld18Alpha || void 0 === React79.startTransition || (didWarnOld18Alpha = true, console.error( + "You are using an outdated, pre-release alpha of React 18 that does not support useSyncExternalStore. The use-sync-external-store shim will not work correctly. Upgrade to a newer pre-release." + )); + var value = getSnapshot(); + if (!didWarnUncachedGetSnapshot) { + var cachedValue = getSnapshot(); + objectIs(value, cachedValue) || (console.error( + "The result of getSnapshot should be cached to avoid an infinite loop" + ), didWarnUncachedGetSnapshot = true); + } + cachedValue = useState50({ + inst: { value, getSnapshot } + }); + var inst = cachedValue[0].inst, forceUpdate = cachedValue[1]; + useLayoutEffect10( + function() { + inst.value = value; + inst.getSnapshot = getSnapshot; + checkIfSnapshotChanged(inst) && forceUpdate({ inst }); + }, + [subscribe2, value, getSnapshot] + ); + useEffect47( + function() { + checkIfSnapshotChanged(inst) && forceUpdate({ inst }); + return subscribe2(function() { + checkIfSnapshotChanged(inst) && forceUpdate({ inst }); + }); + }, + [subscribe2] + ); + useDebugValue2(value); + return value; + } + function checkIfSnapshotChanged(inst) { + var latestGetSnapshot = inst.getSnapshot; + inst = inst.value; + try { + var nextValue = latestGetSnapshot(); + return !objectIs(inst, nextValue); + } catch (error2) { + return true; + } + } + function useSyncExternalStore$1(subscribe2, getSnapshot) { + return getSnapshot(); + } + "undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error()); + var React79 = require_react(), objectIs = "function" === typeof Object.is ? Object.is : is, useState50 = React79.useState, useEffect47 = React79.useEffect, useLayoutEffect10 = React79.useLayoutEffect, useDebugValue2 = React79.useDebugValue, didWarnOld18Alpha = false, didWarnUncachedGetSnapshot = false, shim = "undefined" === typeof window || "undefined" === typeof window.document || "undefined" === typeof window.document.createElement ? useSyncExternalStore$1 : useSyncExternalStore$2; + exports.useSyncExternalStore = void 0 !== React79.useSyncExternalStore ? React79.useSyncExternalStore : shim; + "undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(Error()); + })(); + } +}); + +// node_modules/use-sync-external-store/shim/index.js +var require_shim = __commonJS({ + "node_modules/use-sync-external-store/shim/index.js"(exports, module) { + "use strict"; + if (false) { + module.exports = null; + } else { + module.exports = require_use_sync_external_store_shim_development(); + } + } +}); + +// node_modules/use-sync-external-store/cjs/use-sync-external-store-shim/with-selector.development.js +var require_with_selector_development = __commonJS({ + "node_modules/use-sync-external-store/cjs/use-sync-external-store-shim/with-selector.development.js"(exports) { + "use strict"; + (function() { + function is(x2, y2) { + return x2 === y2 && (0 !== x2 || 1 / x2 === 1 / y2) || x2 !== x2 && y2 !== y2; + } + "undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error()); + var React79 = require_react(), shim = require_shim(), objectIs = "function" === typeof Object.is ? Object.is : is, useSyncExternalStore3 = shim.useSyncExternalStore, useRef61 = React79.useRef, useEffect47 = React79.useEffect, useMemo57 = React79.useMemo, useDebugValue2 = React79.useDebugValue; + exports.useSyncExternalStoreWithSelector = function(subscribe2, getSnapshot, getServerSnapshot, selector2, isEqual) { + var instRef = useRef61(null); + if (null === instRef.current) { + var inst = { hasValue: false, value: null }; + instRef.current = inst; + } else inst = instRef.current; + instRef = useMemo57( + function() { + function memoizedSelector(nextSnapshot) { + if (!hasMemo) { + hasMemo = true; + memoizedSnapshot = nextSnapshot; + nextSnapshot = selector2(nextSnapshot); + if (void 0 !== isEqual && inst.hasValue) { + var currentSelection = inst.value; + if (isEqual(currentSelection, nextSnapshot)) + return memoizedSelection = currentSelection; + } + return memoizedSelection = nextSnapshot; + } + currentSelection = memoizedSelection; + if (objectIs(memoizedSnapshot, nextSnapshot)) + return currentSelection; + var nextSelection = selector2(nextSnapshot); + if (void 0 !== isEqual && isEqual(currentSelection, nextSelection)) + return memoizedSnapshot = nextSnapshot, currentSelection; + memoizedSnapshot = nextSnapshot; + return memoizedSelection = nextSelection; + } + var hasMemo = false, memoizedSnapshot, memoizedSelection, maybeGetServerSnapshot = void 0 === getServerSnapshot ? null : getServerSnapshot; + return [ + function() { + return memoizedSelector(getSnapshot()); + }, + null === maybeGetServerSnapshot ? void 0 : function() { + return memoizedSelector(maybeGetServerSnapshot()); + } + ]; + }, + [getSnapshot, getServerSnapshot, selector2, isEqual] + ); + var value = useSyncExternalStore3(subscribe2, instRef[0], instRef[1]); + useEffect47( + function() { + inst.hasValue = true; + inst.value = value; + }, + [value] + ); + useDebugValue2(value); + return value; + }; + "undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(Error()); + })(); + } +}); + +// node_modules/use-sync-external-store/shim/with-selector.js +var require_with_selector = __commonJS({ + "node_modules/use-sync-external-store/shim/with-selector.js"(exports, module) { + "use strict"; + if (false) { + module.exports = null; + } else { + module.exports = require_with_selector_development(); + } + } +}); + +// package-external:@wordpress/primitives +var require_primitives = __commonJS({ + "package-external:@wordpress/primitives"(exports, module) { + module.exports = window.wp.primitives; + } +}); + +// package-external:@wordpress/compose +var require_compose = __commonJS({ + "package-external:@wordpress/compose"(exports, module) { + module.exports = window.wp.compose; + } +}); + +// package-external:@wordpress/theme +var require_theme = __commonJS({ + "package-external:@wordpress/theme"(exports, module) { + module.exports = window.wp.theme; + } +}); + +// package-external:@wordpress/private-apis +var require_private_apis = __commonJS({ + "package-external:@wordpress/private-apis"(exports, module) { + module.exports = window.wp.privateApis; + } +}); + +// package-external:@wordpress/components +var require_components = __commonJS({ + "package-external:@wordpress/components"(exports, module) { + module.exports = window.wp.components; + } +}); + +// package-external:@wordpress/data +var require_data = __commonJS({ + "package-external:@wordpress/data"(exports, module) { + module.exports = window.wp.data; + } +}); + +// package-external:@wordpress/notices +var require_notices = __commonJS({ + "package-external:@wordpress/notices"(exports, module) { + module.exports = window.wp.notices; + } +}); + +// package-external:@wordpress/api-fetch +var require_api_fetch = __commonJS({ + "package-external:@wordpress/api-fetch"(exports, module) { + module.exports = window.wp.apiFetch; + } +}); + +// package-external:@wordpress/preferences +var require_preferences = __commonJS({ + "package-external:@wordpress/preferences"(exports, module) { + module.exports = window.wp.preferences; + } +}); + +// node_modules/fast-deep-equal/es6/index.js +var require_es6 = __commonJS({ + "node_modules/fast-deep-equal/es6/index.js"(exports, module) { + "use strict"; + module.exports = function equal(a2, b2) { + if (a2 === b2) return true; + if (a2 && b2 && typeof a2 == "object" && typeof b2 == "object") { + if (a2.constructor !== b2.constructor) return false; + var length, i2, keys; + if (Array.isArray(a2)) { + length = a2.length; + if (length != b2.length) return false; + for (i2 = length; i2-- !== 0; ) + if (!equal(a2[i2], b2[i2])) return false; + return true; + } + if (a2 instanceof Map && b2 instanceof Map) { + if (a2.size !== b2.size) return false; + for (i2 of a2.entries()) + if (!b2.has(i2[0])) return false; + for (i2 of a2.entries()) + if (!equal(i2[1], b2.get(i2[0]))) return false; + return true; + } + if (a2 instanceof Set && b2 instanceof Set) { + if (a2.size !== b2.size) return false; + for (i2 of a2.entries()) + if (!b2.has(i2[0])) return false; + return true; + } + if (ArrayBuffer.isView(a2) && ArrayBuffer.isView(b2)) { + length = a2.length; + if (length != b2.length) return false; + for (i2 = length; i2-- !== 0; ) + if (a2[i2] !== b2[i2]) return false; + return true; + } + if (a2.constructor === RegExp) return a2.source === b2.source && a2.flags === b2.flags; + if (a2.valueOf !== Object.prototype.valueOf) return a2.valueOf() === b2.valueOf(); + if (a2.toString !== Object.prototype.toString) return a2.toString() === b2.toString(); + keys = Object.keys(a2); + length = keys.length; + if (length !== Object.keys(b2).length) return false; + for (i2 = length; i2-- !== 0; ) + if (!Object.prototype.hasOwnProperty.call(b2, keys[i2])) return false; + for (i2 = length; i2-- !== 0; ) { + var key2 = keys[i2]; + if (!equal(a2[key2], b2[key2])) return false; + } + return true; + } + return a2 !== a2 && b2 !== b2; + }; + } +}); + +// package-external:@wordpress/keycodes +var require_keycodes = __commonJS({ + "package-external:@wordpress/keycodes"(exports, module) { + module.exports = window.wp.keycodes; + } +}); + +// node_modules/remove-accents/index.js +var require_remove_accents = __commonJS({ + "node_modules/remove-accents/index.js"(exports, module) { + var characterMap = { + "\xC0": "A", + "\xC1": "A", + "\xC2": "A", + "\xC3": "A", + "\xC4": "A", + "\xC5": "A", + "\u1EA4": "A", + "\u1EAE": "A", + "\u1EB2": "A", + "\u1EB4": "A", + "\u1EB6": "A", + "\xC6": "AE", + "\u1EA6": "A", + "\u1EB0": "A", + "\u0202": "A", + "\u1EA2": "A", + "\u1EA0": "A", + "\u1EA8": "A", + "\u1EAA": "A", + "\u1EAC": "A", + "\xC7": "C", + "\u1E08": "C", + "\xC8": "E", + "\xC9": "E", + "\xCA": "E", + "\xCB": "E", + "\u1EBE": "E", + "\u1E16": "E", + "\u1EC0": "E", + "\u1E14": "E", + "\u1E1C": "E", + "\u0206": "E", + "\u1EBA": "E", + "\u1EBC": "E", + "\u1EB8": "E", + "\u1EC2": "E", + "\u1EC4": "E", + "\u1EC6": "E", + "\xCC": "I", + "\xCD": "I", + "\xCE": "I", + "\xCF": "I", + "\u1E2E": "I", + "\u020A": "I", + "\u1EC8": "I", + "\u1ECA": "I", + "\xD0": "D", + "\xD1": "N", + "\xD2": "O", + "\xD3": "O", + "\xD4": "O", + "\xD5": "O", + "\xD6": "O", + "\xD8": "O", + "\u1ED0": "O", + "\u1E4C": "O", + "\u1E52": "O", + "\u020E": "O", + "\u1ECE": "O", + "\u1ECC": "O", + "\u1ED4": "O", + "\u1ED6": "O", + "\u1ED8": "O", + "\u1EDC": "O", + "\u1EDE": "O", + "\u1EE0": "O", + "\u1EDA": "O", + "\u1EE2": "O", + "\xD9": "U", + "\xDA": "U", + "\xDB": "U", + "\xDC": "U", + "\u1EE6": "U", + "\u1EE4": "U", + "\u1EEC": "U", + "\u1EEE": "U", + "\u1EF0": "U", + "\xDD": "Y", + "\xE0": "a", + "\xE1": "a", + "\xE2": "a", + "\xE3": "a", + "\xE4": "a", + "\xE5": "a", + "\u1EA5": "a", + "\u1EAF": "a", + "\u1EB3": "a", + "\u1EB5": "a", + "\u1EB7": "a", + "\xE6": "ae", + "\u1EA7": "a", + "\u1EB1": "a", + "\u0203": "a", + "\u1EA3": "a", + "\u1EA1": "a", + "\u1EA9": "a", + "\u1EAB": "a", + "\u1EAD": "a", + "\xE7": "c", + "\u1E09": "c", + "\xE8": "e", + "\xE9": "e", + "\xEA": "e", + "\xEB": "e", + "\u1EBF": "e", + "\u1E17": "e", + "\u1EC1": "e", + "\u1E15": "e", + "\u1E1D": "e", + "\u0207": "e", + "\u1EBB": "e", + "\u1EBD": "e", + "\u1EB9": "e", + "\u1EC3": "e", + "\u1EC5": "e", + "\u1EC7": "e", + "\xEC": "i", + "\xED": "i", + "\xEE": "i", + "\xEF": "i", + "\u1E2F": "i", + "\u020B": "i", + "\u1EC9": "i", + "\u1ECB": "i", + "\xF0": "d", + "\xF1": "n", + "\xF2": "o", + "\xF3": "o", + "\xF4": "o", + "\xF5": "o", + "\xF6": "o", + "\xF8": "o", + "\u1ED1": "o", + "\u1E4D": "o", + "\u1E53": "o", + "\u020F": "o", + "\u1ECF": "o", + "\u1ECD": "o", + "\u1ED5": "o", + "\u1ED7": "o", + "\u1ED9": "o", + "\u1EDD": "o", + "\u1EDF": "o", + "\u1EE1": "o", + "\u1EDB": "o", + "\u1EE3": "o", + "\xF9": "u", + "\xFA": "u", + "\xFB": "u", + "\xFC": "u", + "\u1EE7": "u", + "\u1EE5": "u", + "\u1EED": "u", + "\u1EEF": "u", + "\u1EF1": "u", + "\xFD": "y", + "\xFF": "y", + "\u0100": "A", + "\u0101": "a", + "\u0102": "A", + "\u0103": "a", + "\u0104": "A", + "\u0105": "a", + "\u0106": "C", + "\u0107": "c", + "\u0108": "C", + "\u0109": "c", + "\u010A": "C", + "\u010B": "c", + "\u010C": "C", + "\u010D": "c", + "C\u0306": "C", + "c\u0306": "c", + "\u010E": "D", + "\u010F": "d", + "\u0110": "D", + "\u0111": "d", + "\u0112": "E", + "\u0113": "e", + "\u0114": "E", + "\u0115": "e", + "\u0116": "E", + "\u0117": "e", + "\u0118": "E", + "\u0119": "e", + "\u011A": "E", + "\u011B": "e", + "\u011C": "G", + "\u01F4": "G", + "\u011D": "g", + "\u01F5": "g", + "\u011E": "G", + "\u011F": "g", + "\u0120": "G", + "\u0121": "g", + "\u0122": "G", + "\u0123": "g", + "\u0124": "H", + "\u0125": "h", + "\u0126": "H", + "\u0127": "h", + "\u1E2A": "H", + "\u1E2B": "h", + "\u0128": "I", + "\u0129": "i", + "\u012A": "I", + "\u012B": "i", + "\u012C": "I", + "\u012D": "i", + "\u012E": "I", + "\u012F": "i", + "\u0130": "I", + "\u0131": "i", + "\u0132": "IJ", + "\u0133": "ij", + "\u0134": "J", + "\u0135": "j", + "\u0136": "K", + "\u0137": "k", + "\u1E30": "K", + "\u1E31": "k", + "K\u0306": "K", + "k\u0306": "k", + "\u0139": "L", + "\u013A": "l", + "\u013B": "L", + "\u013C": "l", + "\u013D": "L", + "\u013E": "l", + "\u013F": "L", + "\u0140": "l", + "\u0141": "l", + "\u0142": "l", + "\u1E3E": "M", + "\u1E3F": "m", + "M\u0306": "M", + "m\u0306": "m", + "\u0143": "N", + "\u0144": "n", + "\u0145": "N", + "\u0146": "n", + "\u0147": "N", + "\u0148": "n", + "\u0149": "n", + "N\u0306": "N", + "n\u0306": "n", + "\u014C": "O", + "\u014D": "o", + "\u014E": "O", + "\u014F": "o", + "\u0150": "O", + "\u0151": "o", + "\u0152": "OE", + "\u0153": "oe", + "P\u0306": "P", + "p\u0306": "p", + "\u0154": "R", + "\u0155": "r", + "\u0156": "R", + "\u0157": "r", + "\u0158": "R", + "\u0159": "r", + "R\u0306": "R", + "r\u0306": "r", + "\u0212": "R", + "\u0213": "r", + "\u015A": "S", + "\u015B": "s", + "\u015C": "S", + "\u015D": "s", + "\u015E": "S", + "\u0218": "S", + "\u0219": "s", + "\u015F": "s", + "\u0160": "S", + "\u0161": "s", + "\u0162": "T", + "\u0163": "t", + "\u021B": "t", + "\u021A": "T", + "\u0164": "T", + "\u0165": "t", + "\u0166": "T", + "\u0167": "t", + "T\u0306": "T", + "t\u0306": "t", + "\u0168": "U", + "\u0169": "u", + "\u016A": "U", + "\u016B": "u", + "\u016C": "U", + "\u016D": "u", + "\u016E": "U", + "\u016F": "u", + "\u0170": "U", + "\u0171": "u", + "\u0172": "U", + "\u0173": "u", + "\u0216": "U", + "\u0217": "u", + "V\u0306": "V", + "v\u0306": "v", + "\u0174": "W", + "\u0175": "w", + "\u1E82": "W", + "\u1E83": "w", + "X\u0306": "X", + "x\u0306": "x", + "\u0176": "Y", + "\u0177": "y", + "\u0178": "Y", + "Y\u0306": "Y", + "y\u0306": "y", + "\u0179": "Z", + "\u017A": "z", + "\u017B": "Z", + "\u017C": "z", + "\u017D": "Z", + "\u017E": "z", + "\u017F": "s", + "\u0192": "f", + "\u01A0": "O", + "\u01A1": "o", + "\u01AF": "U", + "\u01B0": "u", + "\u01CD": "A", + "\u01CE": "a", + "\u01CF": "I", + "\u01D0": "i", + "\u01D1": "O", + "\u01D2": "o", + "\u01D3": "U", + "\u01D4": "u", + "\u01D5": "U", + "\u01D6": "u", + "\u01D7": "U", + "\u01D8": "u", + "\u01D9": "U", + "\u01DA": "u", + "\u01DB": "U", + "\u01DC": "u", + "\u1EE8": "U", + "\u1EE9": "u", + "\u1E78": "U", + "\u1E79": "u", + "\u01FA": "A", + "\u01FB": "a", + "\u01FC": "AE", + "\u01FD": "ae", + "\u01FE": "O", + "\u01FF": "o", + "\xDE": "TH", + "\xFE": "th", + "\u1E54": "P", + "\u1E55": "p", + "\u1E64": "S", + "\u1E65": "s", + "X\u0301": "X", + "x\u0301": "x", + "\u0403": "\u0413", + "\u0453": "\u0433", + "\u040C": "\u041A", + "\u045C": "\u043A", + "A\u030B": "A", + "a\u030B": "a", + "E\u030B": "E", + "e\u030B": "e", + "I\u030B": "I", + "i\u030B": "i", + "\u01F8": "N", + "\u01F9": "n", + "\u1ED2": "O", + "\u1ED3": "o", + "\u1E50": "O", + "\u1E51": "o", + "\u1EEA": "U", + "\u1EEB": "u", + "\u1E80": "W", + "\u1E81": "w", + "\u1EF2": "Y", + "\u1EF3": "y", + "\u0200": "A", + "\u0201": "a", + "\u0204": "E", + "\u0205": "e", + "\u0208": "I", + "\u0209": "i", + "\u020C": "O", + "\u020D": "o", + "\u0210": "R", + "\u0211": "r", + "\u0214": "U", + "\u0215": "u", + "B\u030C": "B", + "b\u030C": "b", + "\u010C\u0323": "C", + "\u010D\u0323": "c", + "\xCA\u030C": "E", + "\xEA\u030C": "e", + "F\u030C": "F", + "f\u030C": "f", + "\u01E6": "G", + "\u01E7": "g", + "\u021E": "H", + "\u021F": "h", + "J\u030C": "J", + "\u01F0": "j", + "\u01E8": "K", + "\u01E9": "k", + "M\u030C": "M", + "m\u030C": "m", + "P\u030C": "P", + "p\u030C": "p", + "Q\u030C": "Q", + "q\u030C": "q", + "\u0158\u0329": "R", + "\u0159\u0329": "r", + "\u1E66": "S", + "\u1E67": "s", + "V\u030C": "V", + "v\u030C": "v", + "W\u030C": "W", + "w\u030C": "w", + "X\u030C": "X", + "x\u030C": "x", + "Y\u030C": "Y", + "y\u030C": "y", + "A\u0327": "A", + "a\u0327": "a", + "B\u0327": "B", + "b\u0327": "b", + "\u1E10": "D", + "\u1E11": "d", + "\u0228": "E", + "\u0229": "e", + "\u0190\u0327": "E", + "\u025B\u0327": "e", + "\u1E28": "H", + "\u1E29": "h", + "I\u0327": "I", + "i\u0327": "i", + "\u0197\u0327": "I", + "\u0268\u0327": "i", + "M\u0327": "M", + "m\u0327": "m", + "O\u0327": "O", + "o\u0327": "o", + "Q\u0327": "Q", + "q\u0327": "q", + "U\u0327": "U", + "u\u0327": "u", + "X\u0327": "X", + "x\u0327": "x", + "Z\u0327": "Z", + "z\u0327": "z", + "\u0439": "\u0438", + "\u0419": "\u0418", + "\u0451": "\u0435", + "\u0401": "\u0415" + }; + var chars = Object.keys(characterMap).join("|"); + var allAccents = new RegExp(chars, "g"); + var firstAccent = new RegExp(chars, ""); + function matcher(match2) { + return characterMap[match2]; + } + var removeAccents3 = function(string) { + return string.replace(allAccents, matcher); + }; + var hasAccents = function(string) { + return !!string.match(firstAccent); + }; + module.exports = removeAccents3; + module.exports.has = hasAccents; + module.exports.remove = removeAccents3; + } +}); + +// package-external:@wordpress/date +var require_date = __commonJS({ + "package-external:@wordpress/date"(exports, module) { + module.exports = window.wp.date; + } +}); + +// package-external:@wordpress/warning +var require_warning = __commonJS({ + "package-external:@wordpress/warning"(exports, module) { + module.exports = window.wp.warning; + } +}); + +// package-external:@wordpress/deprecated +var require_deprecated = __commonJS({ + "package-external:@wordpress/deprecated"(exports, module) { + module.exports = window.wp.deprecated; + } +}); + +// package-external:@wordpress/core-data +var require_core_data = __commonJS({ + "package-external:@wordpress/core-data"(exports, module) { + module.exports = window.wp.coreData; + } +}); + +// node_modules/clsx/dist/clsx.mjs +function r(e2) { + var t2, f2, n2 = ""; + if ("string" == typeof e2 || "number" == typeof e2) n2 += e2; + else if ("object" == typeof e2) if (Array.isArray(e2)) { + var o2 = e2.length; + for (t2 = 0; t2 < o2; t2++) e2[t2] && (f2 = r(e2[t2])) && (n2 && (n2 += " "), n2 += f2); + } else for (f2 in e2) e2[f2] && (n2 && (n2 += " "), n2 += f2); + return n2; +} +function clsx() { + for (var e2, t2, f2 = 0, n2 = "", o2 = arguments.length; f2 < o2; f2++) (e2 = arguments[f2]) && (t2 = r(e2)) && (n2 && (n2 += " "), n2 += t2); + return n2; +} +var clsx_default = clsx; + +// node_modules/@base-ui/utils/esm/error.js +var set; +if (true) { + set = /* @__PURE__ */ new Set(); +} +function error(...messages) { + if (true) { + const messageKey = messages.join(" "); + if (!set.has(messageKey)) { + set.add(messageKey); + console.error(`Base UI: ${messageKey}`); + } + } +} + +// node_modules/@base-ui/utils/esm/useStableCallback.js +var React2 = __toESM(require_react(), 1); + +// node_modules/@base-ui/utils/esm/useRefWithInit.js +var React = __toESM(require_react(), 1); +var UNINITIALIZED = {}; +function useRefWithInit(init2, initArg) { + const ref = React.useRef(UNINITIALIZED); + if (ref.current === UNINITIALIZED) { + ref.current = init2(initArg); + } + return ref; +} + +// node_modules/@base-ui/utils/esm/useStableCallback.js +var useInsertionEffect = React2[`useInsertionEffect${Math.random().toFixed(1)}`.slice(0, -3)]; +var useSafeInsertionEffect = ( + // React 17 doesn't have useInsertionEffect. + useInsertionEffect && // Preact replaces useInsertionEffect with useLayoutEffect and fires too late. + useInsertionEffect !== React2.useLayoutEffect ? useInsertionEffect : (fn) => fn() +); +function useStableCallback(callback) { + const stable = useRefWithInit(createStableCallback).current; + stable.next = callback; + useSafeInsertionEffect(stable.effect); + return stable.trampoline; +} +function createStableCallback() { + const stable = { + next: void 0, + callback: assertNotCalled, + trampoline: (...args) => stable.callback?.(...args), + effect: () => { + stable.callback = stable.next; + } + }; + return stable; +} +function assertNotCalled() { + if (true) { + throw ( + /* minify-error-disabled */ + new Error("Base UI: Cannot call an event handler while rendering.") + ); + } +} + +// node_modules/@base-ui/utils/esm/useIsoLayoutEffect.js +var React3 = __toESM(require_react(), 1); +var noop = () => { +}; +var useIsoLayoutEffect = typeof document !== "undefined" ? React3.useLayoutEffect : noop; + +// node_modules/@base-ui/utils/esm/warn.js +var set2; +if (true) { + set2 = /* @__PURE__ */ new Set(); +} +function warn(...messages) { + if (true) { + const messageKey = messages.join(" "); + if (!set2.has(messageKey)) { + set2.add(messageKey); + console.warn(`Base UI: ${messageKey}`); + } + } +} + +// node_modules/@base-ui/react/esm/internals/direction-context/DirectionContext.js +var React4 = __toESM(require_react(), 1); +var DirectionContext = /* @__PURE__ */ React4.createContext(void 0); +if (true) DirectionContext.displayName = "DirectionContext"; +function useDirection() { + const context = React4.useContext(DirectionContext); + return context?.direction ?? "ltr"; +} + +// node_modules/@base-ui/react/esm/internals/useRenderElement.js +var React7 = __toESM(require_react(), 1); + +// node_modules/@base-ui/utils/esm/useMergedRefs.js +function useMergedRefs(a2, b2, c2, d2) { + const forkRef = useRefWithInit(createForkRef).current; + if (didChange(forkRef, a2, b2, c2, d2)) { + update(forkRef, [a2, b2, c2, d2]); + } + return forkRef.callback; +} +function useMergedRefsN(refs) { + const forkRef = useRefWithInit(createForkRef).current; + if (didChangeN(forkRef, refs)) { + update(forkRef, refs); + } + return forkRef.callback; +} +function createForkRef() { + return { + callback: null, + cleanup: null, + refs: [] + }; +} +function didChange(forkRef, a2, b2, c2, d2) { + return forkRef.refs[0] !== a2 || forkRef.refs[1] !== b2 || forkRef.refs[2] !== c2 || forkRef.refs[3] !== d2; +} +function didChangeN(forkRef, newRefs) { + return forkRef.refs.length !== newRefs.length || forkRef.refs.some((ref, index2) => ref !== newRefs[index2]); +} +function update(forkRef, refs) { + forkRef.refs = refs; + if (refs.every((ref) => ref == null)) { + forkRef.callback = null; + return; + } + forkRef.callback = (instance) => { + if (forkRef.cleanup) { + forkRef.cleanup(); + forkRef.cleanup = null; + } + if (instance != null) { + const cleanupCallbacks = Array(refs.length).fill(null); + for (let i2 = 0; i2 < refs.length; i2 += 1) { + const ref = refs[i2]; + if (ref == null) { + continue; + } + switch (typeof ref) { + case "function": { + const refCleanup = ref(instance); + if (typeof refCleanup === "function") { + cleanupCallbacks[i2] = refCleanup; + } + break; + } + case "object": { + ref.current = instance; + break; + } + default: + } + } + forkRef.cleanup = () => { + for (let i2 = 0; i2 < refs.length; i2 += 1) { + const ref = refs[i2]; + if (ref == null) { + continue; + } + switch (typeof ref) { + case "function": { + const cleanupCallback = cleanupCallbacks[i2]; + if (typeof cleanupCallback === "function") { + cleanupCallback(); + } else { + ref(null); + } + break; + } + case "object": { + ref.current = null; + break; + } + default: + } + } + }; + } + }; +} + +// node_modules/@base-ui/utils/esm/getReactElementRef.js +var React6 = __toESM(require_react(), 1); + +// node_modules/@base-ui/utils/esm/reactVersion.js +var React5 = __toESM(require_react(), 1); +var majorVersion = parseInt(React5.version, 10); +function isReactVersionAtLeast(reactVersionToCheck) { + return majorVersion >= reactVersionToCheck; +} + +// node_modules/@base-ui/utils/esm/getReactElementRef.js +function getReactElementRef(element) { + if (!/* @__PURE__ */ React6.isValidElement(element)) { + return null; + } + const reactElement = element; + const propsWithRef = reactElement.props; + return (isReactVersionAtLeast(19) ? propsWithRef?.ref : reactElement.ref) ?? null; +} + +// node_modules/@base-ui/utils/esm/mergeObjects.js +function mergeObjects(a2, b2) { + if (a2 && !b2) { + return a2; + } + if (!a2 && b2) { + return b2; + } + if (a2 || b2) { + return { + ...a2, + ...b2 + }; + } + return void 0; +} + +// node_modules/@base-ui/utils/esm/empty.js +function NOOP() { +} +var EMPTY_ARRAY = Object.freeze([]); +var EMPTY_OBJECT = Object.freeze({}); + +// node_modules/@base-ui/react/esm/internals/getStateAttributesProps.js +function getStateAttributesProps(state, customMapping) { + const props = {}; + for (const key2 in state) { + const value = state[key2]; + if (customMapping?.hasOwnProperty(key2)) { + const customProps = customMapping[key2](value); + if (customProps != null) { + Object.assign(props, customProps); + } + continue; + } + if (value === true) { + props[`data-${key2.toLowerCase()}`] = ""; + } else if (value) { + props[`data-${key2.toLowerCase()}`] = value.toString(); + } + } + return props; +} + +// node_modules/@base-ui/react/esm/utils/resolveClassName.js +function resolveClassName(className, state) { + return typeof className === "function" ? className(state) : className; +} + +// node_modules/@base-ui/react/esm/utils/resolveStyle.js +function resolveStyle(style, state) { + return typeof style === "function" ? style(state) : style; +} + +// node_modules/@base-ui/react/esm/merge-props/mergeProps.js +var EMPTY_PROPS = {}; +function mergeProps(a2, b2, c2, d2, e2) { + if (!c2 && !d2 && !e2 && !a2) { + return createInitialMergedProps(b2); + } + let merged = createInitialMergedProps(a2); + if (b2) { + merged = mergeInto(merged, b2); + } + if (c2) { + merged = mergeInto(merged, c2); + } + if (d2) { + merged = mergeInto(merged, d2); + } + if (e2) { + merged = mergeInto(merged, e2); + } + return merged; +} +function mergePropsN(props) { + if (props.length === 0) { + return EMPTY_PROPS; + } + if (props.length === 1) { + return createInitialMergedProps(props[0]); + } + let merged = createInitialMergedProps(props[0]); + for (let i2 = 1; i2 < props.length; i2 += 1) { + merged = mergeInto(merged, props[i2]); + } + return merged; +} +function createInitialMergedProps(inputProps) { + if (isPropsGetter(inputProps)) { + return { + ...resolvePropsGetter(inputProps, EMPTY_PROPS) + }; + } + return copyInitialProps(inputProps); +} +function mergeInto(merged, inputProps) { + if (isPropsGetter(inputProps)) { + return resolvePropsGetter(inputProps, merged); + } + return mutablyMergeInto(merged, inputProps); +} +function copyInitialProps(inputProps) { + const copiedProps = { + ...inputProps + }; + for (const propName in copiedProps) { + const propValue = copiedProps[propName]; + if (isEventHandler(propName, propValue)) { + copiedProps[propName] = wrapEventHandler(propValue); + } + } + return copiedProps; +} +function mutablyMergeInto(mergedProps, externalProps) { + if (!externalProps) { + return mergedProps; + } + for (const propName in externalProps) { + const externalPropValue = externalProps[propName]; + switch (propName) { + case "style": { + mergedProps[propName] = mergeObjects(mergedProps.style, externalPropValue); + break; + } + case "className": { + mergedProps[propName] = mergeClassNames(mergedProps.className, externalPropValue); + break; + } + default: { + if (isEventHandler(propName, externalPropValue)) { + mergedProps[propName] = mergeEventHandlers(mergedProps[propName], externalPropValue); + } else { + mergedProps[propName] = externalPropValue; + } + } + } + } + return mergedProps; +} +function isEventHandler(key2, value) { + const code0 = key2.charCodeAt(0); + const code1 = key2.charCodeAt(1); + const code2 = key2.charCodeAt(2); + return code0 === 111 && code1 === 110 && code2 >= 65 && code2 <= 90 && (typeof value === "function" || typeof value === "undefined"); +} +function isPropsGetter(inputProps) { + return typeof inputProps === "function"; +} +function resolvePropsGetter(inputProps, previousProps) { + if (isPropsGetter(inputProps)) { + return inputProps(previousProps); + } + return inputProps ?? EMPTY_PROPS; +} +function mergeEventHandlers(ourHandler, theirHandler) { + if (!theirHandler) { + return ourHandler; + } + if (!ourHandler) { + return wrapEventHandler(theirHandler); + } + return (...args) => { + const event = args[0]; + if (isSyntheticEvent(event)) { + const baseUIEvent = event; + makeEventPreventable(baseUIEvent); + const result2 = theirHandler(...args); + if (!baseUIEvent.baseUIHandlerPrevented) { + ourHandler?.(...args); + } + return result2; + } + const result = theirHandler(...args); + ourHandler?.(...args); + return result; + }; +} +function wrapEventHandler(handler) { + if (!handler) { + return handler; + } + return (...args) => { + const event = args[0]; + if (isSyntheticEvent(event)) { + makeEventPreventable(event); + } + return handler(...args); + }; +} +function makeEventPreventable(event) { + event.preventBaseUIHandler = () => { + event.baseUIHandlerPrevented = true; + }; + return event; +} +function mergeClassNames(ourClassName, theirClassName) { + if (theirClassName) { + if (ourClassName) { + return theirClassName + " " + ourClassName; + } + return theirClassName; + } + return ourClassName; +} +function isSyntheticEvent(event) { + return event != null && typeof event === "object" && "nativeEvent" in event; +} + +// node_modules/@base-ui/react/esm/internals/useRenderElement.js +var import_react = __toESM(require_react(), 1); +function useRenderElement(element, componentProps, params = {}) { + const renderProp = componentProps.render; + const outProps = useRenderElementProps(componentProps, params); + if (params.enabled === false) { + return null; + } + const state = params.state ?? EMPTY_OBJECT; + return evaluateRenderProp(element, renderProp, outProps, state); +} +function useRenderElementProps(componentProps, params = {}) { + const { + className: classNameProp, + style: styleProp, + render: renderProp + } = componentProps; + const { + state = EMPTY_OBJECT, + ref, + props, + stateAttributesMapping: stateAttributesMapping6, + enabled = true + } = params; + const className = enabled ? resolveClassName(classNameProp, state) : void 0; + const style = enabled ? resolveStyle(styleProp, state) : void 0; + const stateProps = enabled ? getStateAttributesProps(state, stateAttributesMapping6) : EMPTY_OBJECT; + const resolvedProps = enabled && props ? resolveRenderFunctionProps(props) : void 0; + const outProps = enabled ? mergeObjects(stateProps, resolvedProps) ?? {} : EMPTY_OBJECT; + if (typeof document !== "undefined") { + if (!enabled) { + useMergedRefs(null, null); + } else if (Array.isArray(ref)) { + outProps.ref = useMergedRefsN([outProps.ref, getReactElementRef(renderProp), ...ref]); + } else { + outProps.ref = useMergedRefs(outProps.ref, getReactElementRef(renderProp), ref); + } + } + if (!enabled) { + return EMPTY_OBJECT; + } + if (className !== void 0) { + outProps.className = mergeClassNames(outProps.className, className); + } + if (style !== void 0) { + outProps.style = mergeObjects(outProps.style, style); + } + return outProps; +} +function resolveRenderFunctionProps(props) { + if (Array.isArray(props)) { + return mergePropsN(props); + } + return mergeProps(void 0, props); +} +var REACT_LAZY_TYPE = /* @__PURE__ */ Symbol.for("react.lazy"); +var COMPONENT_IDENTIFIER_PATTERN = /^[A-Z][A-Za-z0-9$]*$/; +var LOWERCASE_CHARACTER_PATTERN = /[a-z]/; +function evaluateRenderProp(element, render4, props, state) { + if (render4) { + if (typeof render4 === "function") { + if (true) { + warnIfRenderPropLooksLikeComponent(render4); + } + return render4(props, state); + } + const mergedProps = mergeProps(props, render4.props); + mergedProps.ref = props.ref; + let newElement = render4; + if (newElement?.$$typeof === REACT_LAZY_TYPE) { + const children = React7.Children.toArray(render4); + newElement = children[0]; + } + if (true) { + if (!/* @__PURE__ */ React7.isValidElement(newElement)) { + throw new Error(["Base UI: The `render` prop was provided an invalid React element as `React.isValidElement(render)` is `false`.", "A valid React element must be provided to the `render` prop because it is cloned with props to replace the default element.", "https://base-ui.com/r/invalid-render-prop"].join("\n")); + } + } + return /* @__PURE__ */ React7.cloneElement(newElement, mergedProps); + } + if (element) { + if (typeof element === "string") { + return renderTag(element, props); + } + } + throw new Error(true ? "Base UI: Render element or function are not defined." : formatErrorMessage_default(8)); +} +function warnIfRenderPropLooksLikeComponent(renderFn) { + const functionName = renderFn.name; + if (functionName.length === 0) { + return; + } + if (!COMPONENT_IDENTIFIER_PATTERN.test(functionName)) { + return; + } + if (!LOWERCASE_CHARACTER_PATTERN.test(functionName)) { + return; + } + warn(`The \`render\` prop received a function named \`${functionName}\` that starts with an uppercase letter.`, "This usually means a React component was passed directly as `render={Component}`.", "Base UI calls `render` as a plain function, which can break the Rules of Hooks during reconciliation.", "If this is an intentional render callback, rename it to start with a lowercase letter.", "Use `render={}` or `render={(props) => }` instead.", "https://base-ui.com/r/invalid-render-prop"); +} +function renderTag(Tag, props) { + if (Tag === "button") { + return /* @__PURE__ */ (0, import_react.createElement)("button", { + type: "button", + ...props, + key: props.key + }); + } + if (Tag === "img") { + return /* @__PURE__ */ (0, import_react.createElement)("img", { + alt: "", + ...props, + key: props.key + }); + } + return /* @__PURE__ */ React7.createElement(Tag, props); +} + +// node_modules/@base-ui/react/esm/internals/reason-parts.js +var reason_parts_exports = {}; +__export(reason_parts_exports, { + cancelOpen: () => cancelOpen, + chipRemovePress: () => chipRemovePress, + clearPress: () => clearPress, + closePress: () => closePress, + closeWatcher: () => closeWatcher, + decrementPress: () => decrementPress, + disabled: () => disabled, + drag: () => drag, + escapeKey: () => escapeKey, + focusOut: () => focusOut, + imperativeAction: () => imperativeAction, + incrementPress: () => incrementPress, + inputBlur: () => inputBlur, + inputChange: () => inputChange, + inputClear: () => inputClear, + inputPaste: () => inputPaste, + inputPress: () => inputPress, + itemPress: () => itemPress, + keyboard: () => keyboard, + linkPress: () => linkPress, + listNavigation: () => listNavigation, + none: () => none, + outsidePress: () => outsidePress, + pointer: () => pointer, + scrub: () => scrub, + siblingOpen: () => siblingOpen, + swipe: () => swipe, + trackPress: () => trackPress, + triggerFocus: () => triggerFocus, + triggerHover: () => triggerHover, + triggerPress: () => triggerPress, + wheel: () => wheel, + windowResize: () => windowResize +}); +var none = "none"; +var triggerPress = "trigger-press"; +var triggerHover = "trigger-hover"; +var triggerFocus = "trigger-focus"; +var outsidePress = "outside-press"; +var itemPress = "item-press"; +var closePress = "close-press"; +var linkPress = "link-press"; +var clearPress = "clear-press"; +var chipRemovePress = "chip-remove-press"; +var trackPress = "track-press"; +var incrementPress = "increment-press"; +var decrementPress = "decrement-press"; +var inputChange = "input-change"; +var inputClear = "input-clear"; +var inputBlur = "input-blur"; +var inputPaste = "input-paste"; +var inputPress = "input-press"; +var focusOut = "focus-out"; +var escapeKey = "escape-key"; +var closeWatcher = "close-watcher"; +var listNavigation = "list-navigation"; +var keyboard = "keyboard"; +var pointer = "pointer"; +var drag = "drag"; +var wheel = "wheel"; +var scrub = "scrub"; +var cancelOpen = "cancel-open"; +var siblingOpen = "sibling-open"; +var disabled = "disabled"; +var imperativeAction = "imperative-action"; +var swipe = "swipe"; +var windowResize = "window-resize"; + +// node_modules/@base-ui/react/esm/internals/createBaseUIEventDetails.js +function createChangeEventDetails(reason, event, trigger, customProperties) { + let canceled = false; + let allowPropagation = false; + const custom = customProperties ?? EMPTY_OBJECT; + const details = { + reason, + event: event ?? new Event("base-ui"), + cancel() { + canceled = true; + }, + allowPropagation() { + allowPropagation = true; + }, + get isCanceled() { + return canceled; + }, + get isPropagationAllowed() { + return allowPropagation; + }, + trigger, + ...custom + }; + return details; +} + +// node_modules/@base-ui/utils/esm/useId.js +var React9 = __toESM(require_react(), 1); + +// node_modules/@base-ui/utils/esm/safeReact.js +var React8 = __toESM(require_react(), 1); +var SafeReact = { + ...React8 +}; + +// node_modules/@base-ui/utils/esm/useId.js +var globalId = 0; +function useGlobalId(idOverride, prefix = "mui") { + const [defaultId, setDefaultId] = React9.useState(idOverride); + const id = idOverride || defaultId; + React9.useEffect(() => { + if (defaultId == null) { + globalId += 1; + setDefaultId(`${prefix}-${globalId}`); + } + }, [defaultId, prefix]); + return id; +} +var maybeReactUseId = SafeReact.useId; +function useId(idOverride, prefix) { + if (maybeReactUseId !== void 0) { + const reactId = maybeReactUseId(); + return idOverride ?? (prefix ? `${prefix}-${reactId}` : reactId); + } + return useGlobalId(idOverride, prefix); +} + +// node_modules/@base-ui/react/esm/internals/useBaseUiId.js +function useBaseUiId(idOverride) { + return useId(idOverride, "base-ui"); +} + +// node_modules/@base-ui/react/esm/internals/useAnimationsFinished.js +var ReactDOM = __toESM(require_react_dom(), 1); + +// node_modules/@base-ui/utils/esm/useOnMount.js +var React10 = __toESM(require_react(), 1); +var EMPTY = []; +function useOnMount(fn) { + React10.useEffect(fn, EMPTY); +} + +// node_modules/@base-ui/utils/esm/useAnimationFrame.js +var EMPTY2 = null; +var LAST_RAF = globalThis.requestAnimationFrame; +var Scheduler = class { + /* This implementation uses an array as a backing data-structure for frame callbacks. + * It allows `O(1)` callback cancelling by inserting a `null` in the array, though it + * never calls the native `cancelAnimationFrame` if there are no frames left. This can + * be much more efficient if there is a call pattern that alterns as + * "request-cancel-request-cancel-…". + * But in the case of "request-request-…-cancel-cancel-…", it leaves the final animation + * frame to run anyway. We turn that frame into a `O(1)` no-op via `callbacksCount`. */ + callbacks = []; + callbacksCount = 0; + nextId = 1; + startId = 1; + isScheduled = false; + tick = (timestamp) => { + this.isScheduled = false; + const currentCallbacks = this.callbacks; + const currentCallbacksCount = this.callbacksCount; + this.callbacks = []; + this.callbacksCount = 0; + this.startId = this.nextId; + if (currentCallbacksCount > 0) { + for (let i2 = 0; i2 < currentCallbacks.length; i2 += 1) { + currentCallbacks[i2]?.(timestamp); + } + } + }; + request(fn) { + const id = this.nextId; + this.nextId += 1; + this.callbacks.push(fn); + this.callbacksCount += 1; + const didRAFChange = LAST_RAF !== requestAnimationFrame && (LAST_RAF = requestAnimationFrame, true); + if (!this.isScheduled || didRAFChange) { + requestAnimationFrame(this.tick); + this.isScheduled = true; + } + return id; + } + cancel(id) { + const index2 = id - this.startId; + if (index2 < 0 || index2 >= this.callbacks.length) { + return; + } + this.callbacks[index2] = null; + this.callbacksCount -= 1; + } +}; +var scheduler = new Scheduler(); +var AnimationFrame = class _AnimationFrame { + static create() { + return new _AnimationFrame(); + } + static request(fn) { + return scheduler.request(fn); + } + static cancel(id) { + return scheduler.cancel(id); + } + currentId = EMPTY2; + /** + * Executes `fn` after `delay`, clearing any previously scheduled call. + */ + request(fn) { + this.cancel(); + this.currentId = scheduler.request(() => { + this.currentId = EMPTY2; + fn(); + }); + } + cancel = () => { + if (this.currentId !== EMPTY2) { + scheduler.cancel(this.currentId); + this.currentId = EMPTY2; + } + }; + disposeEffect = () => { + return this.cancel; + }; +}; +function useAnimationFrame() { + const timeout = useRefWithInit(AnimationFrame.create).current; + useOnMount(timeout.disposeEffect); + return timeout; +} + +// node_modules/@base-ui/react/esm/utils/resolveRef.js +function resolveRef(maybeRef) { + if (maybeRef == null) { + return maybeRef; + } + return "current" in maybeRef ? maybeRef.current : maybeRef; +} + +// node_modules/@base-ui/react/esm/internals/stateAttributesMapping.js +var TransitionStatusDataAttributes = /* @__PURE__ */ (function(TransitionStatusDataAttributes2) { + TransitionStatusDataAttributes2["startingStyle"] = "data-starting-style"; + TransitionStatusDataAttributes2["endingStyle"] = "data-ending-style"; + return TransitionStatusDataAttributes2; +})({}); +var STARTING_HOOK = { + [TransitionStatusDataAttributes.startingStyle]: "" +}; +var ENDING_HOOK = { + [TransitionStatusDataAttributes.endingStyle]: "" +}; +var transitionStatusMapping = { + transitionStatus(value) { + if (value === "starting") { + return STARTING_HOOK; + } + if (value === "ending") { + return ENDING_HOOK; + } + return null; + } +}; + +// node_modules/@base-ui/react/esm/internals/useAnimationsFinished.js +function useAnimationsFinished(elementOrRef, waitForStartingStyleRemoved = false, treatAbortedAsFinished = true) { + const frame = useAnimationFrame(); + return useStableCallback((fnToExecute, signal = null) => { + frame.cancel(); + const element = resolveRef(elementOrRef); + if (element == null) { + return; + } + const resolvedElement = element; + const done = () => { + ReactDOM.flushSync(fnToExecute); + }; + if (typeof resolvedElement.getAnimations !== "function" || globalThis.BASE_UI_ANIMATIONS_DISABLED) { + fnToExecute(); + return; + } + function exec() { + Promise.all(resolvedElement.getAnimations().map((animation) => animation.finished)).then(() => { + if (!signal?.aborted) { + done(); + } + }).catch(() => { + if (treatAbortedAsFinished) { + if (!signal?.aborted) { + done(); + } + return; + } + const currentAnimations = resolvedElement.getAnimations(); + if (!signal?.aborted && currentAnimations.length > 0 && currentAnimations.some((animation) => animation.pending || animation.playState !== "finished")) { + exec(); + } + }); + } + if (waitForStartingStyleRemoved) { + const startingStyleAttribute = TransitionStatusDataAttributes.startingStyle; + if (!resolvedElement.hasAttribute(startingStyleAttribute)) { + frame.request(exec); + return; + } + const attributeObserver = new MutationObserver(() => { + if (!resolvedElement.hasAttribute(startingStyleAttribute)) { + attributeObserver.disconnect(); + exec(); + } + }); + attributeObserver.observe(resolvedElement, { + attributes: true, + attributeFilter: [startingStyleAttribute] + }); + signal?.addEventListener("abort", () => attributeObserver.disconnect(), { + once: true + }); + return; + } + frame.request(exec); + }); +} + +// node_modules/@base-ui/react/esm/internals/useTransitionStatus.js +var React11 = __toESM(require_react(), 1); +function useTransitionStatus(open, enableIdleState = false, deferEndingState = false) { + const [transitionStatus, setTransitionStatus] = React11.useState(open && enableIdleState ? "idle" : void 0); + const [mounted, setMounted] = React11.useState(open); + if (open && !mounted) { + setMounted(true); + setTransitionStatus("starting"); + } + if (!open && mounted && transitionStatus !== "ending" && !deferEndingState) { + setTransitionStatus("ending"); + } + if (!open && !mounted && transitionStatus === "ending") { + setTransitionStatus(void 0); + } + useIsoLayoutEffect(() => { + if (!open && mounted && transitionStatus !== "ending" && deferEndingState) { + const frame = AnimationFrame.request(() => { + setTransitionStatus("ending"); + }); + return () => { + AnimationFrame.cancel(frame); + }; + } + return void 0; + }, [open, mounted, transitionStatus, deferEndingState]); + useIsoLayoutEffect(() => { + if (!open || enableIdleState) { + return void 0; + } + const frame = AnimationFrame.request(() => { + setTransitionStatus(void 0); + }); + return () => { + AnimationFrame.cancel(frame); + }; + }, [enableIdleState, open]); + useIsoLayoutEffect(() => { + if (!open || !enableIdleState) { + return void 0; + } + if (open && mounted && transitionStatus !== "idle") { + setTransitionStatus("starting"); + } + const frame = AnimationFrame.request(() => { + setTransitionStatus("idle"); + }); + return () => { + AnimationFrame.cancel(frame); + }; + }, [enableIdleState, open, mounted, transitionStatus]); + return { + mounted, + setMounted, + transitionStatus + }; +} + +// node_modules/@base-ui/react/esm/internals/use-button/useButton.js +var React14 = __toESM(require_react(), 1); + +// node_modules/@floating-ui/utils/dist/floating-ui.utils.dom.mjs +function hasWindow() { + return typeof window !== "undefined"; +} +function getNodeName(node) { + if (isNode(node)) { + return (node.nodeName || "").toLowerCase(); + } + return "#document"; +} +function getWindow(node) { + var _node$ownerDocument; + return (node == null || (_node$ownerDocument = node.ownerDocument) == null ? void 0 : _node$ownerDocument.defaultView) || window; +} +function getDocumentElement(node) { + var _ref; + return (_ref = (isNode(node) ? node.ownerDocument : node.document) || window.document) == null ? void 0 : _ref.documentElement; +} +function isNode(value) { + if (!hasWindow()) { + return false; + } + return value instanceof Node || value instanceof getWindow(value).Node; +} +function isElement(value) { + if (!hasWindow()) { + return false; + } + return value instanceof Element || value instanceof getWindow(value).Element; +} +function isHTMLElement(value) { + if (!hasWindow()) { + return false; + } + return value instanceof HTMLElement || value instanceof getWindow(value).HTMLElement; +} +function isShadowRoot(value) { + if (!hasWindow() || typeof ShadowRoot === "undefined") { + return false; + } + return value instanceof ShadowRoot || value instanceof getWindow(value).ShadowRoot; +} +function isOverflowElement(element) { + const { + overflow, + overflowX, + overflowY, + display + } = getComputedStyle2(element); + return /auto|scroll|overlay|hidden|clip/.test(overflow + overflowY + overflowX) && display !== "inline" && display !== "contents"; +} +function isTableElement(element) { + return /^(table|td|th)$/.test(getNodeName(element)); +} +function isTopLayer(element) { + try { + if (element.matches(":popover-open")) { + return true; + } + } catch (_e) { + } + try { + return element.matches(":modal"); + } catch (_e) { + return false; + } +} +var willChangeRe = /transform|translate|scale|rotate|perspective|filter/; +var containRe = /paint|layout|strict|content/; +var isNotNone = (value) => !!value && value !== "none"; +var isWebKitValue; +function isContainingBlock(elementOrCss) { + const css = isElement(elementOrCss) ? getComputedStyle2(elementOrCss) : elementOrCss; + return isNotNone(css.transform) || isNotNone(css.translate) || isNotNone(css.scale) || isNotNone(css.rotate) || isNotNone(css.perspective) || !isWebKit() && (isNotNone(css.backdropFilter) || isNotNone(css.filter)) || willChangeRe.test(css.willChange || "") || containRe.test(css.contain || ""); +} +function getContainingBlock(element) { + let currentNode = getParentNode(element); + while (isHTMLElement(currentNode) && !isLastTraversableNode(currentNode)) { + if (isContainingBlock(currentNode)) { + return currentNode; + } else if (isTopLayer(currentNode)) { + return null; + } + currentNode = getParentNode(currentNode); + } + return null; +} +function isWebKit() { + if (isWebKitValue == null) { + isWebKitValue = typeof CSS !== "undefined" && CSS.supports && CSS.supports("-webkit-backdrop-filter", "none"); + } + return isWebKitValue; +} +function isLastTraversableNode(node) { + return /^(html|body|#document)$/.test(getNodeName(node)); +} +function getComputedStyle2(element) { + return getWindow(element).getComputedStyle(element); +} +function getNodeScroll(element) { + if (isElement(element)) { + return { + scrollLeft: element.scrollLeft, + scrollTop: element.scrollTop + }; + } + return { + scrollLeft: element.scrollX, + scrollTop: element.scrollY + }; +} +function getParentNode(node) { + if (getNodeName(node) === "html") { + return node; + } + const result = ( + // Step into the shadow DOM of the parent of a slotted node. + node.assignedSlot || // DOM Element detected. + node.parentNode || // ShadowRoot detected. + isShadowRoot(node) && node.host || // Fallback. + getDocumentElement(node) + ); + return isShadowRoot(result) ? result.host : result; +} +function getNearestOverflowAncestor(node) { + const parentNode = getParentNode(node); + if (isLastTraversableNode(parentNode)) { + return node.ownerDocument ? node.ownerDocument.body : node.body; + } + if (isHTMLElement(parentNode) && isOverflowElement(parentNode)) { + return parentNode; + } + return getNearestOverflowAncestor(parentNode); +} +function getOverflowAncestors(node, list, traverseIframes) { + var _node$ownerDocument2; + if (list === void 0) { + list = []; + } + if (traverseIframes === void 0) { + traverseIframes = true; + } + const scrollableAncestor = getNearestOverflowAncestor(node); + const isBody = scrollableAncestor === ((_node$ownerDocument2 = node.ownerDocument) == null ? void 0 : _node$ownerDocument2.body); + const win = getWindow(scrollableAncestor); + if (isBody) { + const frameElement = getFrameElement(win); + return list.concat(win, win.visualViewport || [], isOverflowElement(scrollableAncestor) ? scrollableAncestor : [], frameElement && traverseIframes ? getOverflowAncestors(frameElement) : []); + } else { + return list.concat(scrollableAncestor, getOverflowAncestors(scrollableAncestor, [], traverseIframes)); + } +} +function getFrameElement(win) { + return win.parent && Object.getPrototypeOf(win.parent) ? win.frameElement : null; +} + +// node_modules/@base-ui/react/esm/internals/composite/root/CompositeRootContext.js +var React12 = __toESM(require_react(), 1); +var CompositeRootContext = /* @__PURE__ */ React12.createContext(void 0); +if (true) CompositeRootContext.displayName = "CompositeRootContext"; +function useCompositeRootContext(optional = false) { + const context = React12.useContext(CompositeRootContext); + if (context === void 0 && !optional) { + throw new Error(true ? "Base UI: CompositeRootContext is missing. Composite parts must be placed within ." : formatErrorMessage_default(16)); + } + return context; +} + +// node_modules/@base-ui/react/esm/utils/useFocusableWhenDisabled.js +var React13 = __toESM(require_react(), 1); +function useFocusableWhenDisabled(parameters) { + const { + focusableWhenDisabled, + disabled: disabled2, + composite = false, + tabIndex: tabIndexProp = 0, + isNativeButton + } = parameters; + const isFocusableComposite = composite && focusableWhenDisabled !== false; + const isNonFocusableComposite = composite && focusableWhenDisabled === false; + const props = React13.useMemo(() => { + const additionalProps = { + // allow Tabbing away from focusableWhenDisabled elements + onKeyDown(event) { + if (disabled2 && focusableWhenDisabled && event.key !== "Tab") { + event.preventDefault(); + } + } + }; + if (!composite) { + additionalProps.tabIndex = tabIndexProp; + if (!isNativeButton && disabled2) { + additionalProps.tabIndex = focusableWhenDisabled ? tabIndexProp : -1; + } + } + if (isNativeButton && (focusableWhenDisabled || isFocusableComposite) || !isNativeButton && disabled2) { + additionalProps["aria-disabled"] = disabled2; + } + if (isNativeButton && (!focusableWhenDisabled || isNonFocusableComposite)) { + additionalProps.disabled = disabled2; + } + return additionalProps; + }, [composite, disabled2, focusableWhenDisabled, isFocusableComposite, isNonFocusableComposite, isNativeButton, tabIndexProp]); + return { + props + }; +} + +// node_modules/@base-ui/react/esm/internals/use-button/useButton.js +function useButton(parameters = {}) { + const { + disabled: disabled2 = false, + focusableWhenDisabled, + tabIndex = 0, + native: isNativeButton = true, + composite: compositeProp + } = parameters; + const elementRef = React14.useRef(null); + const compositeRootContext = useCompositeRootContext(true); + const isCompositeItem = compositeProp ?? compositeRootContext !== void 0; + const { + props: focusableWhenDisabledProps + } = useFocusableWhenDisabled({ + focusableWhenDisabled, + disabled: disabled2, + composite: isCompositeItem, + tabIndex, + isNativeButton + }); + if (true) { + React14.useEffect(() => { + if (!elementRef.current) { + return; + } + const isButtonTag = isButtonElement(elementRef.current); + if (isNativeButton) { + if (!isButtonTag) { + const ownerStackMessage = SafeReact.captureOwnerStack?.() || ""; + const message2 = "A component that acts as a button expected a native