diff --git a/.github/files/check-lock-files.sh b/.github/files/check-lock-files.sh index 92ed57eacfa21..716ad717460b6 100755 --- a/.github/files/check-lock-files.sh +++ b/.github/files/check-lock-files.sh @@ -30,7 +30,7 @@ for FILE in $(git -c core.quotepath=off ls-files 'pnpm-lock.yaml' '**/pnpm-lock. echo "::endgroup::" if ! git diff --exit-code pnpm-lock.yaml; then echo "---" # Bracket message containing newlines for better visibility in GH's logs. - echo "::error file=$FILE::$FILE is not up to date!%0AYou can probably fix this by running \`pnpm install\` in the appropriate directory." + echo "::error file=$FILE::$FILE is not up to date!%0AYou can probably fix this by running \`pnpm install\`.%0AIf that doesn't do it, try \`pnpm install --resolution-only\`." echo "---" EXIT=1 fi diff --git a/.github/files/generate-ci-matrix.php b/.github/files/generate-ci-matrix.php index ce67ff260078d..fb470f485d935 100755 --- a/.github/files/generate-ci-matrix.php +++ b/.github/files/generate-ci-matrix.php @@ -58,7 +58,7 @@ $matrix = array(); // Add PHP tests. -foreach ( array( '7.0', '7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3' ) as $php ) { +foreach ( array( '7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3' ) as $php ) { $matrix[] = array( 'name' => "PHP tests: PHP $php WP latest", 'script' => 'test-php', @@ -68,6 +68,16 @@ ); } +// TODO: When WordPress 6.5 is no longer supported, this can be removed. +$matrix[] = array( + 'name' => 'PHP tests: PHP 7.0 WP previous', + 'script' => 'test-php', + 'php' => '7.0', + 'wp' => 'previous', + 'timeout' => 20, // 2023-08-17: Successful runs seem to take up to ~12 minutes. + 'force-package-tests' => true, +); + foreach ( array( 'previous', 'trunk' ) as $wp ) { $phpver = $versions['PHP_VERSION']; $matrix[] = array( diff --git a/.github/files/jetpack-staging-sites/k6-shared.js b/.github/files/jetpack-staging-sites/k6-shared.js index fb79f2da89856..91dd714441ed7 100644 --- a/.github/files/jetpack-staging-sites/k6-shared.js +++ b/.github/files/jetpack-staging-sites/k6-shared.js @@ -15,13 +15,13 @@ export const sites = [ blog_id: '215379549', }, { - url: 'https://jetpackedgephp74.wpcomstaging.com', - note: 'php 7.4', + url: 'https://jetpackedgephpold.wpcomstaging.com', + note: 'php old', blog_id: '215379848', }, { - url: 'https://jetpackedgephp82.wpcomstaging.com', - note: 'php 8.2', + url: 'https://jetpackedgephpnew.wpcomstaging.com', + note: 'php new', blog_id: '215380000', }, { diff --git a/.github/files/jetpack-staging-sites/update-jetpack-staging-sites.sh b/.github/files/jetpack-staging-sites/update-jetpack-staging-sites.sh index 15299517da7c1..933044f606550 100755 --- a/.github/files/jetpack-staging-sites/update-jetpack-staging-sites.sh +++ b/.github/files/jetpack-staging-sites/update-jetpack-staging-sites.sh @@ -8,7 +8,7 @@ TMP_DIR=$(mktemp -d) # Temp dir where the plugin .zip files are downloaded and unpacked. REMOTE_DIR='/srv/htdocs/jetpack-staging' # Remote dir where the unpacked plugin files are synced to. -PLUGINS=( "jetpack" "jetpack-mu-wpcom-plugin" "wpcomsh" ); # Plugins to update. +PLUGINS=( "jetpack" "wpcomsh" ); # Plugins to update. declare -A PLUGIN_DOWNLOAD_URLS # Array used to hold fetched plugin download URLs. SITES='{ @@ -18,16 +18,16 @@ SITES='{ "ssh_string": "jetpackedge.wordpress.com@sftp.wp.com", "blog_id": "215379549" }, - "jetpackedgephp74.wpcomstaging.com": { - "url": "https://jetpackedgephp74.wpcomstaging.com/", - "note": "php 7.4", - "ssh_string": "jetpackedgephp74.wordpress.com@sftp.wp.com", + "jetpackedgephpold.wpcomstaging.com": { + "url": "https://jetpackedgephpold.wpcomstaging.com/", + "note": "php old", + "ssh_string": "jetpackedgephpold.wordpress.com@sftp.wp.com", "blog_id": "215379848" }, - "jetpackedgephp82.wpcomstaging.com": { - "url": "https://jetpackedgephp82.wpcomstaging.com/", - "note": "php 8.2", - "ssh_string": "jetpackedgephp82.wordpress.com@sftp.wp.com", + "jetpackedgephpnew.wpcomstaging.com": { + "url": "https://jetpackedgephpnew.wpcomstaging.com/", + "note": "php new", + "ssh_string": "jetpackedgephpnew.wordpress.com@sftp.wp.com", "blog_id": "215380000" }, "jetpackedgeecomm.wpcomstaging.com": { diff --git a/.github/files/lint-project-structure.sh b/.github/files/lint-project-structure.sh index 57ad1922878c2..9efb4928ffb75 100755 --- a/.github/files/lint-project-structure.sh +++ b/.github/files/lint-project-structure.sh @@ -22,6 +22,11 @@ declare -A PROJECT_PREFIXES=( ['js-packages']='JS Package' ) +declare -A PKG_VENDOR_DIR_CACHE=() +while IFS=$'\t' read -r PKG VENDOR; do + PKG_VENDOR_DIR_CACHE["$PKG=dev-trunk"]="$VENDOR" +done < <( jq -r '[ .name, if .type == "jetpack-library" then "jetpack_vendor" else "vendor" end ] | @tsv' "$BASE"/projects/packages/*/composer.json ) + PACKAGES=$(jq -nc 'reduce inputs as $in ({}; .[ $in.name ] |= ( $in.extra["mirror-repo"] | type == "string" ) )' "$BASE"/projects/packages/*/composer.json) JSPACKAGES='{}' for PROJECT in "$BASE"/projects/js-packages/*/.; do @@ -401,6 +406,33 @@ for PROJECT in projects/*/*; do fi fi + # - Plugin non-dev composer dependencies need to be production-included. + if [[ "$TYPE" == "plugins" && -e "$PROJECT/composer.lock" ]]; then + HAS_COMPOSER_PLUGIN=false + if composer -d "$PROJECT" info --locked automattic/jetpack-composer-plugin &>/dev/null; then + HAS_COMPOSER_PLUGIN=true + fi + while IFS=$'\t' read -r PKG VER; do + VENDOR=vendor + if $HAS_COMPOSER_PLUGIN; then + if [[ -z "${PKG_VENDOR_DIR_CACHE["$PKG=$VER"]}" ]]; then + if composer -d "$PROJECT" info --locked --format=json "$PKG" | jq -e '.type == "jetpack-library"' &>/dev/null; then + PKG_VENDOR_DIR_CACHE["$PKG=$VER"]=jetpack_vendor + else + PKG_VENDOR_DIR_CACHE["$PKG=$VER"]=vendor + fi + fi + VENDOR=${PKG_VENDOR_DIR_CACHE["$PKG=$VER"]} + fi + if [[ "$(git check-attr production-include -- "$PROJECT/$VENDOR/$PKG/file")" != *": production-include: set" ]]; then + EXIT=1 + echo "---" + echo "::error file=$PROJECT/.gitattributes::Non-dev composer dependency $PKG is not being production-included. Either make it a dev dependency, or add a line like%0A/$VENDOR/$PKG/** production-include%0Ain \`.gitattributes\`." + echo "---" + fi + done < <( composer -d "$PROJECT" info --locked --no-dev --format=json | jq -r 'if type == "object" then .locked[] | [ .name, .version ] | @tsv else empty end' ) + fi + # - `.extra.dependencies.test-only` must refer to dev dependencies. if jq -e '.extra.dependencies["test-only"] // empty' "$PROJECT/composer.json" >/dev/null; then while IFS=$'\t' read -r LINE DEP; do diff --git a/.github/files/select-wordpress-tag.sh b/.github/files/select-wordpress-tag.sh new file mode 100755 index 0000000000000..920d628f4e2e0 --- /dev/null +++ b/.github/files/select-wordpress-tag.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +## Environment used by this script: +# +# Required: +# - WP_BRANCH: Version of WordPress to check out. +# +# Other: +# - GITHUB_ENV: File written to to set environment variables for later steps. + +set -eo pipefail + +case "$WP_BRANCH" in + trunk) + WORDPRESS_TAG=trunk + ;; + latest) + WORDPRESS_TAG=$(php ./tools/get-wp-version.php) + ;; + previous) + # We hard-code the version here because there's a time near WP releases where + # we've dropped the old 'previous' but WP hasn't actually released the new 'latest' + WORDPRESS_TAG=6.5 + ;; + *) + echo "Unrecognized value for WP_BRANCH: $WP_BRANCH" >&2 + exit 1 + ;; +esac + +if [[ -n "$GITHUB_ENV" ]]; then + echo "WORDPRESS_TAG=$WORDPRESS_TAG" >> "$GITHUB_ENV" +fi diff --git a/.github/files/setup-wordpress-env.sh b/.github/files/setup-wordpress-env.sh index 1b233f1095790..6f187a8bfa46c 100755 --- a/.github/files/setup-wordpress-env.sh +++ b/.github/files/setup-wordpress-env.sh @@ -30,27 +30,11 @@ mysql -e "CREATE DATABASE wordpress_tests;" echo "::endgroup::" echo "::group::Preparing WordPress from \"$WP_BRANCH\" branch"; -case "$WP_BRANCH" in - trunk) - TAG=trunk - ;; - latest) - TAG=$(php ./tools/get-wp-version.php) - ;; - previous) - # We hard-code the version here because there's a time near WP releases where - # we've dropped the old 'previous' but WP hasn't actually released the new 'latest' - TAG=6.4 - ;; - *) - echo "Unrecognized value for WP_BRANCH: $WP_BRANCH" >&2 - exit 1 - ;; -esac -git clone --depth=1 --branch "$TAG" git://develop.git.wordpress.org/ "/tmp/wordpress-$WP_BRANCH" +source .github/files/select-wordpress-tag.sh +git clone --depth=1 --branch "$WORDPRESS_TAG" git://develop.git.wordpress.org/ "/tmp/wordpress-$WP_BRANCH" # We need a built version of WordPress to test against, so download that into the src directory instead of what's in wordpress-develop. rm -rf "/tmp/wordpress-$WP_BRANCH/src" -git clone --depth=1 --branch "$TAG" git://core.git.wordpress.org/ "/tmp/wordpress-$WP_BRANCH/src" +git clone --depth=1 --branch "$WORDPRESS_TAG" git://core.git.wordpress.org/ "/tmp/wordpress-$WP_BRANCH/src" echo "::endgroup::" if [[ -n "$GITHUB_ENV" ]]; then @@ -123,6 +107,19 @@ for PLUGIN in projects/plugins/*/composer.json; do fi cd "$BASE" + # Upgrade/downgrade WorDBless if necessary. + if [[ ( "$WP_BRANCH" == 'trunk' || "$WP_BRANCH" == 'previous' ) && "$TEST_SCRIPT" == "test-php" ]]; then + VER=$(composer --format=json --working-dir="$DIR" show | jq -r '.installed[] | select( .name == "roots/wordpress" ) | .version') + if [[ -n "$VER" ]]; then + INSVER=$WORDPRESS_TAG + [[ "$WORDPRESS_TAG" == 'trunk' ]] && INSVER="dev-main as $VER" + echo "Supposed to run tests against WordPress $WORDPRESS_TAG, so setting roots/wordpress and roots/wordpress-no-content to \"$INSVER\"" + # Composer seems to sometimes have issues with deleting the wordpress dir on its own, so do it manually first. + rm -rf "$DIR/wordpress" + composer --working-dir="$DIR" require --dev roots/wordpress="$INSVER" roots/wordpress-no-content="$INSVER" + fi + fi + cp -r "$DIR" "/tmp/wordpress-$WP_BRANCH/src/wp-content/plugins/$NAME" # Plugin dir for tests in WP >= 5.6-beta1 ln -s "/tmp/wordpress-$WP_BRANCH/src/wp-content/plugins/$NAME" "/tmp/wordpress-$WP_BRANCH/tests/phpunit/data/plugins/$NAME" diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 73d15df217a05..1af344c2f1573 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -42,13 +42,6 @@ rangeStrategy: 'widen', }, - // Forbid `@wordpress/dependency-extraction-webpack-plugin` v6 until WP 6.5 support is dropped. - // https://github.com/WordPress/gutenberg/issues/62202 - { - matchPackageNames: [ '@wordpress/dependency-extraction-webpack-plugin' ], - allowedVersions: '<6.0.0', - }, - // Various other monorepos and package groupings. { extends: [ 'monorepo:wordpress' ], @@ -104,8 +97,8 @@ 'uuid', '@testing-library/preact', ], - reviewers: [ 'team:jetpack-search' ], - addLabels: [ 'Search', 'Instant Search' ], + reviewers: [ 'team:red' ], + addLabels: [ '[Feature] Search', 'Instant Search' ], }, ], diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 78082fc0f54dc..39e20a92f7aee 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -78,6 +78,14 @@ jobs: pnpm install echo "::endgroup::" + # If we're going to be making WorDBless use WP "nightlies", remove the relevant package from Composer's cache to get the latest version. + if [[ "$WP_BRANCH" == 'trunk' && "$TEST_SCRIPT" == "test-php" ]]; then + echo "::group::Clear composer cache for roots/wordpress" + DIR=$(composer config cache-files-dir) + rm -rf "$DIR/roots/wordpress" "$DIR/roots/wordpress-no-content" + echo "::endgroup::" + fi + - name: Detect changed projects id: changed run: | @@ -93,6 +101,10 @@ jobs: echo "projects=${CHANGED}" >> "$GITHUB_OUTPUT" echo "any-plugins=${ANY_PLUGINS}" >> "$GITHUB_OUTPUT" + - name: Select WordPress version + if: steps.changed.outputs.any-plugins != 'true' && matrix.wp != 'none' + run: .github/files/select-wordpress-tag.sh + - name: Setup WordPress environment for plugin tests env: API_TOKEN_GITHUB: ${{ secrets.GITHUB_TOKEN }} @@ -105,12 +117,6 @@ jobs: FORCE_PACKAGE_TESTS: ${{ matrix.force-package-tests && 'true' || 'false' }} CHANGED: ${{ steps.changed.outputs.projects }} run: | - # If we're going to be making WorDBless use WP "nightlies", remove the relevant package from Composer's cache to get the latest version. - if [[ "$WP_BRANCH" == 'trunk' && ( "$TEST_SCRIPT" == "test-php" ) ]]; then - DIR=$(composer config cache-files-dir) - rm -rf "$DIR/roots/wordpress" - fi - EXIT=0 mkdir artifacts for P in composer.json projects/*/*/composer.json; do @@ -165,15 +171,17 @@ jobs: echo 'Platform reqs failed, running `composer update`' composer --working-dir="$DIR" update fi - fi - if [[ "$WP_BRANCH" == 'trunk' && "$TEST_SCRIPT" == "test-php" ]]; then - VER=$(composer --format=json --working-dir="$DIR" show | jq -r '.installed[] | select( .name == "roots/wordpress" ) | .version') - if [[ -n "$VER" ]]; then - echo 'Supposed to run tests against WordPress trunk, so upgrading roots/wordpress and roots/wordpress-no-content to dev-main' - # Composer seems to sometimes have issues with deleting the wordpress dir on its own, so do it manually first. - rm -rf "$DIR/wordpress" - composer --working-dir="$DIR" require --dev roots/wordpress="dev-main as $VER" roots/wordpress-no-content="dev-main as $VER" + if [[ "$WP_BRANCH" == 'trunk' || "$WP_BRANCH" == 'previous' ]]; then + VER=$(composer --format=json --working-dir="$DIR" show | jq -r '.installed[] | select( .name == "roots/wordpress" ) | .version') + if [[ -n "$VER" ]]; then + INSVER=$WORDPRESS_TAG + [[ "$WORDPRESS_TAG" == 'trunk' ]] && INSVER="dev-main as $VER" + echo "Supposed to run tests against WordPress $WORDPRESS_TAG, so setting roots/wordpress and roots/wordpress-no-content to \"$INSVER\"" + # Composer seems to sometimes have issues with deleting the wordpress dir on its own, so do it manually first. + rm -rf "$DIR/wordpress" + composer --working-dir="$DIR" require --dev roots/wordpress="$INSVER" roots/wordpress-no-content="$INSVER" + fi fi fi diff --git a/.phan/config.base.php b/.phan/config.base.php index 636d1f437bbaf..97c272c219115 100644 --- a/.phan/config.base.php +++ b/.phan/config.base.php @@ -63,6 +63,7 @@ function make_phan_config( $dir, $options = array() ) { $extra_stubs = array(); $global_stubs = array(); $internal_stubs = array(); + foreach ( array_merge( $options['stubs'], $options['+stubs'] ) as $stub ) { switch ( $stub ) { case 'akismet': @@ -104,6 +105,7 @@ function make_phan_config( $dir, $options = array() ) { $stubs[] = "$root/.phan/stubs/wpcom-stubs.php"; if ( $dir !== "$root/projects/plugins/wpcomsh" ) { $extra_stubs[] = "$root/projects/plugins/wpcomsh/feature-plugins/nav-redesign.php"; + $extra_stubs[] = "$root/projects/plugins/wpcomsh/feature-plugins/masterbar.php"; $extra_stubs[] = "$root/projects/plugins/wpcomsh/footer-credit/footer-credit/customizer.php"; $extra_stubs[] = "$root/projects/plugins/wpcomsh/footer-credit/theme-optimizations.php"; $extra_stubs[] = "$root/projects/plugins/wpcomsh/lib/require-lib.php"; diff --git a/.phan/stubs/akismet-stubs.php b/.phan/stubs/akismet-stubs.php index 7aaae2d82b728..eff107a09a85b 100644 --- a/.phan/stubs/akismet-stubs.php +++ b/.phan/stubs/akismet-stubs.php @@ -1,6 +1,6 @@ - + diff --git a/.pnpmfile.cjs b/.pnpmfile.cjs index 05d761988e87d..56644644bfeeb 100644 --- a/.pnpmfile.cjs +++ b/.pnpmfile.cjs @@ -190,19 +190,6 @@ function afterAllResolved( lockfile ) { return lockfile; } - // eslint-disable-next-line no-unused-vars -- Don't care. - for ( const [ k, v ] of Object.entries( lockfile.packages ) ) { - // Forbid `@wordpress/dependency-extraction-webpack-plugin` v6 until WP 6.5 support is dropped. - // https://github.com/WordPress/gutenberg/issues/62202 - if ( - k.startsWith( '@wordpress/dependency-extraction-webpack-plugin@' ) && - ! k.startsWith( '@wordpress/dependency-extraction-webpack-plugin@5.' ) - ) { - throw new Error( - '@wordpress/dependency-extraction-webpack-plugin >= 6.0.0 is not allowed until we drop WordPress 6.5 support.\nSee https://github.com/WordPress/gutenberg/issues/62202 for details.' - ); - } - } return lockfile; } diff --git a/composer.json b/composer.json index 53a26f45bc8e9..ba10cc928cda0 100644 --- a/composer.json +++ b/composer.json @@ -15,8 +15,8 @@ "phan/phan": "^5.4", "php-parallel-lint/php-parallel-lint": "^1.4.0", "php-stubs/woocommerce-stubs": ">=8.7", - "php-stubs/wordpress-stubs": ">=6.4", - "php-stubs/wordpress-tests-stubs": ">=6.4", + "php-stubs/wordpress-stubs": ">=6.5", + "php-stubs/wordpress-tests-stubs": ">=6.5", "php-stubs/wp-cli-stubs": "^2.10", "sirbrillig/phpcs-changed": "2.11.4", "squizlabs/php_codesniffer": "^3.6.2" diff --git a/composer.lock b/composer.lock index 98d71f91ea468..6f85e2bf700df 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "34f2d8ac9e65725df2a124991d9ac059", + "content-hash": "700fde8d8373ed5610bfc08d87e10fc2", "packages": [], "packages-dev": [ { @@ -1039,16 +1039,16 @@ }, { "name": "php-stubs/woocommerce-stubs", - "version": "v8.9.1", + "version": "v9.0.0", "source": { "type": "git", "url": "https://github.com/php-stubs/woocommerce-stubs.git", - "reference": "2f5be3f363ff3d02b4e29404c7895d9d15fd3e73" + "reference": "ccfc447c39a48886146fd3cef66d0e6f2db7fa2b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-stubs/woocommerce-stubs/zipball/2f5be3f363ff3d02b4e29404c7895d9d15fd3e73", - "reference": "2f5be3f363ff3d02b4e29404c7895d9d15fd3e73", + "url": "https://api.github.com/repos/php-stubs/woocommerce-stubs/zipball/ccfc447c39a48886146fd3cef66d0e6f2db7fa2b", + "reference": "ccfc447c39a48886146fd3cef66d0e6f2db7fa2b", "shasum": "" }, "require": { @@ -1077,9 +1077,9 @@ ], "support": { "issues": "https://github.com/php-stubs/woocommerce-stubs/issues", - "source": "https://github.com/php-stubs/woocommerce-stubs/tree/v8.9.1" + "source": "https://github.com/php-stubs/woocommerce-stubs/tree/v9.0.0" }, - "time": "2024-05-21T11:47:54+00:00" + "time": "2024-06-18T15:46:13+00:00" }, { "name": "php-stubs/wordpress-stubs", @@ -2014,16 +2014,16 @@ }, { "name": "sirbrillig/phpcs-variable-analysis", - "version": "v2.11.18", + "version": "v2.11.19", "source": { "type": "git", "url": "https://github.com/sirbrillig/phpcs-variable-analysis.git", - "reference": "ca242a0b7309e0f9d1f73b236e04ecf4ca3248d0" + "reference": "bc8d7e30e2005bce5c59018b7cdb08e9fb45c0d1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sirbrillig/phpcs-variable-analysis/zipball/ca242a0b7309e0f9d1f73b236e04ecf4ca3248d0", - "reference": "ca242a0b7309e0f9d1f73b236e04ecf4ca3248d0", + "url": "https://api.github.com/repos/sirbrillig/phpcs-variable-analysis/zipball/bc8d7e30e2005bce5c59018b7cdb08e9fb45c0d1", + "reference": "bc8d7e30e2005bce5c59018b7cdb08e9fb45c0d1", "shasum": "" }, "require": { @@ -2068,7 +2068,7 @@ "source": "https://github.com/sirbrillig/phpcs-variable-analysis", "wiki": "https://github.com/sirbrillig/phpcs-variable-analysis/wiki" }, - "time": "2024-04-13T16:42:46+00:00" + "time": "2024-06-26T20:08:34+00:00" }, { "name": "squizlabs/php_codesniffer", @@ -2165,16 +2165,16 @@ }, { "name": "symfony/console", - "version": "v7.1.1", + "version": "v7.1.2", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "9b008f2d7b21c74ef4d0c3de6077a642bc55ece3" + "reference": "0aa29ca177f432ab68533432db0de059f39c92ae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/9b008f2d7b21c74ef4d0c3de6077a642bc55ece3", - "reference": "9b008f2d7b21c74ef4d0c3de6077a642bc55ece3", + "url": "https://api.github.com/repos/symfony/console/zipball/0aa29ca177f432ab68533432db0de059f39c92ae", + "reference": "0aa29ca177f432ab68533432db0de059f39c92ae", "shasum": "" }, "require": { @@ -2238,7 +2238,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.1.1" + "source": "https://github.com/symfony/console/tree/v7.1.2" }, "funding": [ { @@ -2254,7 +2254,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:57:53+00:00" + "time": "2024-06-28T10:03:55+00:00" }, { "name": "symfony/deprecation-contracts", @@ -2325,16 +2325,16 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.29.0", + "version": "v1.30.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4" + "reference": "0424dff1c58f028c451efff2045f5d92410bd540" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4", - "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/0424dff1c58f028c451efff2045f5d92410bd540", + "reference": "0424dff1c58f028c451efff2045f5d92410bd540", "shasum": "" }, "require": { @@ -2384,7 +2384,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.30.0" }, "funding": [ { @@ -2400,20 +2400,20 @@ "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-05-31T15:07:36+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.29.0", + "version": "v1.30.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f" + "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/32a9da87d7b3245e09ac426c83d334ae9f06f80f", - "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/64647a7c30b2283f5d49b874d84a18fc22054b7a", + "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a", "shasum": "" }, "require": { @@ -2462,7 +2462,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.30.0" }, "funding": [ { @@ -2478,20 +2478,20 @@ "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-05-31T15:07:36+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.29.0", + "version": "v1.30.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "bc45c394692b948b4d383a08d7753968bed9a83d" + "reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/bc45c394692b948b4d383a08d7753968bed9a83d", - "reference": "bc45c394692b948b4d383a08d7753968bed9a83d", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/a95281b0be0d9ab48050ebd988b967875cdb9fdb", + "reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb", "shasum": "" }, "require": { @@ -2543,7 +2543,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.30.0" }, "funding": [ { @@ -2559,20 +2559,20 @@ "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-05-31T15:07:36+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.29.0", + "version": "v1.30.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec" + "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec", - "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c", + "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c", "shasum": "" }, "require": { @@ -2623,7 +2623,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.30.0" }, "funding": [ { @@ -2639,20 +2639,20 @@ "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-06-19T12:30:46+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.29.0", + "version": "v1.30.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b" + "reference": "77fa7995ac1b21ab60769b7323d600a991a90433" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/87b68208d5c1188808dd7839ee1e6c8ec3b02f1b", - "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/77fa7995ac1b21ab60769b7323d600a991a90433", + "reference": "77fa7995ac1b21ab60769b7323d600a991a90433", "shasum": "" }, "require": { @@ -2703,7 +2703,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.30.0" }, "funding": [ { @@ -2719,7 +2719,7 @@ "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-05-31T15:07:36+00:00" }, { "name": "symfony/service-contracts", @@ -2806,16 +2806,16 @@ }, { "name": "symfony/string", - "version": "v7.1.1", + "version": "v7.1.2", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "60bc311c74e0af215101235aa6f471bcbc032df2" + "reference": "14221089ac66cf82e3cf3d1c1da65de305587ff8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/60bc311c74e0af215101235aa6f471bcbc032df2", - "reference": "60bc311c74e0af215101235aa6f471bcbc032df2", + "url": "https://api.github.com/repos/symfony/string/zipball/14221089ac66cf82e3cf3d1c1da65de305587ff8", + "reference": "14221089ac66cf82e3cf3d1c1da65de305587ff8", "shasum": "" }, "require": { @@ -2873,7 +2873,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.1.1" + "source": "https://github.com/symfony/string/tree/v7.1.2" }, "funding": [ { @@ -2889,7 +2889,7 @@ "type": "tidelift" } ], - "time": "2024-06-04T06:40:14+00:00" + "time": "2024-06-28T09:27:18+00:00" }, { "name": "tysonandre/var_representation_polyfill", diff --git a/docs/monorepo.md b/docs/monorepo.md index a6a634c8a2b0d..f2bd53c0b6f1f 100644 --- a/docs/monorepo.md +++ b/docs/monorepo.md @@ -125,8 +125,8 @@ We use `composer.json` to hold metadata about projects. Much of our generic tool * `.extra.autotagger`: Set truthy to enable automatic release-version tagging in the mirror repo. See [Mirror repositories > Autotagger](#autotagger) for details. * `.extra.changelogger`: Configuration object for [Changelogger](#jetpack-changelogger). See [its documentation](https://github.com/Automattic/jetpack-changelogger#configuration) for details. * `.extra.changelogger-default-type`: Certain of our tools automatically create Changelogger change entries. This is the value to use for `--type` when doing so. Default type is `changed`. -* `.extra.dependencies.build`: This optional array specifies the "slugs" of any within-monorepo build dependencies that can't otherwise be inferred. The "slug" consists of the two levels of directory under `projects/`, e.g. `plugins/jetpack` or `packages/lazy-images`. -* `.extra.dependencies.test`: This optional array specifies the "slugs" of any within-monorepo testing dependencies that can't otherwise be inferred. The "slug" consists of the two levels of directory under `projects/`, e.g. `plugins/jetpack` or `packages/lazy-images`. See [Testing](#testing) for details. +* `.extra.dependencies.build`: This optional array specifies the "slugs" of any within-monorepo build dependencies that can't otherwise be inferred. The "slug" consists of the two levels of directory under `projects/`, e.g. `plugins/jetpack` or `packages/changelogger`. +* `.extra.dependencies.test`: This optional array specifies the "slugs" of any within-monorepo testing dependencies that can't otherwise be inferred. The "slug" consists of the two levels of directory under `projects/`, e.g. `plugins/jetpack` or `packages/changelogger`. See [Testing](#testing) for details. * `.extra.dependencies.test-only`: This optional array specifies the "slugs" of any within-monorepo dependencies that are only used for testing and/or static analysis and should be ignored otherwise when analyzing intra-monorepo dependencies. * `.extra.dev-releases`: Indicate that the plugin will have developer alpha releases. Instead of the mirror repositories showing "VER-alpha", they'll start at "VER-a.0" and you can use the `-a` flag to the release tooling to release "VER-a.1". * `.extra.mirror-repo`: This specifies the name of the GitHub mirror repo, i.e. the "Automattic/jetpack-_something_" in "https://github.com/Automattic/jetpack-_something_". diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 98d3cfe30dcd7..3898d44d77748 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,7 +4,7 @@ settings: autoInstallPeers: false excludeLinksFromLockfile: false -pnpmfileChecksum: bjkzupj7mgxhpr4jv54264ts6e +pnpmfileChecksum: hogs2xjgnkyiea7znn3abfx4o4 importers: @@ -115,9 +115,15 @@ importers: '@types/react': specifier: 18.3.1 version: 18.3.1 + '@types/wordpress__block-editor': + specifier: 11.5.14 + version: 11.5.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@wordpress/api-fetch': specifier: 7.2.0 version: 7.2.0 + '@wordpress/blob': + specifier: 4.2.0 + version: 4.2.0 '@wordpress/block-editor': specifier: 13.2.0 version: 13.2.0(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -709,20 +715,20 @@ importers: specifier: 4.0.2 version: 4.0.2(postcss@8.4.31) rollup-plugin-svelte: - specifier: 7.1.0 - version: 7.1.0(rollup@2.79.1)(svelte@3.58.0) + specifier: 7.2.2 + version: 7.2.2(rollup@2.79.1)(svelte@4.2.18) rollup-plugin-svelte-svg: specifier: 1.0.0-beta.6 - version: 1.0.0-beta.6(svelte@3.58.0) + version: 1.0.0-beta.6(svelte@4.2.18) sass: specifier: 1.64.1 version: 1.64.1 svelte: - specifier: 3.58.0 - version: 3.58.0 + specifier: 4.2.18 + version: 4.2.18 svelte-preprocess: - specifier: 5.0.4 - version: 5.0.4(@babel/core@7.24.7)(postcss@8.4.31)(sass@1.64.1)(svelte@3.58.0)(typescript@5.0.4) + specifier: 6.0.2 + version: 6.0.2(@babel/core@7.24.7)(postcss@8.4.31)(sass@1.64.1)(svelte@4.2.18)(typescript@5.0.4) tslib: specifier: 2.5.0 version: 2.5.0 @@ -1296,8 +1302,8 @@ importers: specifier: 7.24.7 version: 7.24.7 '@playwright/test': - specifier: 1.44.1 - version: 1.44.1 + specifier: 1.45.1 + version: 1.45.1 '@storybook/addon-a11y': specifier: 8.1.6 version: 8.1.6 @@ -1443,8 +1449,8 @@ importers: specifier: 29.7.0 version: 29.7.0 svelte: - specifier: 3.58.0 - version: 3.58.0 + specifier: 4.2.18 + version: 4.2.18 tslib: specifier: 2.5.0 version: 2.5.0 @@ -1579,6 +1585,10 @@ importers: projects/packages/admin-ui: {} projects/packages/assets: + dependencies: + react: + specifier: 18.3.1 + version: 18.3.1 devDependencies: '@automattic/jetpack-webpack-config': specifier: workspace:* @@ -1924,13 +1934,13 @@ importers: version: 0.6.0 '@types/node': specifier: ^20.4.2 - version: 20.14.2 + version: 20.14.10 '@types/qs': specifier: 6.9.15 version: 6.9.15 jest: specifier: 29.7.0 - version: 29.7.0(@types/node@20.14.2) + version: 29.7.0(@types/node@20.14.10) typescript: specifier: 5.0.4 version: 5.0.4 @@ -2102,21 +2112,36 @@ importers: projects/packages/jetpack-mu-wpcom: dependencies: + '@automattic/i18n-utils': + specifier: 1.2.3 + version: 1.2.3 + '@automattic/jetpack-base-styles': + specifier: workspace:* + version: link:../../js-packages/base-styles + '@automattic/jetpack-shared-extension-utils': + specifier: workspace:* + version: link:../../js-packages/shared-extension-utils '@automattic/typography': specifier: 1.0.0 version: 1.0.0 '@preact/signals': specifier: ^1.2.2 - version: 1.2.3(preact@10.22.0) + version: 1.3.0(preact@10.22.1) '@sentry/browser': specifier: 7.80.1 version: 7.80.1 + '@tanstack/react-query': + specifier: ^5.15.5 + version: 5.20.5(react@18.3.1) '@wordpress/api-fetch': specifier: 7.2.0 version: 7.2.0 '@wordpress/base-styles': specifier: 5.2.0 version: 5.2.0 + '@wordpress/blocks': + specifier: 13.2.0 + version: 13.2.0(react@18.3.1) '@wordpress/components': specifier: 28.2.0 version: 28.2.0(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -2126,12 +2151,18 @@ importers: '@wordpress/dom-ready': specifier: ^4.0.0 version: 4.2.0 + '@wordpress/element': + specifier: 6.2.0 + version: 6.2.0 '@wordpress/hooks': specifier: 4.2.0 version: 4.2.0 '@wordpress/i18n': specifier: 5.2.0 version: 5.2.0 + '@wordpress/icons': + specifier: 10.2.0 + version: 10.2.0(react@18.3.1) '@wordpress/plugins': specifier: 7.2.0 version: 7.2.0(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -2143,7 +2174,7 @@ importers: version: 2.1.1 preact: specifier: ^10.13.1 - version: 10.22.0 + version: 10.22.1 wpcom-proxy-request: specifier: ^7.0.3 version: 7.0.5 @@ -2168,11 +2199,11 @@ importers: specifier: 7.24.7 version: 7.24.7(@babel/core@7.24.7) '@playwright/test': - specifier: 1.44.1 - version: 1.44.1 + specifier: 1.45.1 + version: 1.45.1 '@types/node': specifier: ^20.4.2 - version: 20.14.2 + version: 20.14.10 '@types/react': specifier: ^18.2.28 version: 18.3.1 @@ -2321,6 +2352,9 @@ importers: '@automattic/jetpack-connection': specifier: workspace:* version: link:../../js-packages/connection + '@automattic/jetpack-explat': + specifier: workspace:* + version: link:../explat '@automattic/jetpack-licensing': specifier: workspace:* version: link:../../js-packages/licensing @@ -2546,8 +2580,8 @@ importers: specifier: 4.0.0 version: 4.0.0 preact: - specifier: 10.12.1 - version: 10.12.1 + specifier: 10.22.1 + version: 10.22.1 prop-types: specifier: 15.7.2 version: 15.7.2 @@ -2610,8 +2644,8 @@ importers: specifier: 10.1.0 version: 10.1.0 '@testing-library/preact': - specifier: 3.2.3 - version: 3.2.3(preact@10.12.1) + specifier: 3.2.4 + version: 3.2.4(preact@10.22.1) '@testing-library/react': specifier: 15.0.7 version: 15.0.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -2909,8 +2943,8 @@ importers: specifier: 4.0.0 version: 4.0.0 preact: - specifier: 10.12.1 - version: 10.12.1 + specifier: 10.22.1 + version: 10.22.1 prop-types: specifier: 15.7.2 version: 15.7.2 @@ -2967,8 +3001,8 @@ importers: specifier: 10.1.0 version: 10.1.0 '@testing-library/preact': - specifier: 3.2.3 - version: 3.2.3(preact@10.12.1) + specifier: 3.2.4 + version: 3.2.4(preact@10.22.1) '@testing-library/react': specifier: 15.0.7 version: 15.0.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -3166,8 +3200,8 @@ importers: projects/plugins/automattic-for-agencies-client/tests/e2e: devDependencies: '@playwright/test': - specifier: 1.44.1 - version: 1.44.1 + specifier: 1.45.1 + version: 1.45.1 allure-playwright: specifier: 2.9.2 version: 2.9.2 @@ -3317,8 +3351,8 @@ importers: projects/plugins/boost/tests/e2e: devDependencies: '@playwright/test': - specifier: 1.44.1 - version: 1.44.1 + specifier: 1.45.1 + version: 1.45.1 allure-playwright: specifier: 2.9.2 version: 2.9.2 @@ -3408,8 +3442,8 @@ importers: projects/plugins/classic-theme-helper-plugin/tests/e2e: devDependencies: '@playwright/test': - specifier: 1.44.1 - version: 1.44.1 + specifier: 1.45.1 + version: 1.45.1 allure-playwright: specifier: 2.9.2 version: 2.9.2 @@ -3575,17 +3609,17 @@ importers: specifier: 4.0.2 version: 4.0.2(postcss@8.4.31) rollup-plugin-svelte: - specifier: 7.1.0 - version: 7.1.0(rollup@2.79.1)(svelte@3.58.0) + specifier: 7.2.2 + version: 7.2.2(rollup@2.79.1)(svelte@4.2.18) sass: specifier: 1.64.1 version: 1.64.1 svelte: - specifier: 3.58.0 - version: 3.58.0 + specifier: 4.2.18 + version: 4.2.18 svelte-preprocess: - specifier: 5.0.4 - version: 5.0.4(@babel/core@7.24.7)(postcss@8.4.31)(sass@1.64.1)(svelte@3.58.0)(typescript@5.0.4) + specifier: 6.0.2 + version: 6.0.2(@babel/core@7.24.7)(postcss@8.4.31)(sass@1.64.1)(svelte@4.2.18)(typescript@5.0.4) typescript: specifier: 5.0.4 version: 5.0.4 @@ -3688,6 +3722,9 @@ importers: '@wordpress/primitives': specifier: 4.2.0 version: 4.2.0(react@18.3.1) + '@wordpress/rich-text': + specifier: 7.2.0 + version: 7.2.0(react@18.3.1) '@wordpress/url': specifier: 4.2.0 version: 4.2.0 @@ -3718,6 +3755,9 @@ importers: copy-webpack-plugin: specifier: 11.0.0 version: 11.0.0(webpack@5.76.0(webpack-cli@4.9.1)) + crypto-js: + specifier: 4.2.0 + version: 4.2.0 debug: specifier: 4.3.4 version: 4.3.4 @@ -3946,8 +3986,8 @@ importers: projects/plugins/jetpack/tests/e2e: devDependencies: '@playwright/test': - specifier: 1.44.1 - version: 1.44.1 + specifier: 1.45.1 + version: 1.45.1 allure-playwright: specifier: 2.9.2 version: 2.9.2 @@ -4037,8 +4077,8 @@ importers: projects/plugins/migration/tests/e2e: devDependencies: '@playwright/test': - specifier: 1.44.1 - version: 1.44.1 + specifier: 1.45.1 + version: 1.45.1 allure-playwright: specifier: 2.9.2 version: 2.9.2 @@ -4070,7 +4110,7 @@ importers: version: 7.2.0 '@wordpress/components': specifier: 28.2.0 - version: 28.2.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 28.2.0(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@wordpress/data': specifier: 10.2.0 version: 10.2.0(react@18.3.1) @@ -4126,6 +4166,9 @@ importers: '@babel/runtime': specifier: 7.24.7 version: 7.24.7 + '@types/react': + specifier: 18.3.1 + version: 18.3.1 '@wordpress/browserslist-config': specifier: 6.2.0 version: 6.2.0 @@ -4138,6 +4181,9 @@ importers: sass-loader: specifier: 12.4.0 version: 12.4.0(sass@1.64.1)(webpack@5.76.0(webpack-cli@4.9.1)) + typescript: + specifier: 5.0.4 + version: 5.0.4 webpack: specifier: 5.76.0 version: 5.76.0(webpack-cli@4.9.1) @@ -4150,8 +4196,8 @@ importers: projects/plugins/search/tests/e2e: devDependencies: '@playwright/test': - specifier: 1.44.1 - version: 1.44.1 + specifier: 1.45.1 + version: 1.45.1 allure-playwright: specifier: 2.9.2 version: 2.9.2 @@ -4286,8 +4332,8 @@ importers: projects/plugins/social/tests/e2e: devDependencies: '@playwright/test': - specifier: 1.44.1 - version: 1.44.1 + specifier: 1.45.1 + version: 1.45.1 allure-playwright: specifier: 2.9.2 version: 2.9.2 @@ -4377,8 +4423,8 @@ importers: projects/plugins/starter-plugin/tests/e2e: devDependencies: '@playwright/test': - specifier: 1.44.1 - version: 1.44.1 + specifier: 1.45.1 + version: 1.45.1 allure-playwright: specifier: 2.9.2 version: 2.9.2 @@ -4482,8 +4528,8 @@ importers: projects/plugins/videopress/tests/e2e: devDependencies: '@playwright/test': - specifier: 1.44.1 - version: 1.44.1 + specifier: 1.45.1 + version: 1.45.1 allure-playwright: specifier: 2.9.2 version: 2.9.2 @@ -4603,8 +4649,8 @@ importers: tools/e2e-commons: devDependencies: '@playwright/test': - specifier: 1.44.1 - version: 1.44.1 + specifier: 1.45.1 + version: 1.45.1 '@slack/web-api': specifier: 6.10.0 version: 6.10.0 @@ -4710,9 +4756,6 @@ importers: eslint-plugin-import: specifier: 2.29.1 version: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.0.4))(eslint@8.57.0) - eslint-plugin-inclusive-language: - specifier: 2.2.1 - version: 2.2.1 eslint-plugin-jest: specifier: 27.9.0 version: 27.9.0(@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.0.4))(eslint@8.57.0)(typescript@5.0.4))(eslint@8.57.0)(jest@29.7.0)(typescript@5.0.4) @@ -4742,13 +4785,10 @@ importers: version: 4.6.2(eslint@8.57.0) eslint-plugin-svelte: specifier: 2.41.0 - version: 2.41.0(eslint@8.57.0)(svelte@3.58.0) + version: 2.41.0(eslint@8.57.0)(svelte@4.2.18) eslint-plugin-testing-library: specifier: 6.2.2 version: 6.2.2(eslint@8.57.0)(typescript@5.0.4) - eslint-plugin-wpcalypso: - specifier: 8.0.0 - version: 8.0.0(@babel/core@7.24.7)(eslint-plugin-inclusive-language@2.2.1)(eslint-plugin-jsdoc@46.10.1(eslint@8.57.0))(eslint-plugin-react-hooks@4.6.2(eslint@8.57.0))(eslint-plugin-react@7.34.2(eslint@8.57.0))(eslint@8.57.0) glob: specifier: 10.4.1 version: 10.4.1 @@ -4772,7 +4812,7 @@ importers: version: wp-prettier@3.0.3 prettier-plugin-svelte: specifier: 3.0.3 - version: 3.0.3(svelte@3.58.0)(wp-prettier@3.0.3) + version: 3.0.3(svelte@4.2.18)(wp-prettier@3.0.3) semver: specifier: 7.5.2 version: 7.5.2 @@ -4780,11 +4820,11 @@ importers: specifier: 1.50.0 version: 1.50.0 svelte: - specifier: 3.58.0 - version: 3.58.0 + specifier: 4.2.18 + version: 4.2.18 svelte-eslint-parser: specifier: 0.39.2 - version: 0.39.2(svelte@3.58.0) + version: 0.39.2(svelte@4.2.18) typescript: specifier: 5.0.4 version: 5.0.4 @@ -4831,6 +4871,12 @@ packages: '@automattic/calypso-color-schemes@3.1.3': resolution: {integrity: sha512-nzs36yfxUOcsD3HvB72IHgdUfIzTRnT7QmF78CBXEREawTEs0uDyELdx/rAOtW/PauxRYRGQ4zeK5c67FWqLxw==} + '@automattic/calypso-config@1.2.0': + resolution: {integrity: sha512-7NE5oVOEyQ4KRz1VNnPIHgW+mcwxnkcs/+Cymba7OA7SYKARiTg3ETGlZGX19S0F7gjYZMq+IeLHeAZSrNjz/Q==} + + '@automattic/calypso-url@1.1.0': + resolution: {integrity: sha512-oA6pzfrp538gq5JEjE0ARDjvR8Efhw+jrK15TJPjAq5Q+vhPSJhH8sYKEsMAoYZV3d5nnyUcmI5Evge+yq4zeg==} + '@automattic/color-studio@2.6.0': resolution: {integrity: sha512-2LzB6bbQw1vayZxZy5Y+DnCYU7x8tPu+rZhNkWD7V8QZTSJMJO65XKZhYaCByC+C5OegXyGyZzcqEOHHdj5iiQ==} @@ -4843,6 +4889,12 @@ packages: '@automattic/format-currency@1.0.1': resolution: {integrity: sha512-RY2eiUlDiqNSHiJzz2YmH/mw4IjAUO5hkxbwcVGHJkBZowdq/WcSG2yhXc8N9cV9N1fTO/ryCuJvGnpHUe+mAg==} + '@automattic/i18n-utils@1.2.3': + resolution: {integrity: sha512-zvZlazUoEasLATrta3ljfxu2uaZWgHRNKWf56KKBlrPiIxNQvx9D7YyN2MhiV27e/PuAhB0gI4ghqp3gzurKmA==} + + '@automattic/languages@1.0.0': + resolution: {integrity: sha512-froTyDbTmLitHkvY9WLCpFdjUo6moOLkDKw63J2fLiB2gBApy2thkBV+LRx4Z0kIF5iXVkQF4yYOPYkT9Sr13Q==} + '@automattic/popup-monitor@1.0.2': resolution: {integrity: sha512-Y4LMfdkV8iDmezu/7Ov/18JaFJ0QAy5vCntiP0S5AhLt4R/kjLtBt4ifNXNbdKTthGxlL17+LJ1bNtHBVCzPwg==} @@ -5922,8 +5974,8 @@ packages: peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - '@eslint-community/regexpp@4.10.1': - resolution: {integrity: sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA==} + '@eslint-community/regexpp@4.11.0': + resolution: {integrity: sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} '@eslint/eslintrc@2.1.4': @@ -5941,20 +5993,20 @@ packages: resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==} engines: {node: '>=14'} - '@floating-ui/core@1.6.2': - resolution: {integrity: sha512-+2XpQV9LLZeanU4ZevzRnGFg2neDeKHgFLjP6YLW+tly0IvrhqT4u8enLGjLH3qeh85g19xY5rsAusfwTdn5lg==} + '@floating-ui/core@1.6.4': + resolution: {integrity: sha512-a4IowK4QkXl4SCWTGUR0INAfEOX3wtsYw3rKK5InQEHMGObkR8Xk44qYQD9P4r6HHw0iIfK6GUKECmY8sTkqRA==} - '@floating-ui/dom@1.6.5': - resolution: {integrity: sha512-Nsdud2X65Dz+1RHjAIP0t8z5e2ff/IRbei6BqFrl1urT8sDVzM1HMQ+R0XcU5ceRfyO3I6ayeqIfh+6Wb8LGTw==} + '@floating-ui/dom@1.6.7': + resolution: {integrity: sha512-wmVfPG5o2xnKDU4jx/m4w5qva9FWHcnZ8BvzEe90D/RpwsJaTAVYPEPdQ8sbr/N8zZTAHlZUTQdqg8ZUbzHmng==} - '@floating-ui/react-dom@2.1.0': - resolution: {integrity: sha512-lNzj5EQmEKn5FFKc04+zasr09h/uX8RtJRNj5gUXsSQIXHVWTVh+hVAg1vOMCexkX8EgvemMvIFpQfkosnVNyA==} + '@floating-ui/react-dom@2.1.1': + resolution: {integrity: sha512-4h84MJt3CHrtG18mGsXuLCHMrug49d7DFkU0RMIyshRveBeyV2hmV/pDaF2Uxtu8kgq5r46llp5E5FQiR0K2Yg==} peerDependencies: react: '>=16.8.0' react-dom: '>=16.8.0' - '@floating-ui/utils@0.2.2': - resolution: {integrity: sha512-J4yDIIthosAsRZ5CPYP/jQvUAQtlZTTD/4suA08/FEnlxqW3sKS9iAhgsa9VYLZ6vDHn/ixJgIqRQPotoBjxIw==} + '@floating-ui/utils@0.2.4': + resolution: {integrity: sha512-dWO2pw8hhi+WrXq1YJy2yCuWoL20PddgGaqTgVe4cOS9Q6qklXCiA1tJEqX6BEwRNSCP84/afac9hd4MS+zEUA==} '@hapi/hoek@9.3.0': resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==} @@ -6226,16 +6278,16 @@ packages: resolution: {integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} - '@playwright/test@1.44.1': - resolution: {integrity: sha512-1hZ4TNvD5z9VuhNJ/walIjvMVvYkZKf71axoF/uiAqpntQJXpG64dlXhoDXE3OczPuTuvjf/M5KWFg5VAVUS3Q==} - engines: {node: '>=16'} + '@playwright/test@1.45.1': + resolution: {integrity: sha512-Wo1bWTzQvGA7LyKGIZc8nFSTFf2TkthGIFBR+QVNilvwouGzFd4PYukZe3rvf5PSqjHi1+1NyKSDZKcQWETzaA==} + engines: {node: '>=18'} hasBin: true - '@preact/signals-core@1.6.0': - resolution: {integrity: sha512-O/XGxwP85h1F7+ouqTMOIZ3+V1whfaV9ToIVcuyGriD4JkSD00cQo54BKdqjvBJxbenvp7ynfqRHEwI6e+NIhw==} + '@preact/signals-core@1.7.0': + resolution: {integrity: sha512-bEZLgmJGSBVP5PUPDowhPW3bVdMmp9Tr5OEl+SQK+8Tv9T7UsIfyN905cfkmmeqw8z4xp8T6zrl4M1uj9+HAfg==} - '@preact/signals@1.2.3': - resolution: {integrity: sha512-M2DXse3Wi8HwjI1d2vQWOLJ3lHogvqTsJYvl4ofXRXgMFQzJ7kmlZvlt5i8x5S5VwgZu0ghru4HkLqOoFfU2JQ==} + '@preact/signals@1.3.0': + resolution: {integrity: sha512-EOMeg42SlLS72dhoq6Vjq08havnLseWmPQ8A0YsgIAqMgWgx7V1a39+Pxo6i7SY5NwJtH4849JogFq3M67AzWg==} peerDependencies: preact: 10.x @@ -6252,19 +6304,19 @@ packages: '@radix-ui/primitive@1.0.0': resolution: {integrity: sha512-3e7rn8FDMin4CgeL7Z/49smCA3rFYY3Ha2rUQ7HRWFadS5iCRw08ZgVT1LaNTCNqgvrUiyczLflrVrF0SRQtNA==} - '@radix-ui/primitive@1.0.1': - resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==} + '@radix-ui/primitive@1.1.0': + resolution: {integrity: sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==} '@radix-ui/react-compose-refs@1.0.0': resolution: {integrity: sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA==} peerDependencies: react: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-compose-refs@1.0.1': - resolution: {integrity: sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==} + '@radix-ui/react-compose-refs@1.1.0': + resolution: {integrity: sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==} peerDependencies: '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true @@ -6274,11 +6326,11 @@ packages: peerDependencies: react: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-context@1.0.1': - resolution: {integrity: sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==} + '@radix-ui/react-context@1.1.0': + resolution: {integrity: sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==} peerDependencies: '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true @@ -6289,13 +6341,13 @@ packages: react: ^16.8 || ^17.0 || ^18.0 react-dom: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-dialog@1.0.5': - resolution: {integrity: sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==} + '@radix-ui/react-dialog@1.1.1': + resolution: {integrity: sha512-zysS+iU4YP3STKNS6USvFVqI4qqx8EpiwmT5TuCApVEBca+eRCbONi4EgzfNSuVnOXvC5UPHHMjs8RXO6DH9Bg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true @@ -6308,13 +6360,13 @@ packages: react: ^16.8 || ^17.0 || ^18.0 react-dom: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-dismissable-layer@1.0.5': - resolution: {integrity: sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==} + '@radix-ui/react-dismissable-layer@1.1.0': + resolution: {integrity: sha512-/UovfmmXGptwGcBQawLzvn2jOfM0t4z3/uKffoBlj724+n3FvBbZ7M0aaBOmkp6pqFYpO4yx8tSVJjx3Fl2jig==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true @@ -6326,11 +6378,11 @@ packages: peerDependencies: react: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-focus-guards@1.0.1': - resolution: {integrity: sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==} + '@radix-ui/react-focus-guards@1.1.0': + resolution: {integrity: sha512-w6XZNUPVv6xCpZUqb/yN9DL6auvpGX3C/ee6Hdi16v2UUy25HV2Q5bcflsiDyT/g5RwbPQ/GIT1vLkeRb+ITBw==} peerDependencies: '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true @@ -6341,13 +6393,13 @@ packages: react: ^16.8 || ^17.0 || ^18.0 react-dom: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-focus-scope@1.0.4': - resolution: {integrity: sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==} + '@radix-ui/react-focus-scope@1.1.0': + resolution: {integrity: sha512-200UD8zylvEyL8Bx+z76RJnASR2gRMuxlgFCPAe/Q/679a/r0eK3MBVYMb7vZODZcffZBdob1EGnky78xmVvcA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true @@ -6359,11 +6411,11 @@ packages: peerDependencies: react: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-id@1.0.1': - resolution: {integrity: sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==} + '@radix-ui/react-id@1.1.0': + resolution: {integrity: sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==} peerDependencies: '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true @@ -6374,13 +6426,13 @@ packages: react: ^16.8 || ^17.0 || ^18.0 react-dom: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-portal@1.0.4': - resolution: {integrity: sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==} + '@radix-ui/react-portal@1.1.1': + resolution: {integrity: sha512-A3UtLk85UtqhzFqtoC8Q0KvR2GbXF3mtPgACSazajqq6A41mEQgo53iPzY4i6BwDxlIFqWIhiQ2G729n+2aw/g==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true @@ -6393,13 +6445,13 @@ packages: react: ^16.8 || ^17.0 || ^18.0 react-dom: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-presence@1.0.1': - resolution: {integrity: sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==} + '@radix-ui/react-presence@1.1.0': + resolution: {integrity: sha512-Gq6wuRN/asf9H/E/VzdKoUtT8GC9PQc9z40/vEr0VCJ4u5XvvhWIrSsCB6vD2/cH7ugTdSfYq9fLJCcM00acrQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true @@ -6412,13 +6464,13 @@ packages: react: ^16.8 || ^17.0 || ^18.0 react-dom: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-primitive@1.0.3': - resolution: {integrity: sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==} + '@radix-ui/react-primitive@2.0.0': + resolution: {integrity: sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true @@ -6430,11 +6482,11 @@ packages: peerDependencies: react: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-slot@1.0.2': - resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==} + '@radix-ui/react-slot@1.1.0': + resolution: {integrity: sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==} peerDependencies: '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true @@ -6444,11 +6496,11 @@ packages: peerDependencies: react: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-use-callback-ref@1.0.1': - resolution: {integrity: sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==} + '@radix-ui/react-use-callback-ref@1.1.0': + resolution: {integrity: sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==} peerDependencies: '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true @@ -6458,11 +6510,11 @@ packages: peerDependencies: react: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-use-controllable-state@1.0.1': - resolution: {integrity: sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==} + '@radix-ui/react-use-controllable-state@1.1.0': + resolution: {integrity: sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==} peerDependencies: '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true @@ -6472,11 +6524,11 @@ packages: peerDependencies: react: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-use-escape-keydown@1.0.3': - resolution: {integrity: sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==} + '@radix-ui/react-use-escape-keydown@1.1.0': + resolution: {integrity: sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==} peerDependencies: '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true @@ -6486,11 +6538,11 @@ packages: peerDependencies: react: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-use-layout-effect@1.0.1': - resolution: {integrity: sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==} + '@radix-ui/react-use-layout-effect@1.1.0': + resolution: {integrity: sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==} peerDependencies: '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true @@ -6601,6 +6653,10 @@ packages: peerDependencies: rollup: ^1.20.0||^2.0.0 + '@rollup/pluginutils@4.2.1': + resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==} + engines: {node: '>= 8.0.0'} + '@rollup/pluginutils@5.1.0': resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==} engines: {node: '>=14.0.0'} @@ -6766,6 +6822,9 @@ packages: typescript: optional: true + '@storybook/channels@8.1.11': + resolution: {integrity: sha512-fu5FTqo6duOqtJFa6gFzKbiSLJoia+8Tibn3xFfB6BeifWrH81hc+AZq0lTmHo5qax2G5t8ZN8JooHjMw6k2RA==} + '@storybook/channels@8.1.6': resolution: {integrity: sha512-CzDnP6qfI8OC8pGUk+wPUzLPYcKhX8XbriF2gBtwl6qVM8YfkHP2mLTiDYDwBIi0rLuUbSm/SpILXQ/ouOHOGw==} @@ -6773,6 +6832,9 @@ packages: resolution: {integrity: sha512-xsFdBoAbo+2h/UCWuVXiH4Tu49iQ6d+3R1J8F2n4N6rAKxMqAb6fzYnH1GeRYeZk0HGqb2iNc4kBkxj0jW0rKw==} hasBin: true + '@storybook/client-logger@8.1.11': + resolution: {integrity: sha512-DVMh2usz3yYmlqCLCiCKy5fT8/UR9aTh+gSqwyNFkGZrIM4otC5A8eMXajXifzotQLT5SaOEnM3WzHwmpvMIEA==} + '@storybook/client-logger@8.1.6': resolution: {integrity: sha512-QfSoUxS1rmrBzO7o99og9g+Gkm7sTmU5ZOpTkjszjlRqfV6/77eUnUOzUikej4LqPLmlJV5fqGuvoP0aNVksDw==} @@ -6785,6 +6847,14 @@ packages: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta + '@storybook/core-common@8.1.11': + resolution: {integrity: sha512-Ix0nplD4I4DrV2t9B+62jaw1baKES9UbR/Jz9LVKFF9nsua3ON0aVe73dOjMxFWBngpzBYWe+zYBTZ7aQtDH4Q==} + peerDependencies: + prettier: ^2 || ^3 + peerDependenciesMeta: + prettier: + optional: true + '@storybook/core-common@8.1.6': resolution: {integrity: sha512-OTlfJFaTOB588ibXrrFm0TAXam6E5xV1VXSjNXL+fIifx8Kjln2HNSy1JKjvcblQneYiV4J1xPCVnAIe0EGHDg==} peerDependencies: @@ -6793,6 +6863,9 @@ packages: prettier: optional: true + '@storybook/core-events@8.1.11': + resolution: {integrity: sha512-vXaNe2KEW9BGlLrg0lzmf5cJ0xt+suPjWmEODH5JqBbrdZ67X6ApA2nb6WcxDQhykesWCuFN5gp1l+JuDOBi7A==} + '@storybook/core-events@8.1.6': resolution: {integrity: sha512-DaIVe4TUp/7uQdSJYGmJv9S/S364tSgZ3S3dZ1vsf1rgoUbCp5kTBtcd/fcqgukMPREgCgO9oDhmemI3SLAqzw==} @@ -6805,11 +6878,14 @@ packages: '@storybook/csf-plugin@8.1.6': resolution: {integrity: sha512-y2OW84leoWsqfBXb7EoRy2QUmtsI3gpqYqpyD/d5K+vQ+E9CBel2WB8RPrwcYm2L88WPDaufQQDzqyB7aMx4fQ==} + '@storybook/csf-tools@8.1.11': + resolution: {integrity: sha512-6qMWAg/dBwCVIHzANM9lSHoirwqSS+wWmv+NwAs0t9S94M75IttHYxD3IyzwaSYCC5llp0EQFvtXXAuSfFbibg==} + '@storybook/csf-tools@8.1.6': resolution: {integrity: sha512-jrKfHFNhiLBhWWW4/fm2wgKEVg55e6QuYUHY16KGd7PdPuzm+2Pt7jIl5V9yIj6a59YbjeMpT6jWPKbFx2TuCw==} - '@storybook/csf@0.1.8': - resolution: {integrity: sha512-Ntab9o7LjBCbFIao5l42itFiaSh/Qu+l16l/r/9qmV9LnYZkO+JQ7tzhdlwpgJfhs+B5xeejpdAtftDRyXNajw==} + '@storybook/csf@0.1.11': + resolution: {integrity: sha512-dHYFQH3mA+EtnCkHXzicbLgsvzYjcDJ1JWsogbItZogkPHgSJM/Wr71uMkcvw8v9mmCyP4NpXJuu6bPoVsOnzg==} '@storybook/docs-mdx@3.1.0-next.0': resolution: {integrity: sha512-t4syFIeSyufieNovZbLruPt2DmRKpbwL4fERCZ1MifWDRIORCKLc4NCEHy+IqvIqd71/SJV2k4B51nF7vlJfmQ==} @@ -6833,6 +6909,9 @@ packages: '@storybook/manager@8.1.6': resolution: {integrity: sha512-B7xc09FYHqC1sknJoWkGHBBCMQlfg7hF+4x42cGhAyYed4TeYAf7b1PDniq8L/PLbUgzTw+A62UC1fMurCcVDQ==} + '@storybook/node-logger@8.1.11': + resolution: {integrity: sha512-wdzFo7B2naGhS52L3n1qBkt5BfvQjs8uax6B741yKRpiGgeAN8nz8+qelkD25MbSukxvbPgDot7WJvsMU/iCzg==} + '@storybook/node-logger@8.1.6': resolution: {integrity: sha512-IZEiTLFHu8Oom/vdEGpisSw5CfU+cw6/fTaX1P3EVClFOWVuy8/3X5MPu4wJH3jPym6E2DBduIUFeRsiuq61gA==} @@ -6912,6 +6991,9 @@ packages: react-dom: optional: true + '@storybook/types@8.1.11': + resolution: {integrity: sha512-k9N5iRuY2+t7lVRL6xeu6diNsxO3YI3lS4Juv3RZ2K4QsE/b3yG5ElfJB8DjHDSHwRH4ORyrU71KkOCUVfvtnw==} + '@storybook/types@8.1.6': resolution: {integrity: sha512-cWpS9+x1pxCO39spR8QmumMK2ub2p5cvMtrRvWaIjBFPbCwm2CvjBXFWIra2veBCZTxUKJ9VWxvi7pzRHjN/nw==} @@ -6991,68 +7073,68 @@ packages: resolution: {integrity: sha512-XWzIhLTr5WYns/cNFXpXrmFy+LFf2xp60VnNUBZCpM1CGTx47FCDuUj2DQjxirMf2L6CP2jTRELK8ef01TecFQ==} engines: {node: '>=14'} - '@swc/core-darwin-arm64@1.5.27': - resolution: {integrity: sha512-jyoygXBcUcwUya2BI7Uvl0jwcm4kd0RBDGGkWgcFAZmwucSuLT3EsbpWhOwlL3ACT4rpnRlvh+k8nJlq3+w2Aw==} + '@swc/core-darwin-arm64@1.6.13': + resolution: {integrity: sha512-SOF4buAis72K22BGJ3N8y88mLNfxLNprTuJUpzikyMGrvkuBFNcxYtMhmomO0XHsgLDzOJ+hWzcgjRNzjMsUcQ==} engines: {node: '>=10'} cpu: [arm64] os: [darwin] - '@swc/core-darwin-x64@1.5.27': - resolution: {integrity: sha512-eOC583D6b3MS9oODCcZUvAV7ajunjENAPVQL7aZaW+piERW+o4koZAiPlzFdMAUMj7UeVg+UN9sBBbTbJgruIA==} + '@swc/core-darwin-x64@1.6.13': + resolution: {integrity: sha512-AW8akFSC+tmPE6YQQvK9S2A1B8pjnXEINg+gGgw0KRUUXunvu1/OEOeC5L2Co1wAwhD7bhnaefi06Qi9AiwOag==} engines: {node: '>=10'} cpu: [x64] os: [darwin] - '@swc/core-linux-arm-gnueabihf@1.5.27': - resolution: {integrity: sha512-bMvX0yF7WYzn1K+s0JWJhvyA3OeZHVrdjka8eZ4LSeuLfC0ggJefo+klyeuN2szn/LYP6/3oByyrWNY8RSHB4w==} + '@swc/core-linux-arm-gnueabihf@1.6.13': + resolution: {integrity: sha512-f4gxxvDXVUm2HLYXRd311mSrmbpQF2MZ4Ja6XCQz1hWAxXdhRl1gpnZ+LH/xIfGSwQChrtLLVrkxdYUCVuIjFg==} engines: {node: '>=10'} cpu: [arm] os: [linux] - '@swc/core-linux-arm64-gnu@1.5.27': - resolution: {integrity: sha512-KlkOcSPxrCqZTm4XrT/LT1o9gmyM2T6bw/hL6IwTYRBJg+sej4rc9iSfVRFZBfNuG3EVkFQSXxik+0yVOXR93Q==} + '@swc/core-linux-arm64-gnu@1.6.13': + resolution: {integrity: sha512-Nf/eoW2CbG8s+9JoLtjl9FByBXyQ5cjdBsA4efO7Zw4p+YSuXDgc8HRPC+E2+ns0praDpKNZtLvDtmF2lL+2Gg==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-arm64-musl@1.5.27': - resolution: {integrity: sha512-EwdTt5qykxFXJu7kS+0X0Mp/IlwO8KJ6LVNak2+N8bt1J1q/nCdg1tRDOYQ1Z/MVa1Tm+lJ664Qs1y2NY2SDTw==} + '@swc/core-linux-arm64-musl@1.6.13': + resolution: {integrity: sha512-2OysYSYtdw79prJYuKIiux/Gj0iaGEbpS2QZWCIY4X9sGoETJ5iMg+lY+YCrIxdkkNYd7OhIbXdYFyGs/w5LDg==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-x64-gnu@1.5.27': - resolution: {integrity: sha512-RsBbxbiSNWLJ2jbAdITtv30J4eZw4O/JJ5zxYgWI54TdY7YrVsqIdzuX+ldximt+CYvO9irHm/mSr/IJY2YXrw==} + '@swc/core-linux-x64-gnu@1.6.13': + resolution: {integrity: sha512-PkR4CZYJNk5hcd2+tMWBpnisnmYsUzazI1O5X7VkIGFcGePTqJ/bWlfUIVVExWxvAI33PQFzLbzmN5scyIUyGQ==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-linux-x64-musl@1.5.27': - resolution: {integrity: sha512-XpRx0Kpy6JEi1WSMqUfR3k8hXLqNOkVqFcUfzvfQ4QNBX5Ek7ywh7WAxlPhCrFp+wAfNAqqUyRY1xZpLvRU51A==} + '@swc/core-linux-x64-musl@1.6.13': + resolution: {integrity: sha512-OdsY7wryTxCKwGQcwW9jwWg3cxaHBkTTHi91+5nm7hFPpmZMz1HivJrWAMwVE7iXFw+M4l6ugB/wCvpYrUAAjA==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-win32-arm64-msvc@1.5.27': - resolution: {integrity: sha512-pwSTUIokyIp+Ha1pur34qdYjxqL1QzhP/HM8anzsFs4yvV2LSI7c3qc4GWPNv2eQ9WiFXyo29uCEIRN6qig7wg==} + '@swc/core-win32-arm64-msvc@1.6.13': + resolution: {integrity: sha512-ap6uNmYjwk9M/+bFEuWRNl3hq4VqgQ/Lk+ID/F5WGqczNr0L7vEf+pOsRAn0F6EV+o/nyb3ePt8rLhE/wjHpPg==} engines: {node: '>=10'} cpu: [arm64] os: [win32] - '@swc/core-win32-ia32-msvc@1.5.27': - resolution: {integrity: sha512-S0S6vqFscvmxPolwmpZvTRfTbYR+eGcyc0ge4x/+HcnBCm+m84rcGxmp3bBb1edNFaIV+X47BlGvvh85cJ4rkQ==} + '@swc/core-win32-ia32-msvc@1.6.13': + resolution: {integrity: sha512-IJ8KH4yIUHTnS/U1jwQmtbfQals7zWPG0a9hbEfIr4zI0yKzjd83lmtS09lm2Q24QBWOCFGEEbuZxR4tIlvfzA==} engines: {node: '>=10'} cpu: [ia32] os: [win32] - '@swc/core-win32-x64-msvc@1.5.27': - resolution: {integrity: sha512-aem+BcNW42JPbvV6L3Jl3LLj6G80aYADzYenToYisy0Aop0XZAxL/0FbhV+xWORNFtIUKynNtaa1CK7w0UxehQ==} + '@swc/core-win32-x64-msvc@1.6.13': + resolution: {integrity: sha512-f6/sx6LMuEnbuxtiSL/EkR0Y6qUHFw1XVrh6rwzKXptTipUdOY+nXpKoh+1UsBm/r7H0/5DtOdrn3q5ZHbFZjQ==} engines: {node: '>=10'} cpu: [x64] os: [win32] - '@swc/core@1.5.27': - resolution: {integrity: sha512-HmSSCBoUSRDFAd8aEB+WILkCofIp1c2OU6ZJWu1aCt6pijwQSkA4y51CTBcdvyy/+zX1W3cic7alfdhmQxxeEQ==} + '@swc/core@1.6.13': + resolution: {integrity: sha512-eailUYex6fkfaQTev4Oa3mwn0/e3mQU4H8y1WPuImYQESOQDtVrowwUGDSc19evpBbHpKtwM+hw8nLlhIsF+Tw==} engines: {node: '>=10'} peerDependencies: '@swc/helpers': '*' @@ -7069,8 +7151,8 @@ packages: peerDependencies: '@swc/core': '*' - '@swc/types@0.1.8': - resolution: {integrity: sha512-RNFA3+7OJFNYY78x0FYwi1Ow+iF1eF5WvmfY1nXPOEH4R2p/D4Cr1vzje7dNAI2aLFqpv8Wyz4oKSWqIZArpQA==} + '@swc/types@0.1.9': + resolution: {integrity: sha512-qKnCno++jzcJ4lM4NTfYifm1EFSCeIfKiAHAfkENZAV5Kl9PjJIyd2yeeVv6c/2CckuLyv2NmRC5pv6pm2WQBg==} '@tannin/compile@1.1.0': resolution: {integrity: sha512-n8m9eNDfoNZoxdvWiTfW/hSPhehzLJ3zW7f8E7oT6mCROoMNWCB4TYtv041+2FMAxweiE0j7i1jubQU4MEC/Gg==} @@ -7151,8 +7233,8 @@ packages: vitest: optional: true - '@testing-library/preact@3.2.3': - resolution: {integrity: sha512-y6Kklp1XK3f1X2fWCbujmJyzkf+1BgLYXNgAx21j9+D4CoqMTz5qC4SQufb1L6q/jxLGACzrQ90ewVOTBvHOfg==} + '@testing-library/preact@3.2.4': + resolution: {integrity: sha512-F+kJ243LP6VmEK1M809unzTE/ijg+bsMNuiRN0JEDIJBELKKDNhdgC/WrUSZ7klwJvtlO3wQZ9ix+jhObG07Fg==} engines: {node: '>= 12'} peerDependencies: preact: '>=10 || ^10.0.0-alpha.0 || ^10.0.0-beta.0' @@ -7251,8 +7333,8 @@ packages: '@types/estree@1.0.5': resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} - '@types/express-serve-static-core@4.19.3': - resolution: {integrity: sha512-KOzM7MhcBFlmnlr/fzISFF5vGWVSvN6fTd4T+ExOt08bA/dA5kpSzY52nMsI1KDFmUREpJelPYyuslLRSjjgCg==} + '@types/express-serve-static-core@4.19.5': + resolution: {integrity: sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg==} '@types/express@4.17.21': resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==} @@ -7314,8 +7396,8 @@ packages: '@types/linkify-it@5.0.0': resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} - '@types/lodash@4.17.5': - resolution: {integrity: sha512-MBIOHVZqVqgfro1euRDWX7OO0fBVUUMrN6Pwm8LQsz8cWhEpihlvR70ENj3f40j58TNxZaWv2ndSkInykNBBJw==} + '@types/lodash@4.17.6': + resolution: {integrity: sha512-OpXEVoCKSS3lQqjx9GGGOapBeuW5eUboYHRlHP9urXPX25IKZ6AnP5ZRxtVf63iieUbsHxLn8NQ5Nlftc6yzAA==} '@types/markdown-it@14.0.1': resolution: {integrity: sha512-6WfOG3jXR78DW8L5cTYCVVGAsIFZskRHCDo5tbqa+qtKVt4oDRVH7hyIWu1SpDQJlmIoEivNQZ5h+AGAOrgOtQ==} @@ -7341,11 +7423,11 @@ packages: '@types/ms@0.7.34': resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==} - '@types/node@18.19.34': - resolution: {integrity: sha512-eXF4pfBNV5DAMKGbI02NnDtWrQ40hAN558/2vvS4gMpMIxaf6JmD7YjnZbq0Q9TDSSkKBamime8ewRoomHdt4g==} + '@types/node@18.19.39': + resolution: {integrity: sha512-nPwTRDKUctxw3di5b4TfT3I0sWDiWoPQCZjXhvdkINntwr8lcoVCKsTgnXeRubKIlfnV+eN/HYk6Jb40tbcEAQ==} - '@types/node@20.14.2': - resolution: {integrity: sha512-xyu6WAMVwv6AKFLB+e/7ySZVr/0zLCzOa7rSpq6jNwpqOrUbcACDWC+53d4n2QHOnDou0fbIsg8wZu/sxrnI4Q==} + '@types/node@20.14.10': + resolution: {integrity: sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==} '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} @@ -7359,9 +7441,6 @@ packages: '@types/prop-types@15.7.12': resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==} - '@types/pug@2.0.10': - resolution: {integrity: sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==} - '@types/qrcode.react@1.0.5': resolution: {integrity: sha512-BghPtnlwvrvq8QkGa1H25YnN+5OIgCKFuQruncGWLGJYOzeSKiix/4+B9BtfKF2wf5ja8yfyWYA3OXju995G8w==} @@ -8045,8 +8124,8 @@ packages: resolution: {integrity: sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==} engines: {node: '>=0.4.0'} - acorn-walk@8.3.2: - resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==} + acorn-walk@8.3.3: + resolution: {integrity: sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==} engines: {node: '>=0.4.0'} acorn@7.4.1: @@ -8054,8 +8133,8 @@ packages: engines: {node: '>=0.4.0'} hasBin: true - acorn@8.11.3: - resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} + acorn@8.12.1: + resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} engines: {node: '>=0.4.0'} hasBin: true @@ -8296,8 +8375,12 @@ packages: axios@1.6.8: resolution: {integrity: sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==} - axobject-query@3.2.1: - resolution: {integrity: sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==} + axobject-query@3.2.4: + resolution: {integrity: sha512-aPTElBrbifBU1krmZxGZOlBkslORe7Ll7+BDnI50Wy4LgOt69luMgevkDfTq1O/ZgprooPCtWpjCwKSZw/iZ4A==} + engines: {node: '>= 0.4'} + + axobject-query@4.0.0: + resolution: {integrity: sha512-+60uv1hiVFhHZeO+Lz0RYzsVHy5Wr1ayX0mwda9KPDVLNJgZ1T9Ny7VmFbLDzxsH0D87I86vgj3gFrjTJUYznw==} b4a@1.6.6: resolution: {integrity: sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==} @@ -8403,8 +8486,8 @@ packages: bare-fs@2.3.1: resolution: {integrity: sha512-W/Hfxc/6VehXlsgFtbB5B4xFcsCl+pAh30cYhoFyXErf6oGrwjh8SwiPAdHgpmWonKuYpZgGywN0SXt7dgsADA==} - bare-os@2.3.0: - resolution: {integrity: sha512-oPb8oMM1xZbhRQBngTgpcQ5gXw6kjOaRsSWsIeNyRxGed2w/ARyP7ScBYpWR1qfX2E5rS3gBw6OWcSQo+s+kUg==} + bare-os@2.4.0: + resolution: {integrity: sha512-v8DTT08AS/G0F9xrhyLtepoo9EJBJ85FRSMbu1pQUlAf6A8T0tEEQGMVObWeqpjhSPXsE0VGlluFBJu2fdoTNg==} bare-path@2.1.3: resolution: {integrity: sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==} @@ -8552,8 +8635,8 @@ packages: caniuse-api@3.0.0: resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} - caniuse-lite@1.0.30001632: - resolution: {integrity: sha512-udx3o7yHJfUxMLkGohMlVHCvFvWmirKh9JAH/d7WOLPetlH+LTL5cocMZ0t7oZx/mdlOWXti97xLZWc8uURRHg==} + caniuse-lite@1.0.30001640: + resolution: {integrity: sha512-lA4VMpW0PSUrFnkmVuEKBUovSWKhj7puyCg8StBChgu298N1AtuF1sKWEvfDuimSEDbhlb/KqPKC3fs1HbuQUA==} capital-case@1.0.4: resolution: {integrity: sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==} @@ -8727,6 +8810,9 @@ packages: resolution: {integrity: sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==} engines: {node: '>=0.10.0'} + code-red@1.0.4: + resolution: {integrity: sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==} + collect-v8-coverage@1.0.2: resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} @@ -8836,6 +8922,9 @@ packages: engines: {node: ^12.20.0 || ^14.13.0 || >=16.0.0} hasBin: true + confbox@0.1.7: + resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} + config@3.3.7: resolution: {integrity: sha512-mX/n7GKDYZMqvvkY6e6oBY49W8wxdmQt+ho/5lhwFDXqQW9gI+Ahp8EKp8VAbISPnmf2+Bv5uZK7lKXZ6pf1aA==} engines: {node: '>= 10.0.0'} @@ -8924,6 +9013,9 @@ packages: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} + crypto-js@4.2.0: + resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} + crypto-random-string@2.0.0: resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==} engines: {node: '>=8'} @@ -9368,8 +9460,8 @@ packages: engines: {node: '>=0.10.0'} hasBin: true - electron-to-chromium@1.4.796: - resolution: {integrity: sha512-NglN/xprcM+SHD2XCli4oC6bWe6kHoytcyLKCWXmRL854F0qhPhaYgUswUsglnPxYaNQIg2uMY4BvaomIf3kLA==} + electron-to-chromium@1.4.818: + resolution: {integrity: sha512-eGvIk2V0dGImV9gWLq8fDfTTsCAeMDwZqEPMr+jMInxZdnp9Us8UpovYpRCf9NQ7VOFgrN2doNSgvISbsbNpxA==} elegant-spinner@1.0.1: resolution: {integrity: sha512-B+ZM+RXvRqQaAmkMlO/oSe5nMUOaUnyfGYCEHoR8wrXsZR2mA0XVibsxV1bvTwxdRWah1PkQqso2EzhILGHtEQ==} @@ -9474,8 +9566,8 @@ packages: es-module-lexer@0.9.3: resolution: {integrity: sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==} - es-module-lexer@1.5.3: - resolution: {integrity: sha512-i1gCgmR9dCl6Vil6UKPI/trA69s08g/syhiDK9TG0Nf1RJjjFI+AzoWW7sPufzkgYAn861skuCwJa0pIIHYxvg==} + es-module-lexer@1.5.4: + resolution: {integrity: sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==} es-object-atoms@1.0.0: resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} @@ -9495,9 +9587,6 @@ packages: es6-error@4.1.1: resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==} - es6-promise@3.3.1: - resolution: {integrity: sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==} - esbuild-loader@3.0.1: resolution: {integrity: sha512-aZfGybqTeuyCd4AsVvWOOfkhIuN+wfZFjMyh3gyQEU1Uvsl8L6vye9HqP93iRa0iTA+6Jclap514PJIC3cLnMA==} peerDependencies: @@ -9607,9 +9696,6 @@ packages: '@typescript-eslint/parser': optional: true - eslint-plugin-inclusive-language@2.2.1: - resolution: {integrity: sha512-RL6avDWXCS0Dcp9axhvHRUp65qG07qjOrh6J4BNNahPvRY3PuYGnAd0H1strZ9cob79JiEW4Bq0j3gEuzbv0/A==} - eslint-plugin-jest-dom@5.4.0: resolution: {integrity: sha512-yBqvFsnpS5Sybjoq61cJiUsenRkC9K32hYQBFS9doBR7nbQZZ5FyO+X7MlmfM1C48Ejx/qTuOCgukDUNyzKZ7A==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0, npm: '>=6', yarn: '>=1'} @@ -9702,22 +9788,6 @@ packages: peerDependencies: eslint: ^7.5.0 || ^8.0.0 - eslint-plugin-wpcalypso@8.0.0: - resolution: {integrity: sha512-V1BqCzy1TpQGMxEId1ZudTszaRBAfjgHav9aCDdzlY1AwOjvVojhxqAk1Ir4AqIDddoz39RKfvJ9SdKpq3S+/Q==} - engines: {node: '>=v14.21.3'} - peerDependencies: - '@babel/core': '>=7.17.5' - eslint: '>=8.48.0' - eslint-plugin-inclusive-language: ^2.2.0 - eslint-plugin-jsdoc: ^46.5.1 - eslint-plugin-react: ^7.33.2 - eslint-plugin-react-hooks: ^4.3.0 - peerDependenciesMeta: - eslint-plugin-react: - optional: true - eslint-plugin-react-hooks: - optional: true - eslint-scope@5.1.1: resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} engines: {node: '>=8.0.0'} @@ -9778,6 +9848,9 @@ packages: estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} @@ -9972,8 +10045,8 @@ packages: flatted@3.3.1: resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} - flow-parser@0.237.2: - resolution: {integrity: sha512-mvI/kdfr3l1waaPbThPA8dJa77nHXrfZIun+SWvFwSwDjmeByU7mGJGRmv1+7guU6ccyLV8e1lqZA1lD4iMGnQ==} + flow-parser@0.239.0: + resolution: {integrity: sha512-U5dgOsS6cg4FGNzzTD/zHRDM4bliL6laUgD0LUCSMzI2zEfKMnRV2/wgDv8nKmO2Z1R8ri5pE1YoldmrSV7FOw==} engines: {node: '>=0.4.0'} fn.name@1.1.0: @@ -9998,8 +10071,8 @@ packages: resolution: {integrity: sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==} engines: {node: '>=8.0.0'} - foreground-child@3.1.1: - resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} + foreground-child@3.2.1: + resolution: {integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==} engines: {node: '>=14'} fork-ts-checker-webpack-plugin@8.0.0: @@ -10031,8 +10104,8 @@ packages: fraction.js@4.3.7: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} - framer-motion@11.2.10: - resolution: {integrity: sha512-/gr3PLZUVFCc86a9MqCUboVrALscrdluzTb3yew+2/qKBU8CX6nzs918/SRBRCqaPbx0TZP10CB6yFgK2C5cYQ==} + framer-motion@11.2.13: + resolution: {integrity: sha512-AyIeegfkXlkX1lWEudRYsJlC+0A59cE8oFK9IsN9bUQzxLwcvN3AEaYaznkELiWlHC7a0eD7pxsYQo7BC05S5A==} peerDependencies: '@emotion/is-prop-valid': '*' react: ^18.0.0 @@ -10237,8 +10310,8 @@ packages: resolution: {integrity: sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - globby@14.0.1: - resolution: {integrity: sha512-jOMLD2Z7MAhyG8aJpNOpmziMOP4rPLcc95oQPKXBazW82z+CEgPFBQvEpRUa1KeIMUJo4Wsm+q6uzO/Q/4BksQ==} + globby@14.0.2: + resolution: {integrity: sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw==} engines: {node: '>=18'} good-listener@1.2.2: @@ -10406,8 +10479,8 @@ packages: resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} engines: {node: '>= 6'} - https-proxy-agent@7.0.4: - resolution: {integrity: sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==} + https-proxy-agent@7.0.5: + resolution: {integrity: sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==} engines: {node: '>= 14'} human-signals@2.1.0: @@ -10422,9 +10495,6 @@ packages: resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} engines: {node: '>=16.17.0'} - humps@2.0.1: - resolution: {integrity: sha512-E0eIbrFWUhwfXJmsbdjRQFQPrl5pTEoKlz163j1mTqqUnU9PgR4AgB8AIITzuB3vLBdxZXyZ9TDIrwB2OASz4g==} - husky@8.0.3: resolution: {integrity: sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==} engines: {node: '>=14'} @@ -10570,8 +10640,9 @@ packages: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} engines: {node: '>= 0.4'} - is-core-module@2.13.1: - resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} + is-core-module@2.14.0: + resolution: {integrity: sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==} + engines: {node: '>= 0.4'} is-data-view@1.0.1: resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} @@ -10694,6 +10765,9 @@ packages: is-reference@1.2.1: resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} + is-reference@3.0.2: + resolution: {integrity: sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==} + is-regex@1.1.4: resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} engines: {node: '>= 0.4'} @@ -10795,8 +10869,8 @@ packages: resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} engines: {node: '>=8'} - istanbul-lib-instrument@6.0.2: - resolution: {integrity: sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw==} + istanbul-lib-instrument@6.0.3: + resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} engines: {node: '>=10'} istanbul-lib-processinfo@2.0.3: @@ -10818,9 +10892,9 @@ packages: iterator.prototype@1.1.2: resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==} - jackspeak@3.4.0: - resolution: {integrity: sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw==} - engines: {node: '>=14'} + jackspeak@3.4.1: + resolution: {integrity: sha512-U23pQPDnmYybVkYjObcuYMk43VRlMLLqLI+RdZy8s8WV8WsxO9SnqSroKaluuvcNOdCAlauKszDwd+umbot5Mg==} + engines: {node: '>=18'} jake@10.9.1: resolution: {integrity: sha512-61btcOHNnLnsOdtLgA5efqQWjnSi/vow5HbI7HMdKKWqvrKR1bLK3BPlJn9gcSaP2ewuamUSMB5XEy76KUIS2w==} @@ -11010,8 +11084,8 @@ packages: resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==} hasBin: true - joi@17.13.1: - resolution: {integrity: sha512-vaBlIKCyo4FCUtCm7Eu4QZd/q02bWcxfUO6YSXAZOWF6gzcLBeba8kwotUdYJjDLW8Cz8RywsSOqiNJZW0mNvg==} + joi@17.13.3: + resolution: {integrity: sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==} jquery@3.6.0: resolution: {integrity: sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==} @@ -11094,8 +11168,8 @@ packages: engines: {node: '>=6'} hasBin: true - jsonc-parser@3.2.1: - resolution: {integrity: sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==} + jsonc-parser@3.3.1: + resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} jsonfile@6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} @@ -11223,6 +11297,9 @@ packages: engines: {node: '>=8.3.0'} hasBin: true + locate-character@3.0.0: + resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==} + locate-path@3.0.0: resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} engines: {node: '>=6'} @@ -11316,9 +11393,9 @@ packages: lower-case@2.0.2: resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} - lru-cache@10.2.2: - resolution: {integrity: sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==} - engines: {node: 14 || >=16.14} + lru-cache@10.4.0: + resolution: {integrity: sha512-bfJaPTuEiTYBu+ulDaeQ0F+uLmlfFkMgXj4cbwfuMSjgObGMzb55FMMbDvbRU0fAHZ4sLGkz2mKwcMg8Dvm8Ww==} + engines: {node: '>=18'} lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} @@ -11462,23 +11539,23 @@ packages: micromark-core-commonmark@2.0.1: resolution: {integrity: sha512-CUQyKr1e///ZODyD1U3xit6zXwy1a8q2a1S1HKtIlmgvurrEpaw/Y9y6KSIbF8P59cn/NjzHyO+Q2fAyYLQrAA==} - micromark-extension-gfm-autolink-literal@2.0.0: - resolution: {integrity: sha512-rTHfnpt/Q7dEAK1Y5ii0W8bhfJlVJFnJMHIPisfPK3gpVNuOP0VnRl96+YJ3RYWV/P4gFeQoGKNlT3RhuvpqAg==} + micromark-extension-gfm-autolink-literal@2.1.0: + resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} - micromark-extension-gfm-footnote@2.0.0: - resolution: {integrity: sha512-6Rzu0CYRKDv3BfLAUnZsSlzx3ak6HAoI85KTiijuKIz5UxZxbUI+pD6oHgw+6UtQuiRwnGRhzMmPRv4smcz0fg==} + micromark-extension-gfm-footnote@2.1.0: + resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==} - micromark-extension-gfm-strikethrough@2.0.0: - resolution: {integrity: sha512-c3BR1ClMp5fxxmwP6AoOY2fXO9U8uFMKs4ADD66ahLTNcwzSCyRVU4k7LPV5Nxo/VJiR4TdzxRQY2v3qIUceCw==} + micromark-extension-gfm-strikethrough@2.1.0: + resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==} - micromark-extension-gfm-table@2.0.0: - resolution: {integrity: sha512-PoHlhypg1ItIucOaHmKE8fbin3vTLpDOUg8KAr8gRCF1MOZI9Nquq2i/44wFvviM4WuxJzc3demT8Y3dkfvYrw==} + micromark-extension-gfm-table@2.1.0: + resolution: {integrity: sha512-Ub2ncQv+fwD70/l4ou27b4YzfNaCJOvyX4HxXU15m7mpYY+rjuWzsLIPZHJL253Z643RpbcP1oeIJlQ/SKW67g==} micromark-extension-gfm-tagfilter@2.0.0: resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} - micromark-extension-gfm-task-list-item@2.0.1: - resolution: {integrity: sha512-cY5PzGcnULaN5O7T+cOzfMoHjBW7j+T9D2sucA5d/KbsBTPcYdebm9zUd9zzdgJGCwahV+/W78Z3nbulBYVbTw==} + micromark-extension-gfm-task-list-item@2.1.0: + resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==} micromark-extension-gfm@3.0.0: resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} @@ -11593,8 +11670,8 @@ packages: resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} engines: {node: '>=16 || 14 >=14.17'} - minimatch@9.0.4: - resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==} + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} minimist@1.2.8: @@ -11622,15 +11699,14 @@ packages: mkdirp-classic@0.5.3: resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} - mkdirp@0.5.6: - resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} - hasBin: true - mkdirp@1.0.4: resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} engines: {node: '>=10'} hasBin: true + mlly@1.7.1: + resolution: {integrity: sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==} + mock-xmlhttprequest@8.3.0: resolution: {integrity: sha512-yjNaP8HskE7GhO0D12kB35+OvCnNAh2fJnD1/mC5Y3WW8WcozJnC23w+8UoU+dmVR4x1KpAB8PPtybY9wI16Ew==} engines: {node: '>=16.0.0'} @@ -11831,8 +11907,8 @@ packages: engines: {node: '>=8.9'} hasBin: true - nypm@0.3.8: - resolution: {integrity: sha512-IGWlC6So2xv6V4cIDmoV0SwwWx7zLG086gyqkyumteH2fIgCAM4nDVFB2iDRszDvmdSVW9xb1N+2KjQ6C7d4og==} + nypm@0.3.9: + resolution: {integrity: sha512-BI2SdqqTHg2d4wJh8P9A1W+bslg33vOE9IZDY6eR2QC+Pu1iNBVZUqczrd43rJb+fMzHU7ltAYKsEFY/kHMFcw==} engines: {node: ^14.16.0 || >=16.10.0} hasBin: true @@ -11840,8 +11916,9 @@ packages: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} - object-inspect@1.13.1: - resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} + object-inspect@1.13.2: + resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==} + engines: {node: '>= 0.4'} object-is@1.1.6: resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==} @@ -11989,8 +12066,8 @@ packages: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} - pac-proxy-agent@7.0.1: - resolution: {integrity: sha512-ASV8yU4LLKBAjqIPMbrgtaKIvxQri/yh2OpI+S6hVa9JRkUI3Y3NPFbfngDtY7oFtSMD3w31Xns89mDa3Feo5A==} + pac-proxy-agent@7.0.2: + resolution: {integrity: sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg==} engines: {node: '>= 14'} pac-resolver@7.0.1: @@ -12101,8 +12178,8 @@ packages: pathe@1.1.2: resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} - pbf@3.2.1: - resolution: {integrity: sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ==} + pbf@3.3.0: + resolution: {integrity: sha512-XDF38WCH3z5OV/OVa8GKUNtLAyneuzbCisx7QUCF8Q6Nutx0WnJrQe5O+kOtBlLfRNUws98Y58Lblp+NJG5T4Q==} hasBin: true peek-stream@1.1.3: @@ -12111,6 +12188,9 @@ packages: pend@1.2.0: resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} + periscopic@3.1.0: + resolution: {integrity: sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==} + photon@4.0.0: resolution: {integrity: sha512-RD3buB17jW9B+OOPjIqv/cE9imCyR+WJ4ALWtb1Q1mVg8OfYnHAyvdVTxa/+bZFNI2FWaQBKry3i1mItmW3H3A==} @@ -12153,14 +12233,17 @@ packages: resolution: {integrity: sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==} engines: {node: '>=14.16'} - playwright-core@1.44.1: - resolution: {integrity: sha512-wh0JWtYTrhv1+OSsLPgFzGzt67Y7BE/ZS3jEqgGBlp2ppp1ZDj8c+9IARNW4dwf1poq5MgHreEM2KV/GuR4cFA==} - engines: {node: '>=16'} + pkg-types@1.1.3: + resolution: {integrity: sha512-+JrgthZG6m3ckicaOB74TwQ+tBWsFl3qVQg7mN8ulwSOElJ7gBhKzj2VkCPnZ4NlF6kEquYU+RIYNVAvzd54UA==} + + playwright-core@1.45.1: + resolution: {integrity: sha512-LF4CUUtrUu2TCpDw4mcrAIuYrEjVDfT1cHbJMfwnE2+1b8PZcFzPNgvZCvq2JfQ4aTjRCCHw5EJ2tmr2NSzdPg==} + engines: {node: '>=18'} hasBin: true - playwright@1.44.1: - resolution: {integrity: sha512-qr/0UJ5CFAtloI3avF95Y0L1xQo6r3LQArLIg/z/PoGJ6xa+EwzrwO5lpNr/09STxdHuUoP2mvuELJS+hLdtgg==} - engines: {node: '>=16'} + playwright@1.45.1: + resolution: {integrity: sha512-Hjrgae4kpSQBr98nhCj3IScxVeVUixqj+5oyif8TdIn2opTCPEzqAqNMeK42i3cWDCVu9MI+ZsGWw+gVR4ISBg==} + engines: {node: '>=18'} hasBin: true pluralize@8.0.0: @@ -12367,8 +12450,8 @@ packages: peerDependencies: postcss: ^8.4.31 - postcss-prefixwrap@1.48.0: - resolution: {integrity: sha512-CXnUo/BsymtFe/4/ic3rbCGdd+SZQE7XJDl1gMtk5o0BUamO9G6L9OxKhbNYb20Na7paZhsKvaDqOeY522Hx5w==} + postcss-prefixwrap@1.49.0: + resolution: {integrity: sha512-TpUrBl78L3zJXuL32YBnPY122zlOo9qm31onXQFX+n0UdyRciBUz8Zefxt5mo963dqQbtkvg91XOgx6Vx8J7hQ==} peerDependencies: postcss: '*' @@ -12412,8 +12495,8 @@ packages: peerDependencies: postcss: ^8.4.31 - postcss-urlrebase@1.3.0: - resolution: {integrity: sha512-LOFN43n1IewKriXiypMNNinXeptttSyGGRLPbBMdQzuTvvCEo5mz/gG06y/HqrkN7p3ayHQf2R2bTBv639FOaQ==} + postcss-urlrebase@1.4.0: + resolution: {integrity: sha512-rRaxMmWvXrn8Rk1PqsxmaJwldRHsr0WbbASKKCZYxXwotHkM/5X/6IrwaEe8pdzpbNGCEY86yhYMN0MhgOkADA==} peerDependencies: postcss: ^8.3.0 @@ -12424,18 +12507,15 @@ packages: resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} engines: {node: ^10 || ^12 || >=14} - postcss@8.4.38: - resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} + postcss@8.4.39: + resolution: {integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==} engines: {node: ^10 || ^12 || >=14} potpack@1.0.2: resolution: {integrity: sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==} - preact@10.12.1: - resolution: {integrity: sha512-l8386ixSsBdbreOAkqtrwqHwdvR35ID8c3rKPa8lCWuO86dBi32QWHV4vfsZK1utLLFMvw+Z5Ad4XLkZzchscg==} - - preact@10.22.0: - resolution: {integrity: sha512-RRurnSjJPj4rp5K6XoP45Ui33ncb7e4H7WiOHVpjbkvqvA3U+N8Z6Qbo0AE6leGYBV66n8EhEaFixvIu3SkxFw==} + preact@10.22.1: + resolution: {integrity: sha512-jRYbDDgMpIb5LHq3hkI0bbl+l/TQ9UnkdQ0ww+lp+4MMOdqaUYdFc5qeyP+IV8FAd/2Em7drVPeKdQxsiWCf/A==} prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} @@ -12702,8 +12782,8 @@ packages: '@types/react': optional: true - react-remove-scroll@2.5.5: - resolution: {integrity: sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==} + react-remove-scroll@2.5.7: + resolution: {integrity: sha512-FnrTWO4L7/Bhhf3CYBNArEG/yROV0tKmTv7/3h9QCFvH6sndeFf1wPqOcbFVu5VAulS5dV1wGT3GZZ/1GawqiA==} engines: {node: '>=10'} peerDependencies: '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 @@ -12963,9 +13043,6 @@ packages: require-main-filename@2.0.0: resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} - require-relative@0.8.7: - resolution: {integrity: sha512-AKGr4qvHiryxRb19m3PsLRGuKVAbJLUD7E6eOaHkfKhwc+vSgVOCY5xNvm9EkolBKTOf0GrQAZKLimOCz81Khg==} - requireindex@1.2.0: resolution: {integrity: sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==} engines: {node: '>=0.10.5'} @@ -13037,11 +13114,6 @@ packages: deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true - rimraf@2.7.1: - resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} - deprecated: Rimraf versions prior to v4 are no longer supported - hasBin: true - rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} deprecated: Rimraf versions prior to v4 are no longer supported @@ -13062,8 +13134,8 @@ packages: peerDependencies: svelte: '*' - rollup-plugin-svelte@7.1.0: - resolution: {integrity: sha512-vopCUq3G+25sKjwF5VilIbiY6KCuMNHP1PFvx2Vr3REBNMDllKHFZN2B9jwwC+MqNc3UPKkjXnceLPEjTjXGXg==} + rollup-plugin-svelte@7.2.2: + resolution: {integrity: sha512-hgnIblTRewaBEVQD6N0Q43o+y6q1TmDRhBjaEzQCi50bs8TXqjc+d1zFZyE8tsfgcfNHZQzclh4RxlFUB85H8Q==} engines: {node: '>=10'} peerDependencies: rollup: '>=2.0.0' @@ -13121,9 +13193,6 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - sander@0.5.1: - resolution: {integrity: sha512-3lVqBir7WuKDHGrKRDn/1Ye3kwpXaDOMsiRP1wd6wpZW56gJhsbp5RqQpA6JG/P+pkXizygnr1dKR8vzWaVsfA==} - sass-loader@12.4.0: resolution: {integrity: sha512-7xN+8khDIzym1oL9XyS6zP6Ges+Bo2B2xbPrjdMHEYyV3AQYhd/wXeru++3ODHF0zMjYmVadblSKrPrjEkL8mg==} engines: {node: '>= 12.13.0'} @@ -13301,18 +13370,14 @@ packages: snake-case@3.0.4: resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} - socks-proxy-agent@8.0.3: - resolution: {integrity: sha512-VNegTZKhuGq5vSD6XNKlbqWhyt/40CgoEw8XxD6dhnm8Jq9IEa3nIa4HwnM8XOqU0CdB0BwWVXusqiFXfHB3+A==} + socks-proxy-agent@8.0.4: + resolution: {integrity: sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==} engines: {node: '>= 14'} socks@2.8.3: resolution: {integrity: sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==} engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} - sorcery@0.11.0: - resolution: {integrity: sha512-J69LQ22xrQB1cIFJhPfgtLuI6BpWRiWu1Y3vSsIwK/eAScqJxd/+CJlUuHQRdX2C9NGFamq+KqNywGgaThwfHw==} - hasBin: true - sort-object-keys@1.1.3: resolution: {integrity: sha512-855pvK+VkU7PaKYPc+Jjnmt4EzejQHyhhF33q31qG8x7maDzkeFhAAThdCYay11CISO+qAMwjOBP+fPZe0IPyg==} @@ -13579,21 +13644,21 @@ packages: svelte: optional: true - svelte-preprocess@5.0.4: - resolution: {integrity: sha512-ABia2QegosxOGsVlsSBJvoWeXy1wUKSfF7SWJdTjLAbx/Y3SrVevvvbFNQqrSJw89+lNSsM58SipmZJ5SRi5iw==} - engines: {node: '>= 14.10.0'} + svelte-preprocess@6.0.2: + resolution: {integrity: sha512-OvDTLfaOkkhjprbDKO0SOCkjNYuHy16dbD4SpqbIi6QiabOMHxRT4km5/dzbFFkmW1L0E2INF3MFltG2pgOyKQ==} + engines: {node: '>= 18.0.0'} peerDependencies: '@babel/core': ^7.10.2 coffeescript: ^2.5.1 less: ^3.11.3 || ^4.0.0 postcss: ^7 || ^8 - postcss-load-config: ^2.1.0 || ^3.0.0 || ^4.0.0 + postcss-load-config: '>=3' pug: ^3.0.0 sass: ^1.26.8 - stylus: ^0.55.0 + stylus: '>=0.55' sugarss: ^2.0.0 || ^3.0.0 || ^4.0.0 - svelte: ^3.23.0 || ^4.0.0-next.0 || ^4.0.0 - typescript: '>=3.9.5 || ^4.0.0 || ^5.0.0' + svelte: ^4.0.0 || ^5.0.0-next.100 || ^5.0.0 + typescript: ^5.0.0 peerDependenciesMeta: '@babel/core': optional: true @@ -13616,9 +13681,9 @@ packages: typescript: optional: true - svelte@3.58.0: - resolution: {integrity: sha512-brIBNNB76mXFmU/Kerm4wFnkskBbluBDCjx/8TcpYRb298Yh2dztS2kQ6bhtjMcvUhd5ynClfwpz5h2gnzdQ1A==} - engines: {node: '>= 8'} + svelte@4.2.18: + resolution: {integrity: sha512-d0FdzYIiAePqRJEb90WlJDkjUEx42xhivxN8muUBmfZnP+tzUgz12DJ2hRJi8sIHCME7jeK1PTMgKPSfTd8JrA==} + engines: {node: '>=16'} svg-parser@2.0.4: resolution: {integrity: sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==} @@ -13730,8 +13795,8 @@ packages: resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} engines: {node: '>=8'} - text-decoder@1.1.0: - resolution: {integrity: sha512-TmLJNj6UgX8xcUZo4UDStGQtDiTzF7BzWlzn9g7UWrjkpHr5uJTK1ld16wZ3LXb2vb6jH8qU89dW5whuMdXYdw==} + text-decoder@1.1.1: + resolution: {integrity: sha512-8zll7REEv4GDD3x4/0pW+ppIxSNs7H1J10IKFZsuOMscumCdM2a+toDGLPA3T+1+fLBql4zbt5z83GEQGGV5VA==} text-hex@1.0.0: resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} @@ -13981,8 +14046,8 @@ packages: resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} engines: {node: '>=18'} - unified@11.0.4: - resolution: {integrity: sha512-apMPnyLjAX+ty4OrNap7yumyVAMlKx5IWU2wlzzUdYJO9A8f1p9m/gywF/GM2ZDFcjQPrx59Mc90KwmxsoklxQ==} + unified@11.0.5: + resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} unique-string@2.0.0: resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==} @@ -14019,16 +14084,16 @@ packages: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} - unplugin@1.10.1: - resolution: {integrity: sha512-d6Mhq8RJeGA8UfKCu54Um4lFA0eSaRa3XxdAJg8tIdxbu1ubW0hBCZUL7yI2uGyYCRndvbK8FLHzqy2XKfeMsg==} + unplugin@1.11.0: + resolution: {integrity: sha512-3r7VWZ/webh0SGgJScpWl2/MRCZK5d3ZYFcNaeci/GQ7Teop7zf0Nl2pUuz7G21BwPd9pcUPOC5KmJ2L3WgC5g==} engines: {node: '>=14.0.0'} untildify@4.0.0: resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} engines: {node: '>=8'} - update-browserslist-db@1.0.16: - resolution: {integrity: sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==} + update-browserslist-db@1.1.0: + resolution: {integrity: sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' @@ -14127,8 +14192,8 @@ packages: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} hasBin: true - v8-to-istanbul@9.2.0: - resolution: {integrity: sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==} + v8-to-istanbul@9.3.0: + resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} engines: {node: '>=10.12.0'} validate-npm-package-license@3.0.4: @@ -14351,8 +14416,8 @@ packages: resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - ws@7.5.9: - resolution: {integrity: sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==} + ws@7.5.10: + resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==} engines: {node: '>=8.3.0'} peerDependencies: bufferutil: ^4.0.1 @@ -14375,8 +14440,8 @@ packages: utf-8-validate: optional: true - ws@8.17.0: - resolution: {integrity: sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==} + ws@8.18.0: + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -14482,16 +14547,16 @@ packages: yauzl@2.10.0: resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} - yjs@13.6.16: - resolution: {integrity: sha512-uEq+n/dFIecBElEdeQea8nDnltScBfuhCSyAxDw4CosveP9Ag0eW6iZi2mdpW7EgxSFT7VXK2MJl3tKaLTmhAQ==} + yjs@13.6.18: + resolution: {integrity: sha512-GBTjO4QCmv2HFKFkYIJl7U77hIB1o22vSCSQD1Ge8ZxWbIbn8AltI4gyXbtL+g5/GJep67HCMq3Y5AmNwDSyEg==} engines: {node: '>=16.0.0', npm: '>=8.0.0'} yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - yocto-queue@1.0.0: - resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} + yocto-queue@1.1.1: + resolution: {integrity: sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==} engines: {node: '>=12.20'} zod@3.22.3: @@ -14535,7 +14600,7 @@ snapshots: '@ariakit/react-core@0.3.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@ariakit/core': 0.3.11 - '@floating-ui/dom': 1.6.5 + '@floating-ui/dom': 1.6.7 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) use-sync-external-store: 1.2.2(react@18.3.1) @@ -14550,6 +14615,14 @@ snapshots: '@automattic/calypso-color-schemes@3.1.3': {} + '@automattic/calypso-config@1.2.0': {} + + '@automattic/calypso-url@1.1.0': + dependencies: + photon: 4.0.0 + transitivePeerDependencies: + - supports-color + '@automattic/color-studio@2.6.0': {} '@automattic/explat-client-react-helpers@0.1.0': @@ -14566,6 +14639,22 @@ snapshots: dependencies: tslib: 2.5.0 + '@automattic/i18n-utils@1.2.3': + dependencies: + '@automattic/calypso-config': 1.2.0 + '@automattic/calypso-url': 1.1.0 + '@automattic/languages': 1.0.0 + '@wordpress/compose': 7.2.0(react@18.3.1) + '@wordpress/i18n': 5.2.0 + react: 18.3.1 + tslib: 2.5.0 + transitivePeerDependencies: + - supports-color + + '@automattic/languages@1.0.0': + dependencies: + tslib: 2.5.0 + '@automattic/popup-monitor@1.0.2': dependencies: events: 3.3.0 @@ -15798,7 +15887,7 @@ snapshots: eslint: 8.57.0 eslint-visitor-keys: 3.4.3 - '@eslint-community/regexpp@4.10.1': {} + '@eslint-community/regexpp@4.11.0': {} '@eslint/eslintrc@2.1.4': dependencies: @@ -15820,22 +15909,22 @@ snapshots: '@fastify/busboy@2.1.1': {} - '@floating-ui/core@1.6.2': + '@floating-ui/core@1.6.4': dependencies: - '@floating-ui/utils': 0.2.2 + '@floating-ui/utils': 0.2.4 - '@floating-ui/dom@1.6.5': + '@floating-ui/dom@1.6.7': dependencies: - '@floating-ui/core': 1.6.2 - '@floating-ui/utils': 0.2.2 + '@floating-ui/core': 1.6.4 + '@floating-ui/utils': 0.2.4 - '@floating-ui/react-dom@2.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@floating-ui/react-dom@2.1.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@floating-ui/dom': 1.6.5 + '@floating-ui/dom': 1.6.7 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@floating-ui/utils@0.2.2': {} + '@floating-ui/utils@0.2.4': {} '@hapi/hoek@9.3.0': {} @@ -15877,7 +15966,7 @@ snapshots: '@jest/console@29.7.0': dependencies: '@jest/types': 29.6.3 - '@types/node': 20.14.2 + '@types/node': 20.14.10 chalk: 4.1.2 jest-message-util: 29.7.0 jest-util: 29.7.0 @@ -15890,14 +15979,14 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.14.2 + '@types/node': 20.14.10 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.9.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.14.2) + jest-config: 29.7.0(@types/node@20.14.10) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -15926,7 +16015,7 @@ snapshots: dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.14.2 + '@types/node': 20.14.10 jest-mock: 29.7.0 '@jest/expect-utils@29.7.0': @@ -15944,7 +16033,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 20.14.2 + '@types/node': 20.14.10 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -15975,14 +16064,14 @@ snapshots: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.25 - '@types/node': 20.14.2 + '@types/node': 20.14.10 chalk: 4.1.2 collect-v8-coverage: 1.0.2 exit: 0.1.2 glob: 7.2.3 graceful-fs: 4.2.11 istanbul-lib-coverage: 3.2.2 - istanbul-lib-instrument: 6.0.2 + istanbul-lib-instrument: 6.0.3 istanbul-lib-report: 3.0.1 istanbul-lib-source-maps: 4.0.1 istanbul-reports: 3.1.7 @@ -15992,7 +16081,7 @@ snapshots: slash: 3.0.0 string-length: 4.0.2 strip-ansi: 6.0.1 - v8-to-istanbul: 9.2.0 + v8-to-istanbul: 9.3.0 transitivePeerDependencies: - supports-color @@ -16045,7 +16134,7 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 20.14.2 + '@types/node': 20.14.10 '@types/yargs': 17.0.32 chalk: 4.1.2 @@ -16214,16 +16303,16 @@ snapshots: '@pkgr/core@0.1.1': {} - '@playwright/test@1.44.1': + '@playwright/test@1.45.1': dependencies: - playwright: 1.44.1 + playwright: 1.45.1 - '@preact/signals-core@1.6.0': {} + '@preact/signals-core@1.7.0': {} - '@preact/signals@1.2.3(preact@10.22.0)': + '@preact/signals@1.3.0(preact@10.22.1)': dependencies: - '@preact/signals-core': 1.6.0 - preact: 10.22.0 + '@preact/signals-core': 1.7.0 + preact: 10.22.1 '@puppeteer/browsers@2.2.2': dependencies: @@ -16255,25 +16344,21 @@ snapshots: dependencies: '@babel/runtime': 7.24.7 - '@radix-ui/primitive@1.0.1': - dependencies: - '@babel/runtime': 7.24.7 + '@radix-ui/primitive@1.1.0': {} '@radix-ui/react-compose-refs@1.0.0(react@18.3.1)': dependencies: '@babel/runtime': 7.24.7 react: 18.3.1 - '@radix-ui/react-compose-refs@1.0.1(@types/react@18.3.1)(react@18.3.1)': + '@radix-ui/react-compose-refs@1.1.0(@types/react@18.3.1)(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 react: 18.3.1 optionalDependencies: '@types/react': 18.3.1 - '@radix-ui/react-compose-refs@1.0.1(react@18.3.1)': + '@radix-ui/react-compose-refs@1.1.0(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 react: 18.3.1 '@radix-ui/react-context@1.0.0(react@18.3.1)': @@ -16281,16 +16366,14 @@ snapshots: '@babel/runtime': 7.24.7 react: 18.3.1 - '@radix-ui/react-context@1.0.1(@types/react@18.3.1)(react@18.3.1)': + '@radix-ui/react-context@1.1.0(@types/react@18.3.1)(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 react: 18.3.1 optionalDependencies: '@types/react': 18.3.1 - '@radix-ui/react-context@1.0.1(react@18.3.1)': + '@radix-ui/react-context@1.1.0(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 react: 18.3.1 '@radix-ui/react-dialog@1.0.0(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': @@ -16337,70 +16420,67 @@ snapshots: transitivePeerDependencies: - '@types/react' - '@radix-ui/react-dialog@1.0.5(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.1)(react@18.3.1) - '@radix-ui/react-context': 1.0.1(@types/react@18.3.1)(react@18.3.1) - '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.3.1)(react@18.3.1) - '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-id': 1.0.1(@types/react@18.3.1)(react@18.3.1) - '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-slot': 1.0.2(@types/react@18.3.1)(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.3.1)(react@18.3.1) + '@radix-ui/react-dialog@1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.1)(react@18.3.1) + '@radix-ui/react-context': 1.1.0(@types/react@18.3.1)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-focus-guards': 1.1.0(@types/react@18.3.1)(react@18.3.1) + '@radix-ui/react-focus-scope': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.0(@types/react@18.3.1)(react@18.3.1) + '@radix-ui/react-portal': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.1.0(@types/react@18.3.1)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.1)(react@18.3.1) aria-hidden: 1.2.4 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-remove-scroll: 2.5.5(@types/react@18.3.1)(react@18.3.1) + react-remove-scroll: 2.5.7(@types/react@18.3.1)(react@18.3.1) optionalDependencies: '@types/react': 18.3.1 '@types/react-dom': 18.3.0 - '@radix-ui/react-dialog@1.0.5(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.1)(react@18.3.1) - '@radix-ui/react-context': 1.0.1(@types/react@18.3.1)(react@18.3.1) - '@radix-ui/react-dismissable-layer': 1.0.5(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.3.1)(react@18.3.1) - '@radix-ui/react-focus-scope': 1.0.4(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-id': 1.0.1(@types/react@18.3.1)(react@18.3.1) - '@radix-ui/react-portal': 1.0.4(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-presence': 1.0.1(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 1.0.3(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-slot': 1.0.2(@types/react@18.3.1)(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.3.1)(react@18.3.1) + '@radix-ui/react-dialog@1.1.1(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.1)(react@18.3.1) + '@radix-ui/react-context': 1.1.0(@types/react@18.3.1)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.0(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-focus-guards': 1.1.0(@types/react@18.3.1)(react@18.3.1) + '@radix-ui/react-focus-scope': 1.1.0(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.0(@types/react@18.3.1)(react@18.3.1) + '@radix-ui/react-portal': 1.1.1(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.0(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.1.0(@types/react@18.3.1)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.1)(react@18.3.1) aria-hidden: 1.2.4 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-remove-scroll: 2.5.5(@types/react@18.3.1)(react@18.3.1) + react-remove-scroll: 2.5.7(@types/react@18.3.1)(react@18.3.1) optionalDependencies: '@types/react': 18.3.1 - '@radix-ui/react-dialog@1.0.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(react@18.3.1) - '@radix-ui/react-context': 1.0.1(react@18.3.1) - '@radix-ui/react-dismissable-layer': 1.0.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-focus-guards': 1.0.1(react@18.3.1) - '@radix-ui/react-focus-scope': 1.0.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-id': 1.0.1(react@18.3.1) - '@radix-ui/react-portal': 1.0.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-presence': 1.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 1.0.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-slot': 1.0.2(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.0.1(react@18.3.1) + '@radix-ui/react-dialog@1.1.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-compose-refs': 1.1.0(react@18.3.1) + '@radix-ui/react-context': 1.1.0(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-focus-guards': 1.1.0(react@18.3.1) + '@radix-ui/react-focus-scope': 1.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.0(react@18.3.1) + '@radix-ui/react-portal': 1.1.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.1.0(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(react@18.3.1) aria-hidden: 1.2.4 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-remove-scroll: 2.5.5(react@18.3.1) + react-remove-scroll: 2.5.7(react@18.3.1) '@radix-ui/react-dismissable-layer@1.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: @@ -16413,41 +16493,38 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@radix-ui/react-dismissable-layer@1.0.5(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-dismissable-layer@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.1)(react@18.3.1) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.1)(react@18.3.1) - '@radix-ui/react-use-escape-keydown': 1.0.3(@types/react@18.3.1)(react@18.3.1) + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.1)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.1)(react@18.3.1) + '@radix-ui/react-use-escape-keydown': 1.1.0(@types/react@18.3.1)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: '@types/react': 18.3.1 '@types/react-dom': 18.3.0 - '@radix-ui/react-dismissable-layer@1.0.5(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-dismissable-layer@1.1.0(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.1)(react@18.3.1) - '@radix-ui/react-primitive': 1.0.3(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.1)(react@18.3.1) - '@radix-ui/react-use-escape-keydown': 1.0.3(@types/react@18.3.1)(react@18.3.1) + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.1)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.1)(react@18.3.1) + '@radix-ui/react-use-escape-keydown': 1.1.0(@types/react@18.3.1)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: '@types/react': 18.3.1 - '@radix-ui/react-dismissable-layer@1.0.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-dismissable-layer@1.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(react@18.3.1) - '@radix-ui/react-primitive': 1.0.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-callback-ref': 1.0.1(react@18.3.1) - '@radix-ui/react-use-escape-keydown': 1.0.3(react@18.3.1) + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-compose-refs': 1.1.0(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(react@18.3.1) + '@radix-ui/react-use-escape-keydown': 1.1.0(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -16456,16 +16533,14 @@ snapshots: '@babel/runtime': 7.24.7 react: 18.3.1 - '@radix-ui/react-focus-guards@1.0.1(@types/react@18.3.1)(react@18.3.1)': + '@radix-ui/react-focus-guards@1.1.0(@types/react@18.3.1)(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 react: 18.3.1 optionalDependencies: '@types/react': 18.3.1 - '@radix-ui/react-focus-guards@1.0.1(react@18.3.1)': + '@radix-ui/react-focus-guards@1.1.0(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 react: 18.3.1 '@radix-ui/react-focus-scope@1.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': @@ -16477,35 +16552,32 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@radix-ui/react-focus-scope@1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-focus-scope@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.1)(react@18.3.1) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.1)(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.1)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.1)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: '@types/react': 18.3.1 '@types/react-dom': 18.3.0 - '@radix-ui/react-focus-scope@1.0.4(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-focus-scope@1.1.0(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.1)(react@18.3.1) - '@radix-ui/react-primitive': 1.0.3(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.1)(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.1)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.1)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: '@types/react': 18.3.1 - '@radix-ui/react-focus-scope@1.0.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-focus-scope@1.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/react-compose-refs': 1.0.1(react@18.3.1) - '@radix-ui/react-primitive': 1.0.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-callback-ref': 1.0.1(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.0(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -16515,18 +16587,16 @@ snapshots: '@radix-ui/react-use-layout-effect': 1.0.0(react@18.3.1) react: 18.3.1 - '@radix-ui/react-id@1.0.1(@types/react@18.3.1)(react@18.3.1)': + '@radix-ui/react-id@1.1.0(@types/react@18.3.1)(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.3.1)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.1)(react@18.3.1) react: 18.3.1 optionalDependencies: '@types/react': 18.3.1 - '@radix-ui/react-id@1.0.1(react@18.3.1)': + '@radix-ui/react-id@1.1.0(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/react-use-layout-effect': 1.0.1(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(react@18.3.1) react: 18.3.1 '@radix-ui/react-portal@1.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': @@ -16536,29 +16606,29 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@radix-ui/react-portal@1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-portal@1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.1)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: '@types/react': 18.3.1 '@types/react-dom': 18.3.0 - '@radix-ui/react-portal@1.0.4(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-portal@1.1.1(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/react-primitive': 1.0.3(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.1)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: '@types/react': 18.3.1 - '@radix-ui/react-portal@1.0.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-portal@1.1.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/react-primitive': 1.0.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -16570,32 +16640,29 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@radix-ui/react-presence@1.0.1(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-presence@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.1)(react@18.3.1) - '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.3.1)(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.1)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.1)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: '@types/react': 18.3.1 '@types/react-dom': 18.3.0 - '@radix-ui/react-presence@1.0.1(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-presence@1.1.0(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.1)(react@18.3.1) - '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.3.1)(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.1)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.1)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: '@types/react': 18.3.1 - '@radix-ui/react-presence@1.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-presence@1.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/react-compose-refs': 1.0.1(react@18.3.1) - '@radix-ui/react-use-layout-effect': 1.0.1(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.0(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -16606,29 +16673,26 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@radix-ui/react-primitive@1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-primitive@2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/react-slot': 1.0.2(@types/react@18.3.1)(react@18.3.1) + '@radix-ui/react-slot': 1.1.0(@types/react@18.3.1)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: '@types/react': 18.3.1 '@types/react-dom': 18.3.0 - '@radix-ui/react-primitive@1.0.3(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-primitive@2.0.0(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/react-slot': 1.0.2(@types/react@18.3.1)(react@18.3.1) + '@radix-ui/react-slot': 1.1.0(@types/react@18.3.1)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: '@types/react': 18.3.1 - '@radix-ui/react-primitive@1.0.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-primitive@2.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/react-slot': 1.0.2(react@18.3.1) + '@radix-ui/react-slot': 1.1.0(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -16638,18 +16702,16 @@ snapshots: '@radix-ui/react-compose-refs': 1.0.0(react@18.3.1) react: 18.3.1 - '@radix-ui/react-slot@1.0.2(@types/react@18.3.1)(react@18.3.1)': + '@radix-ui/react-slot@1.1.0(@types/react@18.3.1)(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.1)(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.1)(react@18.3.1) react: 18.3.1 optionalDependencies: '@types/react': 18.3.1 - '@radix-ui/react-slot@1.0.2(react@18.3.1)': + '@radix-ui/react-slot@1.1.0(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/react-compose-refs': 1.0.1(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.0(react@18.3.1) react: 18.3.1 '@radix-ui/react-use-callback-ref@1.0.0(react@18.3.1)': @@ -16657,16 +16719,14 @@ snapshots: '@babel/runtime': 7.24.7 react: 18.3.1 - '@radix-ui/react-use-callback-ref@1.0.1(@types/react@18.3.1)(react@18.3.1)': + '@radix-ui/react-use-callback-ref@1.1.0(@types/react@18.3.1)(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 react: 18.3.1 optionalDependencies: '@types/react': 18.3.1 - '@radix-ui/react-use-callback-ref@1.0.1(react@18.3.1)': + '@radix-ui/react-use-callback-ref@1.1.0(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 react: 18.3.1 '@radix-ui/react-use-controllable-state@1.0.0(react@18.3.1)': @@ -16675,18 +16735,16 @@ snapshots: '@radix-ui/react-use-callback-ref': 1.0.0(react@18.3.1) react: 18.3.1 - '@radix-ui/react-use-controllable-state@1.0.1(@types/react@18.3.1)(react@18.3.1)': + '@radix-ui/react-use-controllable-state@1.1.0(@types/react@18.3.1)(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.1)(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.1)(react@18.3.1) react: 18.3.1 optionalDependencies: '@types/react': 18.3.1 - '@radix-ui/react-use-controllable-state@1.0.1(react@18.3.1)': + '@radix-ui/react-use-controllable-state@1.1.0(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/react-use-callback-ref': 1.0.1(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(react@18.3.1) react: 18.3.1 '@radix-ui/react-use-escape-keydown@1.0.0(react@18.3.1)': @@ -16695,18 +16753,16 @@ snapshots: '@radix-ui/react-use-callback-ref': 1.0.0(react@18.3.1) react: 18.3.1 - '@radix-ui/react-use-escape-keydown@1.0.3(@types/react@18.3.1)(react@18.3.1)': + '@radix-ui/react-use-escape-keydown@1.1.0(@types/react@18.3.1)(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.1)(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.1)(react@18.3.1) react: 18.3.1 optionalDependencies: '@types/react': 18.3.1 - '@radix-ui/react-use-escape-keydown@1.0.3(react@18.3.1)': + '@radix-ui/react-use-escape-keydown@1.1.0(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/react-use-callback-ref': 1.0.1(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(react@18.3.1) react: 18.3.1 '@radix-ui/react-use-layout-effect@1.0.0(react@18.3.1)': @@ -16714,16 +16770,14 @@ snapshots: '@babel/runtime': 7.24.7 react: 18.3.1 - '@radix-ui/react-use-layout-effect@1.0.1(@types/react@18.3.1)(react@18.3.1)': + '@radix-ui/react-use-layout-effect@1.1.0(@types/react@18.3.1)(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 react: 18.3.1 optionalDependencies: '@types/react': 18.3.1 - '@radix-ui/react-use-layout-effect@1.0.1(react@18.3.1)': + '@radix-ui/react-use-layout-effect@1.1.0(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 react: 18.3.1 '@react-spring/animated@9.7.3(react@18.3.1)': @@ -16829,6 +16883,11 @@ snapshots: picomatch: 2.2.3 rollup: 2.79.1 + '@rollup/pluginutils@4.2.1': + dependencies: + estree-walker: 2.0.2 + picomatch: 2.2.3 + '@rollup/pluginutils@5.1.0(rollup@2.79.1)': dependencies: '@types/estree': 1.0.5 @@ -16930,7 +16989,7 @@ snapshots: '@slack/logger@3.0.0': dependencies: - '@types/node': 20.14.2 + '@types/node': 20.14.10 '@slack/types@2.12.0': {} @@ -16939,7 +16998,7 @@ snapshots: '@slack/logger': 3.0.0 '@slack/types': 2.12.0 '@types/is-stream': 1.1.0 - '@types/node': 20.14.2 + '@types/node': 20.14.10 axios: 1.6.8 eventemitter3: 3.1.2 form-data: 2.5.1 @@ -17078,7 +17137,7 @@ snapshots: '@storybook/client-logger': 8.1.6 '@storybook/components': 8.1.6(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@storybook/core-events': 8.1.6 - '@storybook/csf': 0.1.8 + '@storybook/csf': 0.1.11 '@storybook/docs-tools': 8.1.6 '@storybook/global': 5.0.0 '@storybook/icons': 1.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -17086,7 +17145,7 @@ snapshots: '@storybook/preview-api': 8.1.6 '@storybook/theming': 8.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@storybook/types': 8.1.6 - '@types/lodash': 4.17.5 + '@types/lodash': 4.17.6 color-convert: 2.0.1 dequal: 2.0.3 lodash: 4.17.21 @@ -17114,7 +17173,7 @@ snapshots: '@storybook/client-logger': 8.1.6 '@storybook/components': 8.1.6(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@storybook/core-events': 8.1.6 - '@storybook/csf': 0.1.8 + '@storybook/csf': 0.1.11 '@storybook/docs-tools': 8.1.6 '@storybook/global': 5.0.0 '@storybook/icons': 1.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -17122,7 +17181,7 @@ snapshots: '@storybook/preview-api': 8.1.6 '@storybook/theming': 8.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@storybook/types': 8.1.6 - '@types/lodash': 4.17.5 + '@types/lodash': 4.17.6 color-convert: 2.0.1 dequal: 2.0.3 lodash: 4.17.21 @@ -17150,7 +17209,7 @@ snapshots: '@storybook/client-logger': 8.1.6 '@storybook/components': 8.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@storybook/core-events': 8.1.6 - '@storybook/csf': 0.1.8 + '@storybook/csf': 0.1.11 '@storybook/docs-tools': 8.1.6 '@storybook/global': 5.0.0 '@storybook/icons': 1.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -17158,7 +17217,7 @@ snapshots: '@storybook/preview-api': 8.1.6 '@storybook/theming': 8.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@storybook/types': 8.1.6 - '@types/lodash': 4.17.5 + '@types/lodash': 4.17.6 color-convert: 2.0.1 dequal: 2.0.3 lodash: 4.17.21 @@ -17211,14 +17270,14 @@ snapshots: '@storybook/node-logger': 8.1.6 '@storybook/preview': 8.1.6 '@storybook/preview-api': 8.1.6 - '@types/node': 18.19.34 + '@types/node': 18.19.39 '@types/semver': 7.5.8 browser-assert: 1.2.1 case-sensitive-paths-webpack-plugin: 2.4.0 cjs-module-lexer: 1.3.1 constants-browserify: 1.0.0 css-loader: 6.11.0(webpack@5.76.0(webpack-cli@4.9.1)) - es-module-lexer: 1.5.3 + es-module-lexer: 1.5.4 express: 4.19.2 fork-ts-checker-webpack-plugin: 8.0.0(typescript@5.0.4)(webpack@5.76.0(webpack-cli@4.9.1)) fs-extra: 11.2.0 @@ -17249,6 +17308,14 @@ snapshots: - uglify-js - webpack-cli + '@storybook/channels@8.1.11': + dependencies: + '@storybook/client-logger': 8.1.11 + '@storybook/core-events': 8.1.11 + '@storybook/global': 5.0.0 + telejson: 7.2.0 + tiny-invariant: 1.3.3 + '@storybook/channels@8.1.6': dependencies: '@storybook/client-logger': 8.1.6 @@ -17283,7 +17350,7 @@ snapshots: fs-extra: 11.2.0 get-npm-tarball-url: 2.1.0 giget: 1.2.3 - globby: 14.0.1 + globby: 14.0.2 jscodeshift: 0.15.2 leven: 3.1.0 ora: 5.4.1 @@ -17304,6 +17371,10 @@ snapshots: - supports-color - utf-8-validate + '@storybook/client-logger@8.1.11': + dependencies: + '@storybook/global': 5.0.0 + '@storybook/client-logger@8.1.6': dependencies: '@storybook/global': 5.0.0 @@ -17313,13 +17384,13 @@ snapshots: '@babel/core': 7.24.7 '@babel/preset-env': 7.24.7(@babel/core@7.24.7) '@babel/types': 7.24.7 - '@storybook/csf': 0.1.8 + '@storybook/csf': 0.1.11 '@storybook/csf-tools': 8.1.6 '@storybook/node-logger': 8.1.6 '@storybook/types': 8.1.6 '@types/cross-spawn': 6.0.6 cross-spawn: 7.0.3 - globby: 14.0.1 + globby: 14.0.2 jscodeshift: 0.15.2(@babel/preset-env@7.24.7(@babel/core@7.24.7)) lodash: 4.17.21 prettier: 3.3.2 @@ -17330,10 +17401,10 @@ snapshots: '@storybook/components@8.1.6(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@radix-ui/react-dialog': 1.0.5(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-slot': 1.0.2(@types/react@18.3.1)(react@18.3.1) + '@radix-ui/react-dialog': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.1.0(@types/react@18.3.1)(react@18.3.1) '@storybook/client-logger': 8.1.6 - '@storybook/csf': 0.1.8 + '@storybook/csf': 0.1.11 '@storybook/global': 5.0.0 '@storybook/icons': 1.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@storybook/theming': 8.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -17348,10 +17419,10 @@ snapshots: '@storybook/components@8.1.6(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@radix-ui/react-dialog': 1.0.5(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-slot': 1.0.2(@types/react@18.3.1)(react@18.3.1) + '@radix-ui/react-dialog': 1.1.1(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.1.0(@types/react@18.3.1)(react@18.3.1) '@storybook/client-logger': 8.1.6 - '@storybook/csf': 0.1.8 + '@storybook/csf': 0.1.11 '@storybook/global': 5.0.0 '@storybook/icons': 1.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@storybook/theming': 8.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -17366,10 +17437,10 @@ snapshots: '@storybook/components@8.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@radix-ui/react-dialog': 1.0.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-slot': 1.0.2(react@18.3.1) + '@radix-ui/react-dialog': 1.1.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.1.0(react@18.3.1) '@storybook/client-logger': 8.1.6 - '@storybook/csf': 0.1.8 + '@storybook/csf': 0.1.11 '@storybook/global': 5.0.0 '@storybook/icons': 1.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@storybook/theming': 8.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -17382,6 +17453,41 @@ snapshots: - '@types/react' - '@types/react-dom' + '@storybook/core-common@8.1.11': + dependencies: + '@storybook/core-events': 8.1.11 + '@storybook/csf-tools': 8.1.11 + '@storybook/node-logger': 8.1.11 + '@storybook/types': 8.1.11 + '@yarnpkg/fslib': 2.10.3 + '@yarnpkg/libzip': 2.3.0 + chalk: 4.1.2 + cross-spawn: 7.0.3 + esbuild: 0.20.2 + esbuild-register: 3.5.0(esbuild@0.20.2) + execa: 5.1.1 + file-system-cache: 2.3.0 + find-cache-dir: 3.3.2 + find-up: 5.0.0 + fs-extra: 11.2.0 + glob: 10.4.1 + handlebars: 4.7.8 + lazy-universal-dotenv: 4.0.0 + node-fetch: 2.6.7 + picomatch: 2.3.1 + pkg-dir: 5.0.0 + prettier-fallback: prettier@3.3.2 + pretty-hrtime: 1.0.3 + resolve-from: 5.0.0 + semver: 7.5.2 + tempy: 3.1.0 + tiny-invariant: 1.3.3 + ts-dedent: 2.2.0 + util: 0.12.5 + transitivePeerDependencies: + - encoding + - supports-color + '@storybook/core-common@8.1.6': dependencies: '@storybook/core-events': 8.1.6 @@ -17491,9 +17597,14 @@ snapshots: - encoding - supports-color + '@storybook/core-events@8.1.11': + dependencies: + '@storybook/csf': 0.1.11 + ts-dedent: 2.2.0 + '@storybook/core-events@8.1.6': dependencies: - '@storybook/csf': 0.1.8 + '@storybook/csf': 0.1.11 ts-dedent: 2.2.0 '@storybook/core-server@8.1.6(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': @@ -17506,7 +17617,7 @@ snapshots: '@storybook/channels': 8.1.6 '@storybook/core-common': 8.1.6(prettier@3.3.2) '@storybook/core-events': 8.1.6 - '@storybook/csf': 0.1.8 + '@storybook/csf': 0.1.11 '@storybook/csf-tools': 8.1.6 '@storybook/docs-mdx': 3.1.0-next.0 '@storybook/global': 5.0.0 @@ -17518,7 +17629,7 @@ snapshots: '@storybook/types': 8.1.6 '@types/detect-port': 1.3.5 '@types/diff': 5.2.1 - '@types/node': 18.19.34 + '@types/node': 18.19.39 '@types/pretty-hrtime': 1.0.3 '@types/semver': 7.5.8 better-opn: 3.0.2 @@ -17529,7 +17640,7 @@ snapshots: diff: 5.2.0 express: 4.19.2 fs-extra: 11.2.0 - globby: 14.0.1 + globby: 14.0.2 lodash: 4.17.21 open: 8.4.2 pretty-hrtime: 1.0.3 @@ -17542,7 +17653,7 @@ snapshots: util: 0.12.5 util-deprecate: 1.0.2 watchpack: 2.4.1 - ws: 8.17.0 + ws: 8.18.0 transitivePeerDependencies: - bufferutil - encoding @@ -17557,7 +17668,7 @@ snapshots: '@storybook/core-common': 8.1.6 '@storybook/node-logger': 8.1.6 '@storybook/types': 8.1.6 - '@types/node': 18.19.34 + '@types/node': 18.19.39 ts-dedent: 2.2.0 transitivePeerDependencies: - encoding @@ -17567,7 +17678,21 @@ snapshots: '@storybook/csf-plugin@8.1.6': dependencies: '@storybook/csf-tools': 8.1.6 - unplugin: 1.10.1 + unplugin: 1.11.0 + transitivePeerDependencies: + - supports-color + + '@storybook/csf-tools@8.1.11': + dependencies: + '@babel/generator': 7.24.7 + '@babel/parser': 7.24.7 + '@babel/traverse': 7.24.7 + '@babel/types': 7.24.7 + '@storybook/csf': 0.1.11 + '@storybook/types': 8.1.11 + fs-extra: 11.2.0 + recast: 0.23.9 + ts-dedent: 2.2.0 transitivePeerDependencies: - supports-color @@ -17577,7 +17702,7 @@ snapshots: '@babel/parser': 7.24.7 '@babel/traverse': 7.24.7 '@babel/types': 7.24.7 - '@storybook/csf': 0.1.8 + '@storybook/csf': 0.1.11 '@storybook/types': 8.1.6 fs-extra: 11.2.0 recast: 0.23.9 @@ -17585,7 +17710,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@storybook/csf@0.1.8': + '@storybook/csf@0.1.11': dependencies: type-fest: 2.19.0 @@ -17633,7 +17758,7 @@ snapshots: '@storybook/channels': 8.1.6 '@storybook/client-logger': 8.1.6 '@storybook/core-events': 8.1.6 - '@storybook/csf': 0.1.8 + '@storybook/csf': 0.1.11 '@storybook/global': 5.0.0 '@storybook/icons': 1.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@storybook/router': 8.1.6 @@ -17651,6 +17776,8 @@ snapshots: '@storybook/manager@8.1.6': {} + '@storybook/node-logger@8.1.11': {} + '@storybook/node-logger@8.1.6': {} '@storybook/preset-react-webpack@8.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.0.4)(webpack-cli@4.9.1(webpack@5.76.0))': @@ -17660,7 +17787,7 @@ snapshots: '@storybook/node-logger': 8.1.6 '@storybook/react': 8.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.0.4) '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@5.0.4)(webpack@5.76.0(webpack-cli@4.9.1)) - '@types/node': 18.19.34 + '@types/node': 18.19.39 '@types/semver': 7.5.8 find-up: 5.0.0 fs-extra: 11.2.0 @@ -17688,7 +17815,7 @@ snapshots: '@storybook/channels': 8.1.6 '@storybook/client-logger': 8.1.6 '@storybook/core-events': 8.1.6 - '@storybook/csf': 0.1.8 + '@storybook/csf': 0.1.11 '@storybook/global': 5.0.0 '@storybook/types': 8.1.6 '@types/qs': 6.9.15 @@ -17727,7 +17854,7 @@ snapshots: '@storybook/preset-react-webpack': 8.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.0.4)(webpack-cli@4.9.1(webpack@5.76.0)) '@storybook/react': 8.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.0.4) '@storybook/types': 8.1.6 - '@types/node': 18.19.34 + '@types/node': 18.19.39 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: @@ -17752,7 +17879,7 @@ snapshots: '@storybook/types': 8.1.6 '@types/escodegen': 0.0.6 '@types/estree': 0.0.51 - '@types/node': 18.19.34 + '@types/node': 18.19.39 acorn: 7.4.1 acorn-jsx: 5.3.2(acorn@7.4.1) acorn-walk: 7.2.0 @@ -17784,7 +17911,7 @@ snapshots: '@storybook/types': 8.1.6 '@types/escodegen': 0.0.6 '@types/estree': 0.0.51 - '@types/node': 18.19.34 + '@types/node': 18.19.39 acorn: 7.4.1 acorn-jsx: 5.3.2(acorn@7.4.1) acorn-walk: 7.2.0 @@ -17814,7 +17941,7 @@ snapshots: '@storybook/source-loader@8.1.6': dependencies: - '@storybook/csf': 0.1.8 + '@storybook/csf': 0.1.11 '@storybook/types': 8.1.6 estraverse: 5.3.0 lodash: 4.17.21 @@ -17842,12 +17969,12 @@ snapshots: '@babel/template': 7.24.7 '@babel/types': 7.24.7 '@jest/types': 29.6.3 - '@storybook/core-common': 8.1.6 - '@storybook/csf': 0.1.8 - '@storybook/csf-tools': 8.1.6 + '@storybook/core-common': 8.1.11 + '@storybook/csf': 0.1.11 + '@storybook/csf-tools': 8.1.11 '@storybook/preview-api': 8.1.6 - '@swc/core': 1.5.27 - '@swc/jest': 0.2.36(@swc/core@1.5.27) + '@swc/core': 1.6.13 + '@swc/jest': 0.2.36(@swc/core@1.6.13) expect-playwright: 0.8.0 jest: 29.7.0 jest-circus: 29.7.0 @@ -17858,7 +17985,7 @@ snapshots: jest-serializer-html: 7.1.0 jest-watch-typeahead: 2.2.2(jest@29.7.0) nyc: 15.1.0 - playwright: 1.44.1 + playwright: 1.45.1 transitivePeerDependencies: - '@swc/helpers' - '@types/node' @@ -17880,6 +18007,12 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + '@storybook/types@8.1.11': + dependencies: + '@storybook/channels': 8.1.11 + '@types/express': 4.17.21 + file-system-cache: 2.3.0 + '@storybook/types@8.1.6': dependencies: '@storybook/channels': 8.1.6 @@ -17977,62 +18110,62 @@ snapshots: - supports-color - typescript - '@swc/core-darwin-arm64@1.5.27': + '@swc/core-darwin-arm64@1.6.13': optional: true - '@swc/core-darwin-x64@1.5.27': + '@swc/core-darwin-x64@1.6.13': optional: true - '@swc/core-linux-arm-gnueabihf@1.5.27': + '@swc/core-linux-arm-gnueabihf@1.6.13': optional: true - '@swc/core-linux-arm64-gnu@1.5.27': + '@swc/core-linux-arm64-gnu@1.6.13': optional: true - '@swc/core-linux-arm64-musl@1.5.27': + '@swc/core-linux-arm64-musl@1.6.13': optional: true - '@swc/core-linux-x64-gnu@1.5.27': + '@swc/core-linux-x64-gnu@1.6.13': optional: true - '@swc/core-linux-x64-musl@1.5.27': + '@swc/core-linux-x64-musl@1.6.13': optional: true - '@swc/core-win32-arm64-msvc@1.5.27': + '@swc/core-win32-arm64-msvc@1.6.13': optional: true - '@swc/core-win32-ia32-msvc@1.5.27': + '@swc/core-win32-ia32-msvc@1.6.13': optional: true - '@swc/core-win32-x64-msvc@1.5.27': + '@swc/core-win32-x64-msvc@1.6.13': optional: true - '@swc/core@1.5.27': + '@swc/core@1.6.13': dependencies: '@swc/counter': 0.1.3 - '@swc/types': 0.1.8 + '@swc/types': 0.1.9 optionalDependencies: - '@swc/core-darwin-arm64': 1.5.27 - '@swc/core-darwin-x64': 1.5.27 - '@swc/core-linux-arm-gnueabihf': 1.5.27 - '@swc/core-linux-arm64-gnu': 1.5.27 - '@swc/core-linux-arm64-musl': 1.5.27 - '@swc/core-linux-x64-gnu': 1.5.27 - '@swc/core-linux-x64-musl': 1.5.27 - '@swc/core-win32-arm64-msvc': 1.5.27 - '@swc/core-win32-ia32-msvc': 1.5.27 - '@swc/core-win32-x64-msvc': 1.5.27 + '@swc/core-darwin-arm64': 1.6.13 + '@swc/core-darwin-x64': 1.6.13 + '@swc/core-linux-arm-gnueabihf': 1.6.13 + '@swc/core-linux-arm64-gnu': 1.6.13 + '@swc/core-linux-arm64-musl': 1.6.13 + '@swc/core-linux-x64-gnu': 1.6.13 + '@swc/core-linux-x64-musl': 1.6.13 + '@swc/core-win32-arm64-msvc': 1.6.13 + '@swc/core-win32-ia32-msvc': 1.6.13 + '@swc/core-win32-x64-msvc': 1.6.13 '@swc/counter@0.1.3': {} - '@swc/jest@0.2.36(@swc/core@1.5.27)': + '@swc/jest@0.2.36(@swc/core@1.6.13)': dependencies: '@jest/create-cache-key-function': 29.7.0 - '@swc/core': 1.5.27 + '@swc/core': 1.6.13 '@swc/counter': 0.1.3 - jsonc-parser: 3.2.1 + jsonc-parser: 3.3.1 - '@swc/types@0.1.8': + '@swc/types@0.1.9': dependencies: '@swc/counter': 0.1.3 @@ -18123,10 +18256,10 @@ snapshots: optionalDependencies: jest: 29.7.0 - '@testing-library/preact@3.2.3(preact@10.12.1)': + '@testing-library/preact@3.2.4(preact@10.22.1)': dependencies: '@testing-library/dom': 8.20.1 - preact: 10.12.1 + preact: 10.22.1 '@testing-library/react@15.0.7(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: @@ -18182,17 +18315,17 @@ snapshots: '@types/body-parser@1.19.5': dependencies: '@types/connect': 3.4.38 - '@types/node': 20.14.2 + '@types/node': 20.14.10 '@types/connect@3.4.38': dependencies: - '@types/node': 20.14.2 + '@types/node': 20.14.10 '@types/cookie@0.6.0': {} '@types/cross-spawn@6.0.6': dependencies: - '@types/node': 20.14.2 + '@types/node': 20.14.10 '@types/debug@4.1.12': dependencies: @@ -18228,9 +18361,9 @@ snapshots: '@types/estree@1.0.5': {} - '@types/express-serve-static-core@4.19.3': + '@types/express-serve-static-core@4.19.5': dependencies: - '@types/node': 20.14.2 + '@types/node': 20.14.10 '@types/qs': 6.9.15 '@types/range-parser': 1.2.7 '@types/send': 0.17.4 @@ -18238,18 +18371,18 @@ snapshots: '@types/express@4.17.21': dependencies: '@types/body-parser': 1.19.5 - '@types/express-serve-static-core': 4.19.3 + '@types/express-serve-static-core': 4.19.5 '@types/qs': 6.9.15 '@types/serve-static': 1.15.7 '@types/glob@7.2.0': dependencies: '@types/minimatch': 5.1.2 - '@types/node': 20.14.2 + '@types/node': 20.14.10 '@types/graceful-fs@4.1.9': dependencies: - '@types/node': 20.14.2 + '@types/node': 20.14.10 '@types/gradient-parser@0.1.3': {} @@ -18272,7 +18405,7 @@ snapshots: '@types/is-stream@1.1.0': dependencies: - '@types/node': 20.14.2 + '@types/node': 20.14.10 '@types/istanbul-lib-coverage@2.0.6': {} @@ -18295,7 +18428,7 @@ snapshots: '@types/jsdom@20.0.1': dependencies: - '@types/node': 20.14.2 + '@types/node': 20.14.10 '@types/tough-cookie': 4.0.5 parse5: 7.1.2 @@ -18305,7 +18438,7 @@ snapshots: '@types/linkify-it@5.0.0': {} - '@types/lodash@4.17.5': {} + '@types/lodash@4.17.6': {} '@types/markdown-it@14.0.1': dependencies: @@ -18328,11 +18461,11 @@ snapshots: '@types/ms@0.7.34': {} - '@types/node@18.19.34': + '@types/node@18.19.39': dependencies: undici-types: 5.26.5 - '@types/node@20.14.2': + '@types/node@20.14.10': dependencies: undici-types: 5.26.5 @@ -18344,8 +18477,6 @@ snapshots: '@types/prop-types@15.7.12': {} - '@types/pug@2.0.10': {} - '@types/qrcode.react@1.0.5': dependencies: '@types/react': 18.3.1 @@ -18391,7 +18522,7 @@ snapshots: '@types/resolve@1.17.1': dependencies: - '@types/node': 20.14.2 + '@types/node': 20.14.10 '@types/resolve@1.20.6': {} @@ -18402,17 +18533,17 @@ snapshots: '@types/send@0.17.4': dependencies: '@types/mime': 1.3.5 - '@types/node': 20.14.2 + '@types/node': 20.14.10 '@types/serve-static@1.15.7': dependencies: '@types/http-errors': 2.0.4 - '@types/node': 20.14.2 + '@types/node': 20.14.10 '@types/send': 0.17.4 '@types/simple-peer@9.11.8': dependencies: - '@types/node': 20.14.2 + '@types/node': 20.14.10 '@types/sizzle@2.3.8': {} @@ -18434,7 +18565,7 @@ snapshots: '@types/wait-on@5.3.4': dependencies: - '@types/node': 20.14.2 + '@types/node': 20.14.10 '@types/wordpress__block-editor@11.5.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: @@ -18506,12 +18637,12 @@ snapshots: '@types/yauzl@2.10.3': dependencies: - '@types/node': 20.14.2 + '@types/node': 20.14.10 optional: true '@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.0.4))(eslint@8.57.0)(typescript@5.0.4)': dependencies: - '@eslint-community/regexpp': 4.10.1 + '@eslint-community/regexpp': 4.11.0 '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.0.4) '@typescript-eslint/scope-manager': 6.21.0 '@typescript-eslint/type-utils': 6.21.0(eslint@8.57.0)(typescript@5.0.4) @@ -18832,8 +18963,8 @@ snapshots: fast-deep-equal: 3.1.3 memize: 2.1.0 postcss: 8.4.31 - postcss-prefixwrap: 1.48.0(postcss@8.4.31) - postcss-urlrebase: 1.3.0(postcss@8.4.31) + postcss-prefixwrap: 1.49.0(postcss@8.4.31) + postcss-urlrebase: 1.4.0(postcss@8.4.31) react: 18.3.1 react-autosize-textarea: 7.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-dom: 18.3.1(react@18.3.1) @@ -18887,8 +19018,8 @@ snapshots: fast-deep-equal: 3.1.3 memize: 2.1.0 postcss: 8.4.31 - postcss-prefixwrap: 1.48.0(postcss@8.4.31) - postcss-urlrebase: 1.3.0(postcss@8.4.31) + postcss-prefixwrap: 1.49.0(postcss@8.4.31) + postcss-urlrebase: 1.4.0(postcss@8.4.31) react: 18.3.1 react-autosize-textarea: 7.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-dom: 18.3.1(react@18.3.1) @@ -19095,7 +19226,7 @@ snapshots: '@emotion/serialize': 1.1.4 '@emotion/styled': 11.11.5(@emotion/react@11.11.4(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1) '@emotion/utils': 1.2.1 - '@floating-ui/react-dom': 2.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@floating-ui/react-dom': 2.1.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@types/gradient-parser': 0.1.3 '@types/highlight-words-core': 1.2.1 '@use-gesture/react': 10.3.1(react@18.3.1) @@ -19123,7 +19254,7 @@ snapshots: deepmerge: 4.3.1 downshift: 6.1.12(react@18.3.1) fast-deep-equal: 3.1.3 - framer-motion: 11.2.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + framer-motion: 11.2.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1) gradient-parser: 0.1.5 highlight-words-core: 1.2.2 is-plain-object: 5.0.0 @@ -19151,7 +19282,7 @@ snapshots: '@emotion/serialize': 1.1.4 '@emotion/styled': 11.11.5(@emotion/react@11.11.4(react@18.3.1))(react@18.3.1) '@emotion/utils': 1.2.1 - '@floating-ui/react-dom': 2.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@floating-ui/react-dom': 2.1.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@types/gradient-parser': 0.1.3 '@types/highlight-words-core': 1.2.1 '@use-gesture/react': 10.3.1(react@18.3.1) @@ -19179,7 +19310,7 @@ snapshots: deepmerge: 4.3.1 downshift: 6.1.12(react@18.3.1) fast-deep-equal: 3.1.3 - framer-motion: 11.2.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + framer-motion: 11.2.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1) gradient-parser: 0.1.5 highlight-words-core: 1.2.2 is-plain-object: 5.0.0 @@ -19702,9 +19833,9 @@ snapshots: '@wordpress/interactivity@6.2.0': dependencies: - '@preact/signals': 1.2.3(preact@10.22.0) - deepsignal: 1.5.0(@preact/signals@1.2.3(preact@10.22.0))(preact@10.22.0) - preact: 10.22.0 + '@preact/signals': 1.3.0(preact@10.22.1) + deepsignal: 1.5.0(@preact/signals@1.3.0(preact@10.22.1))(preact@10.22.1) + preact: 10.22.1 transitivePeerDependencies: - '@preact/signals-core' - '@preact/signals-react' @@ -20080,10 +20211,10 @@ snapshots: import-locals: 2.0.0 lib0: 0.2.94 simple-peer: 9.11.1 - y-indexeddb: 9.0.12(yjs@13.6.16) - y-protocols: 1.0.6(yjs@13.6.16) - y-webrtc: 10.2.6(yjs@13.6.16) - yjs: 13.6.16 + y-indexeddb: 9.0.12(yjs@13.6.18) + y-protocols: 1.0.6(yjs@13.6.18) + y-webrtc: 10.2.6(yjs@13.6.18) + yjs: 13.6.18 transitivePeerDependencies: - bufferutil - supports-color @@ -20195,28 +20326,30 @@ snapshots: acorn-globals@7.0.1: dependencies: - acorn: 8.11.3 - acorn-walk: 8.3.2 + acorn: 8.12.1 + acorn-walk: 8.3.3 - acorn-import-assertions@1.9.0(acorn@8.11.3): + acorn-import-assertions@1.9.0(acorn@8.12.1): dependencies: - acorn: 8.11.3 + acorn: 8.12.1 acorn-jsx@5.3.2(acorn@7.4.1): dependencies: acorn: 7.4.1 - acorn-jsx@5.3.2(acorn@8.11.3): + acorn-jsx@5.3.2(acorn@8.12.1): dependencies: - acorn: 8.11.3 + acorn: 8.12.1 acorn-walk@7.2.0: {} - acorn-walk@8.3.2: {} + acorn-walk@8.3.3: + dependencies: + acorn: 8.12.1 acorn@7.4.1: {} - acorn@8.11.3: {} + acorn@8.12.1: {} address@1.2.2: {} @@ -20446,7 +20579,7 @@ snapshots: autoprefixer@10.4.14(postcss@8.4.31): dependencies: browserslist: 4.23.1 - caniuse-lite: 1.0.30001632 + caniuse-lite: 1.0.30001640 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.0.1 @@ -20479,7 +20612,9 @@ snapshots: transitivePeerDependencies: - debug - axobject-query@3.2.1: + axobject-query@3.2.4: {} + + axobject-query@4.0.0: dependencies: dequal: 2.0.3 @@ -20644,12 +20779,12 @@ snapshots: bare-stream: 2.1.3 optional: true - bare-os@2.3.0: + bare-os@2.4.0: optional: true bare-path@2.1.3: dependencies: - bare-os: 2.3.0 + bare-os: 2.4.0 optional: true bare-stream@2.1.3: @@ -20727,10 +20862,10 @@ snapshots: browserslist@4.23.1: dependencies: - caniuse-lite: 1.0.30001632 - electron-to-chromium: 1.4.796 + caniuse-lite: 1.0.30001640 + electron-to-chromium: 1.4.818 node-releases: 2.0.14 - update-browserslist-db: 1.0.16(browserslist@4.23.1) + update-browserslist-db: 1.1.0(browserslist@4.23.1) bs-logger@0.2.6: dependencies: @@ -20801,11 +20936,11 @@ snapshots: caniuse-api@3.0.0: dependencies: browserslist: 4.23.1 - caniuse-lite: 1.0.30001632 + caniuse-lite: 1.0.30001640 lodash.memoize: 4.1.2 lodash.uniq: 4.5.0 - caniuse-lite@1.0.30001632: {} + caniuse-lite@1.0.30001640: {} capital-case@1.0.4: dependencies: @@ -21039,6 +21174,14 @@ snapshots: code-point-at@1.1.0: {} + code-red@1.0.4: + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + '@types/estree': 1.0.5 + acorn: 8.12.1 + estree-walker: 3.0.3 + periscopic: 3.1.0 + collect-v8-coverage@1.0.2: {} color-convert@1.9.3: @@ -21145,6 +21288,8 @@ snapshots: tree-kill: 1.2.2 yargs: 17.6.2 + confbox@0.1.7: {} + config@3.3.7: dependencies: json5: 2.2.3 @@ -21238,13 +21383,13 @@ snapshots: - supports-color - ts-node - create-jest@29.7.0(@types/node@20.14.2): + create-jest@29.7.0(@types/node@20.14.10): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@20.14.2) + jest-config: 29.7.0(@types/node@20.14.10) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -21259,6 +21404,8 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + crypto-js@4.2.0: {} + crypto-random-string@2.0.0: {} crypto-random-string@4.0.0: @@ -21271,12 +21418,12 @@ snapshots: css-loader@6.11.0(webpack@5.76.0(webpack-cli@4.9.1)): dependencies: - icss-utils: 5.1.0(postcss@8.4.38) - postcss: 8.4.38 - postcss-modules-extract-imports: 3.1.0(postcss@8.4.38) - postcss-modules-local-by-default: 4.0.5(postcss@8.4.38) - postcss-modules-scope: 3.2.0(postcss@8.4.38) - postcss-modules-values: 4.0.0(postcss@8.4.38) + icss-utils: 5.1.0(postcss@8.4.39) + postcss: 8.4.39 + postcss-modules-extract-imports: 3.1.0(postcss@8.4.39) + postcss-modules-local-by-default: 4.0.5(postcss@8.4.39) + postcss-modules-scope: 3.2.0(postcss@8.4.39) + postcss-modules-values: 4.0.0(postcss@8.4.39) postcss-value-parser: 4.2.0 semver: 7.6.2 optionalDependencies: @@ -21496,10 +21643,10 @@ snapshots: deepmerge@4.3.1: {} - deepsignal@1.5.0(@preact/signals@1.2.3(preact@10.22.0))(preact@10.22.0): + deepsignal@1.5.0(@preact/signals@1.3.0(preact@10.22.1))(preact@10.22.1): optionalDependencies: - '@preact/signals': 1.2.3(preact@10.22.0) - preact: 10.22.0 + '@preact/signals': 1.3.0(preact@10.22.1) + preact: 10.22.1 default-browser-id@3.0.0: dependencies: @@ -21705,7 +21852,7 @@ snapshots: dependencies: jake: 10.9.1 - electron-to-chromium@1.4.796: {} + electron-to-chromium@1.4.818: {} elegant-spinner@1.0.1: {} @@ -21802,7 +21949,7 @@ snapshots: is-string: 1.0.7 is-typed-array: 1.1.13 is-weakref: 1.0.2 - object-inspect: 1.13.1 + object-inspect: 1.13.2 object-keys: 1.1.1 object.assign: 4.1.5 regexp.prototype.flags: 1.5.2 @@ -21855,7 +22002,7 @@ snapshots: es-module-lexer@0.9.3: {} - es-module-lexer@1.5.3: {} + es-module-lexer@1.5.4: {} es-object-atoms@1.0.0: dependencies: @@ -21879,8 +22026,6 @@ snapshots: es6-error@4.1.1: {} - es6-promise@3.3.1: {} - esbuild-loader@3.0.1(webpack@5.76.0(webpack-cli@4.9.1)): dependencies: esbuild: 0.17.19 @@ -21987,7 +22132,7 @@ snapshots: eslint-import-resolver-node@0.3.9: dependencies: debug: 3.2.7 - is-core-module: 2.13.1 + is-core-module: 2.14.0 resolve: 1.22.8 transitivePeerDependencies: - supports-color @@ -22005,7 +22150,7 @@ snapshots: eslint-plugin-es-x@7.7.0(eslint@8.57.0): dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) - '@eslint-community/regexpp': 4.10.1 + '@eslint-community/regexpp': 4.11.0 eslint: 8.57.0 eslint-compat-utils: 0.5.1(eslint@8.57.0) @@ -22021,7 +22166,7 @@ snapshots: eslint-import-resolver-node: 0.3.9 eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.0.4))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0) hasown: 2.0.2 - is-core-module: 2.13.1 + is-core-module: 2.14.0 is-glob: 4.0.3 minimatch: 3.1.2 object.fromentries: 2.0.8 @@ -22036,10 +22181,6 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-inclusive-language@2.2.1: - dependencies: - humps: 2.0.1 - eslint-plugin-jest-dom@5.4.0(eslint@8.57.0): dependencies: '@babel/runtime': 7.24.7 @@ -22080,7 +22221,7 @@ snapshots: array.prototype.flatmap: 1.3.2 ast-types-flow: 0.0.8 axe-core: 4.7.0 - axobject-query: 3.2.1 + axobject-query: 3.2.4 damerau-levenshtein: 1.0.8 emoji-regex: 9.2.2 es-iterator-helpers: 1.0.19 @@ -22139,7 +22280,7 @@ snapshots: semver: 6.3.1 string.prototype.matchall: 4.0.11 - eslint-plugin-svelte@2.41.0(eslint@8.57.0)(svelte@3.58.0): + eslint-plugin-svelte@2.41.0(eslint@8.57.0)(svelte@4.2.18): dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) '@jridgewell/sourcemap-codec': 1.4.15 @@ -22147,14 +22288,14 @@ snapshots: eslint-compat-utils: 0.5.1(eslint@8.57.0) esutils: 2.0.3 known-css-properties: 0.34.0 - postcss: 8.4.38 - postcss-load-config: 3.1.4(postcss@8.4.38) - postcss-safe-parser: 6.0.0(postcss@8.4.38) + postcss: 8.4.39 + postcss-load-config: 3.1.4(postcss@8.4.39) + postcss-safe-parser: 6.0.0(postcss@8.4.39) postcss-selector-parser: 6.1.0 semver: 7.6.2 - svelte-eslint-parser: 0.39.2(svelte@3.58.0) + svelte-eslint-parser: 0.39.2(svelte@4.2.18) optionalDependencies: - svelte: 3.58.0 + svelte: 4.2.18 transitivePeerDependencies: - ts-node @@ -22166,16 +22307,6 @@ snapshots: - supports-color - typescript - eslint-plugin-wpcalypso@8.0.0(@babel/core@7.24.7)(eslint-plugin-inclusive-language@2.2.1)(eslint-plugin-jsdoc@46.10.1(eslint@8.57.0))(eslint-plugin-react-hooks@4.6.2(eslint@8.57.0))(eslint-plugin-react@7.34.2(eslint@8.57.0))(eslint@8.57.0): - dependencies: - '@babel/core': 7.24.7 - eslint: 8.57.0 - eslint-plugin-inclusive-language: 2.2.1 - eslint-plugin-jsdoc: 46.10.1(eslint@8.57.0) - optionalDependencies: - eslint-plugin-react: 7.34.2(eslint@8.57.0) - eslint-plugin-react-hooks: 4.6.2(eslint@8.57.0) - eslint-scope@5.1.1: dependencies: esrecurse: 4.3.0 @@ -22193,7 +22324,7 @@ snapshots: eslint@8.57.0: dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) - '@eslint-community/regexpp': 4.10.1 + '@eslint-community/regexpp': 4.11.0 '@eslint/eslintrc': 2.1.4 '@eslint/js': 8.57.0 '@humanwhocodes/config-array': 0.11.14 @@ -22235,8 +22366,8 @@ snapshots: espree@9.6.1: dependencies: - acorn: 8.11.3 - acorn-jsx: 5.3.2(acorn@8.11.3) + acorn: 8.12.1 + acorn-jsx: 5.3.2(acorn@8.12.1) eslint-visitor-keys: 3.4.3 esprima@4.0.1: {} @@ -22271,13 +22402,17 @@ snapshots: estree-walker@2.0.2: {} + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.5 + esutils@2.0.3: {} etag@1.8.1: {} eval@0.1.8: dependencies: - '@types/node': 20.14.2 + '@types/node': 20.14.10 require-like: 0.1.2 eventemitter3@3.1.2: {} @@ -22537,7 +22672,7 @@ snapshots: flatted@3.3.1: {} - flow-parser@0.237.2: {} + flow-parser@0.239.0: {} fn.name@1.1.0: {} @@ -22560,7 +22695,7 @@ snapshots: cross-spawn: 7.0.3 signal-exit: 3.0.7 - foreground-child@3.1.1: + foreground-child@3.2.1: dependencies: cross-spawn: 7.0.3 signal-exit: 4.1.0 @@ -22615,7 +22750,7 @@ snapshots: fraction.js@4.3.7: {} - framer-motion@11.2.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + framer-motion@11.2.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: tslib: 2.5.0 optionalDependencies: @@ -22739,7 +22874,7 @@ snapshots: consola: 3.2.3 defu: 6.1.4 node-fetch-native: 1.6.4 - nypm: 0.3.8 + nypm: 0.3.9 ohash: 1.1.3 pathe: 1.1.2 tar: 6.2.1 @@ -22762,9 +22897,9 @@ snapshots: glob@10.4.1: dependencies: - foreground-child: 3.1.1 - jackspeak: 3.4.0 - minimatch: 9.0.4 + foreground-child: 3.2.1 + jackspeak: 3.4.1 + minimatch: 9.0.5 minipass: 7.1.2 path-scurry: 1.11.1 @@ -22838,7 +22973,7 @@ snapshots: merge2: 1.4.1 slash: 4.0.0 - globby@14.0.1: + globby@14.0.2: dependencies: '@sindresorhus/merge-streams': 2.3.0 fast-glob: 3.3.2 @@ -23046,7 +23181,7 @@ snapshots: transitivePeerDependencies: - supports-color - https-proxy-agent@7.0.4: + https-proxy-agent@7.0.5: dependencies: agent-base: 7.1.1 debug: 4.3.4 @@ -23059,8 +23194,6 @@ snapshots: human-signals@5.0.0: {} - humps@2.0.1: {} - husky@8.0.3: {} iconv-lite@0.4.24: @@ -23077,9 +23210,9 @@ snapshots: dependencies: postcss: 8.4.31 - icss-utils@5.1.0(postcss@8.4.38): + icss-utils@5.1.0(postcss@8.4.39): dependencies: - postcss: 8.4.38 + postcss: 8.4.39 ieee754@1.2.1: {} @@ -23186,7 +23319,7 @@ snapshots: is-callable@1.2.7: {} - is-core-module@2.13.1: + is-core-module@2.14.0: dependencies: hasown: 2.0.2 @@ -23277,6 +23410,10 @@ snapshots: dependencies: '@types/estree': 1.0.5 + is-reference@3.0.2: + dependencies: + '@types/estree': 1.0.5 + is-regex@1.1.4: dependencies: call-bind: 1.0.7 @@ -23366,7 +23503,7 @@ snapshots: transitivePeerDependencies: - supports-color - istanbul-lib-instrument@6.0.2: + istanbul-lib-instrument@6.0.3: dependencies: '@babel/core': 7.24.7 '@babel/parser': 7.24.7 @@ -23412,7 +23549,7 @@ snapshots: reflect.getprototypeof: 1.0.6 set-function-name: 2.0.2 - jackspeak@3.4.0: + jackspeak@3.4.1: dependencies: '@isaacs/cliui': 8.0.2 optionalDependencies: @@ -23437,7 +23574,7 @@ snapshots: '@jest/expect': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.14.2 + '@types/node': 20.14.10 chalk: 4.1.2 co: 4.6.0 dedent: 1.5.3 @@ -23476,16 +23613,16 @@ snapshots: - supports-color - ts-node - jest-cli@29.7.0(@types/node@20.14.2): + jest-cli@29.7.0(@types/node@20.14.10): dependencies: '@jest/core': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@20.14.2) + create-jest: 29.7.0(@types/node@20.14.10) exit: 0.1.2 import-local: 3.1.0 - jest-config: 29.7.0(@types/node@20.14.2) + jest-config: 29.7.0(@types/node@20.14.10) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.6.2 @@ -23523,7 +23660,7 @@ snapshots: - babel-plugin-macros - supports-color - jest-config@29.7.0(@types/node@20.14.2): + jest-config@29.7.0(@types/node@20.14.10): dependencies: '@babel/core': 7.24.7 '@jest/test-sequencer': 29.7.0 @@ -23548,7 +23685,7 @@ snapshots: slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 20.14.2 + '@types/node': 20.14.10 transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -23578,7 +23715,7 @@ snapshots: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 '@types/jsdom': 20.0.1 - '@types/node': 20.14.2 + '@types/node': 20.14.10 jest-mock: 29.7.0 jest-util: 29.7.0 jsdom: 20.0.3 @@ -23592,7 +23729,7 @@ snapshots: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.14.2 + '@types/node': 20.14.10 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -23609,7 +23746,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.9 - '@types/node': 20.14.2 + '@types/node': 20.14.10 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -23655,7 +23792,7 @@ snapshots: jest-mock@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 20.14.2 + '@types/node': 20.14.10 jest-util: 29.7.0 jest-playwright-preset@4.0.0(jest-circus@29.7.0)(jest-environment-node@29.7.0)(jest-runner@29.7.0)(jest@29.7.0): @@ -23667,7 +23804,7 @@ snapshots: jest-process-manager: 0.4.0 jest-runner: 29.7.0 nyc: 15.1.0 - playwright-core: 1.44.1 + playwright-core: 1.45.1 rimraf: 3.0.2 uuid: 8.3.2 transitivePeerDependencies: @@ -23722,7 +23859,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.14.2 + '@types/node': 20.14.10 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.11 @@ -23750,7 +23887,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.14.2 + '@types/node': 20.14.10 chalk: 4.1.2 cjs-module-lexer: 1.3.1 collect-v8-coverage: 1.0.2 @@ -23800,7 +23937,7 @@ snapshots: jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 20.14.2 + '@types/node': 20.14.10 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -23830,7 +23967,7 @@ snapshots: dependencies: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.14.2 + '@types/node': 20.14.10 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -23839,13 +23976,13 @@ snapshots: jest-worker@27.5.1: dependencies: - '@types/node': 20.14.2 + '@types/node': 20.14.10 merge-stream: 2.0.0 supports-color: 8.1.1 jest-worker@29.7.0: dependencies: - '@types/node': 20.14.2 + '@types/node': 20.14.10 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 @@ -23862,12 +23999,12 @@ snapshots: - supports-color - ts-node - jest@29.7.0(@types/node@20.14.2): + jest@29.7.0(@types/node@20.14.10): dependencies: '@jest/core': 29.7.0 '@jest/types': 29.6.3 import-local: 3.1.0 - jest-cli: 29.7.0(@types/node@20.14.2) + jest-cli: 29.7.0(@types/node@20.14.10) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -23883,7 +24020,7 @@ snapshots: jiti@1.21.6: {} - joi@17.13.1: + joi@17.13.3: dependencies: '@hapi/hoek': 9.3.0 '@hapi/topo': 5.1.0 @@ -23922,7 +24059,7 @@ snapshots: '@babel/register': 7.24.6(@babel/core@7.24.7) babel-core: 7.0.0-bridge.0(@babel/core@7.24.7) chalk: 4.1.2 - flow-parser: 0.237.2 + flow-parser: 0.239.0 graceful-fs: 4.2.11 micromatch: 4.0.7 neo-async: 2.6.2 @@ -23947,7 +24084,7 @@ snapshots: '@babel/register': 7.24.6(@babel/core@7.24.7) babel-core: 7.0.0-bridge.0(@babel/core@7.24.7) chalk: 4.1.2 - flow-parser: 0.237.2 + flow-parser: 0.239.0 graceful-fs: 4.2.11 micromatch: 4.0.7 neo-async: 2.6.2 @@ -23965,7 +24102,7 @@ snapshots: jsdom@20.0.3: dependencies: abab: 2.0.6 - acorn: 8.11.3 + acorn: 8.12.1 acorn-globals: 7.0.1 cssom: 0.5.0 cssstyle: 2.3.0 @@ -23988,7 +24125,7 @@ snapshots: whatwg-encoding: 2.0.0 whatwg-mimetype: 3.0.0 whatwg-url: 11.0.0 - ws: 8.17.0 + ws: 8.18.0 xml-name-validator: 4.0.0 transitivePeerDependencies: - bufferutil @@ -24019,7 +24156,7 @@ snapshots: json5@2.2.3: {} - jsonc-parser@3.2.1: {} + jsonc-parser@3.3.1: {} jsonfile@6.1.0: dependencies: @@ -24143,7 +24280,7 @@ snapshots: chokidar: 3.5.3 livereload-js: 3.4.1 opts: 2.0.2 - ws: 7.5.9 + ws: 7.5.10 transitivePeerDependencies: - bufferutil - utf-8-validate @@ -24167,6 +24304,8 @@ snapshots: transitivePeerDependencies: - supports-color + locate-character@3.0.0: {} + locate-path@3.0.0: dependencies: p-locate: 3.0.0 @@ -24262,7 +24401,7 @@ snapshots: dependencies: tslib: 2.5.0 - lru-cache@10.2.2: {} + lru-cache@10.4.0: {} lru-cache@5.1.1: dependencies: @@ -24323,7 +24462,7 @@ snapshots: grid-index: 1.1.0 minimist: 1.2.8 murmurhash-js: 1.0.0 - pbf: 3.2.1 + pbf: 3.3.0 potpack: 1.0.2 quickselect: 2.0.0 rw: 1.3.3 @@ -24498,14 +24637,14 @@ snapshots: micromark-util-symbol: 2.0.0 micromark-util-types: 2.0.0 - micromark-extension-gfm-autolink-literal@2.0.0: + micromark-extension-gfm-autolink-literal@2.1.0: dependencies: micromark-util-character: 2.1.0 micromark-util-sanitize-uri: 2.0.0 micromark-util-symbol: 2.0.0 micromark-util-types: 2.0.0 - micromark-extension-gfm-footnote@2.0.0: + micromark-extension-gfm-footnote@2.1.0: dependencies: devlop: 1.1.0 micromark-core-commonmark: 2.0.1 @@ -24516,7 +24655,7 @@ snapshots: micromark-util-symbol: 2.0.0 micromark-util-types: 2.0.0 - micromark-extension-gfm-strikethrough@2.0.0: + micromark-extension-gfm-strikethrough@2.1.0: dependencies: devlop: 1.1.0 micromark-util-chunked: 2.0.0 @@ -24525,7 +24664,7 @@ snapshots: micromark-util-symbol: 2.0.0 micromark-util-types: 2.0.0 - micromark-extension-gfm-table@2.0.0: + micromark-extension-gfm-table@2.1.0: dependencies: devlop: 1.1.0 micromark-factory-space: 2.0.0 @@ -24537,7 +24676,7 @@ snapshots: dependencies: micromark-util-types: 2.0.0 - micromark-extension-gfm-task-list-item@2.0.1: + micromark-extension-gfm-task-list-item@2.1.0: dependencies: devlop: 1.1.0 micromark-factory-space: 2.0.0 @@ -24547,12 +24686,12 @@ snapshots: micromark-extension-gfm@3.0.0: dependencies: - micromark-extension-gfm-autolink-literal: 2.0.0 - micromark-extension-gfm-footnote: 2.0.0 - micromark-extension-gfm-strikethrough: 2.0.0 - micromark-extension-gfm-table: 2.0.0 + micromark-extension-gfm-autolink-literal: 2.1.0 + micromark-extension-gfm-footnote: 2.1.0 + micromark-extension-gfm-strikethrough: 2.1.0 + micromark-extension-gfm-table: 2.1.0 micromark-extension-gfm-tagfilter: 2.0.0 - micromark-extension-gfm-task-list-item: 2.0.1 + micromark-extension-gfm-task-list-item: 2.1.0 micromark-util-combine-extensions: 2.0.0 micromark-util-types: 2.0.0 @@ -24708,7 +24847,7 @@ snapshots: dependencies: brace-expansion: 2.0.1 - minimatch@9.0.4: + minimatch@9.0.5: dependencies: brace-expansion: 2.0.1 @@ -24731,12 +24870,15 @@ snapshots: mkdirp-classic@0.5.3: {} - mkdirp@0.5.6: - dependencies: - minimist: 1.2.8 - mkdirp@1.0.4: {} + mlly@1.7.1: + dependencies: + acorn: 8.12.1 + pathe: 1.1.2 + pkg-types: 1.1.3 + ufo: 1.5.3 + mock-xmlhttprequest@8.3.0: {} moment-timezone@0.5.45: @@ -24859,17 +25001,18 @@ snapshots: transitivePeerDependencies: - supports-color - nypm@0.3.8: + nypm@0.3.9: dependencies: citty: 0.1.6 consola: 3.2.3 execa: 8.0.1 pathe: 1.1.2 + pkg-types: 1.1.3 ufo: 1.5.3 object-assign@4.1.1: {} - object-inspect@1.13.1: {} + object-inspect@1.13.2: {} object-is@1.1.6: dependencies: @@ -24993,7 +25136,7 @@ snapshots: p-limit@4.0.0: dependencies: - yocto-queue: 1.0.0 + yocto-queue: 1.1.1 p-locate@3.0.0: dependencies: @@ -25033,16 +25176,16 @@ snapshots: p-try@2.2.0: {} - pac-proxy-agent@7.0.1: + pac-proxy-agent@7.0.2: dependencies: '@tootallnate/quickjs-emscripten': 0.23.0 agent-base: 7.1.1 debug: 4.3.4 get-uri: 6.0.3 http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.4 + https-proxy-agent: 7.0.5 pac-resolver: 7.0.1 - socks-proxy-agent: 8.0.3 + socks-proxy-agent: 8.0.4 transitivePeerDependencies: - supports-color @@ -25127,7 +25270,7 @@ snapshots: path-scurry@1.11.1: dependencies: - lru-cache: 10.2.2 + lru-cache: 10.4.0 minipass: 7.1.2 path-to-regexp@0.1.7: {} @@ -25144,7 +25287,7 @@ snapshots: pathe@1.1.2: {} - pbf@3.2.1: + pbf@3.3.0: dependencies: ieee754: 1.2.1 resolve-protobuf-schema: 2.1.0 @@ -25157,6 +25300,12 @@ snapshots: pend@1.2.0: {} + periscopic@3.1.0: + dependencies: + '@types/estree': 1.0.5 + estree-walker: 3.0.3 + is-reference: 3.0.2 + photon@4.0.0: dependencies: '@babel/runtime': 7.24.7 @@ -25194,11 +25343,17 @@ snapshots: dependencies: find-up: 6.3.0 - playwright-core@1.44.1: {} + pkg-types@1.1.3: + dependencies: + confbox: 0.1.7 + mlly: 1.7.1 + pathe: 1.1.2 + + playwright-core@1.45.1: {} - playwright@1.44.1: + playwright@1.45.1: dependencies: - playwright-core: 1.44.1 + playwright-core: 1.45.1 optionalDependencies: fsevents: 2.3.2 @@ -25258,12 +25413,12 @@ snapshots: optionalDependencies: postcss: 8.4.31 - postcss-load-config@3.1.4(postcss@8.4.38): + postcss-load-config@3.1.4(postcss@8.4.39): dependencies: lilconfig: 2.1.0 yaml: 1.10.2 optionalDependencies: - postcss: 8.4.38 + postcss: 8.4.39 postcss-loader@6.2.0(postcss@8.4.31)(webpack@5.76.0(webpack-cli@4.9.1)): dependencies: @@ -25315,9 +25470,9 @@ snapshots: dependencies: postcss: 8.4.31 - postcss-modules-extract-imports@3.1.0(postcss@8.4.38): + postcss-modules-extract-imports@3.1.0(postcss@8.4.39): dependencies: - postcss: 8.4.38 + postcss: 8.4.39 postcss-modules-local-by-default@4.0.5(postcss@8.4.31): dependencies: @@ -25326,10 +25481,10 @@ snapshots: postcss-selector-parser: 6.1.0 postcss-value-parser: 4.2.0 - postcss-modules-local-by-default@4.0.5(postcss@8.4.38): + postcss-modules-local-by-default@4.0.5(postcss@8.4.39): dependencies: - icss-utils: 5.1.0(postcss@8.4.38) - postcss: 8.4.38 + icss-utils: 5.1.0(postcss@8.4.39) + postcss: 8.4.39 postcss-selector-parser: 6.1.0 postcss-value-parser: 4.2.0 @@ -25338,9 +25493,9 @@ snapshots: postcss: 8.4.31 postcss-selector-parser: 6.1.0 - postcss-modules-scope@3.2.0(postcss@8.4.38): + postcss-modules-scope@3.2.0(postcss@8.4.39): dependencies: - postcss: 8.4.38 + postcss: 8.4.39 postcss-selector-parser: 6.1.0 postcss-modules-values@4.0.0(postcss@8.4.31): @@ -25348,10 +25503,10 @@ snapshots: icss-utils: 5.1.0(postcss@8.4.31) postcss: 8.4.31 - postcss-modules-values@4.0.0(postcss@8.4.38): + postcss-modules-values@4.0.0(postcss@8.4.39): dependencies: - icss-utils: 5.1.0(postcss@8.4.38) - postcss: 8.4.38 + icss-utils: 5.1.0(postcss@8.4.39) + postcss: 8.4.39 postcss-modules@4.3.1(postcss@8.4.31): dependencies: @@ -25416,7 +25571,7 @@ snapshots: postcss: 8.4.31 postcss-value-parser: 4.2.0 - postcss-prefixwrap@1.48.0(postcss@8.4.31): + postcss-prefixwrap@1.49.0(postcss@8.4.31): dependencies: postcss: 8.4.31 @@ -25431,13 +25586,13 @@ snapshots: postcss: 8.4.31 postcss-value-parser: 4.2.0 - postcss-safe-parser@6.0.0(postcss@8.4.38): + postcss-safe-parser@6.0.0(postcss@8.4.39): dependencies: - postcss: 8.4.38 + postcss: 8.4.39 - postcss-scss@4.0.9(postcss@8.4.38): + postcss-scss@4.0.9(postcss@8.4.39): dependencies: - postcss: 8.4.38 + postcss: 8.4.39 postcss-selector-parser@6.1.0: dependencies: @@ -25455,7 +25610,7 @@ snapshots: postcss: 8.4.31 postcss-selector-parser: 6.1.0 - postcss-urlrebase@1.3.0(postcss@8.4.31): + postcss-urlrebase@1.4.0(postcss@8.4.31): dependencies: postcss: 8.4.31 postcss-value-parser: 4.2.0 @@ -25468,7 +25623,7 @@ snapshots: picocolors: 1.0.1 source-map-js: 1.2.0 - postcss@8.4.38: + postcss@8.4.39: dependencies: nanoid: 3.3.7 picocolors: 1.0.1 @@ -25476,9 +25631,7 @@ snapshots: potpack@1.0.2: {} - preact@10.12.1: {} - - preact@10.22.0: {} + preact@10.22.1: {} prelude-ls@1.2.1: {} @@ -25486,10 +25639,10 @@ snapshots: dependencies: fast-diff: 1.3.0 - prettier-plugin-svelte@3.0.3(svelte@3.58.0)(wp-prettier@3.0.3): + prettier-plugin-svelte@3.0.3(svelte@4.2.18)(wp-prettier@3.0.3): dependencies: prettier: wp-prettier@3.0.3 - svelte: 3.58.0 + svelte: 4.2.18 prettier@2.8.8: {} @@ -25562,11 +25715,11 @@ snapshots: agent-base: 7.1.1 debug: 4.3.4 http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.4 + https-proxy-agent: 7.0.5 lru-cache: 7.18.3 - pac-proxy-agent: 7.0.1 + pac-proxy-agent: 7.0.2 proxy-from-env: 1.1.0 - socks-proxy-agent: 8.0.3 + socks-proxy-agent: 8.0.4 transitivePeerDependencies: - supports-color @@ -25772,7 +25925,7 @@ snapshots: use-callback-ref: 1.3.2(react@18.3.1) use-sidecar: 1.1.2(react@18.3.1) - react-remove-scroll@2.5.5(@types/react@18.3.1)(react@18.3.1): + react-remove-scroll@2.5.7(@types/react@18.3.1)(react@18.3.1): dependencies: react: 18.3.1 react-remove-scroll-bar: 2.3.6(@types/react@18.3.1)(react@18.3.1) @@ -25783,7 +25936,7 @@ snapshots: optionalDependencies: '@types/react': 18.3.1 - react-remove-scroll@2.5.5(react@18.3.1): + react-remove-scroll@2.5.7(react@18.3.1): dependencies: react: 18.3.1 react-remove-scroll-bar: 2.3.6(react@18.3.1) @@ -26077,7 +26230,7 @@ snapshots: micromark-extension-gfm: 3.0.0 remark-parse: 11.0.0 remark-stringify: 11.0.0 - unified: 11.0.4 + unified: 11.0.5 transitivePeerDependencies: - supports-color @@ -26086,7 +26239,7 @@ snapshots: '@types/mdast': 4.0.4 mdast-util-from-markdown: 2.0.1 micromark-util-types: 2.0.0 - unified: 11.0.4 + unified: 11.0.5 transitivePeerDependencies: - supports-color @@ -26094,7 +26247,7 @@ snapshots: dependencies: '@types/mdast': 4.0.4 mdast-util-to-markdown: 2.1.0 - unified: 11.0.4 + unified: 11.0.5 rememo@4.0.1: {} @@ -26120,8 +26273,6 @@ snapshots: require-main-filename@2.0.0: {} - require-relative@0.8.7: {} - requireindex@1.2.0: {} requires-port@1.0.0: {} @@ -26153,13 +26304,13 @@ snapshots: resolve@1.22.8: dependencies: - is-core-module: 2.13.1 + is-core-module: 2.14.0 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 resolve@2.0.0-next.5: dependencies: - is-core-module: 2.13.1 + is-core-module: 2.14.0 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 @@ -26183,10 +26334,6 @@ snapshots: dependencies: glob: 7.2.3 - rimraf@2.7.1: - dependencies: - glob: 7.2.3 - rimraf@3.0.2: dependencies: glob: 7.2.3 @@ -26217,18 +26364,18 @@ snapshots: transitivePeerDependencies: - ts-node - rollup-plugin-svelte-svg@1.0.0-beta.6(svelte@3.58.0): + rollup-plugin-svelte-svg@1.0.0-beta.6(svelte@4.2.18): dependencies: rollup-pluginutils: 2.8.2 - svelte: 3.58.0 + svelte: 4.2.18 svgo: 3.3.2 - rollup-plugin-svelte@7.1.0(rollup@2.79.1)(svelte@3.58.0): + rollup-plugin-svelte@7.2.2(rollup@2.79.1)(svelte@4.2.18): dependencies: - require-relative: 0.8.7 + '@rollup/pluginutils': 4.2.1 + resolve.exports: 2.0.2 rollup: 2.79.1 - rollup-pluginutils: 2.8.2 - svelte: 3.58.0 + svelte: 4.2.18 rollup-pluginutils@2.8.2: dependencies: @@ -26284,13 +26431,6 @@ snapshots: safer-buffer@2.1.2: {} - sander@0.5.1: - dependencies: - es6-promise: 3.3.1 - graceful-fs: 4.2.11 - mkdirp: 0.5.6 - rimraf: 2.7.1 - sass-loader@12.4.0(sass@1.64.1)(webpack@5.76.0(webpack-cli@4.9.1)): dependencies: klona: 2.0.6 @@ -26426,7 +26566,7 @@ snapshots: call-bind: 1.0.7 es-errors: 1.3.0 get-intrinsic: 1.2.4 - object-inspect: 1.13.1 + object-inspect: 1.13.2 signal-exit@3.0.7: {} @@ -26456,7 +26596,7 @@ snapshots: dependencies: bytes-iec: 3.1.1 chokidar: 3.6.0 - globby: 14.0.1 + globby: 14.0.2 jiti: 1.21.6 lilconfig: 3.1.2 nanospinner: 1.1.0 @@ -26481,7 +26621,7 @@ snapshots: dot-case: 3.0.4 tslib: 2.5.0 - socks-proxy-agent@8.0.3: + socks-proxy-agent@8.0.4: dependencies: agent-base: 7.1.1 debug: 4.3.4 @@ -26494,13 +26634,6 @@ snapshots: ip-address: 9.0.5 smart-buffer: 4.2.0 - sorcery@0.11.0: - dependencies: - '@jridgewell/sourcemap-codec': 1.4.15 - buffer-crc32: 0.2.13 - minimist: 1.2.8 - sander: 0.5.1 - sort-object-keys@1.1.3: {} sort-package-json@1.50.0: @@ -26596,9 +26729,9 @@ snapshots: storybook-addon-mock@5.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@storybook/blocks': 8.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@storybook/channels': 8.1.6 + '@storybook/channels': 8.1.11 '@storybook/components': 8.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@storybook/core-events': 8.1.6 + '@storybook/core-events': 8.1.11 '@storybook/manager-api': 8.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@storybook/preview-api': 8.1.6 '@storybook/theming': 8.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -26639,7 +26772,7 @@ snapshots: dependencies: fast-fifo: 1.3.2 queue-tick: 1.0.1 - text-decoder: 1.1.0 + text-decoder: 1.1.1 optionalDependencies: bare-events: 2.4.2 @@ -26806,31 +26939,41 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - svelte-eslint-parser@0.39.2(svelte@3.58.0): + svelte-eslint-parser@0.39.2(svelte@4.2.18): dependencies: eslint-scope: 7.2.2 eslint-visitor-keys: 3.4.3 espree: 9.6.1 - postcss: 8.4.38 - postcss-scss: 4.0.9(postcss@8.4.38) + postcss: 8.4.39 + postcss-scss: 4.0.9(postcss@8.4.39) optionalDependencies: - svelte: 3.58.0 + svelte: 4.2.18 - svelte-preprocess@5.0.4(@babel/core@7.24.7)(postcss@8.4.31)(sass@1.64.1)(svelte@3.58.0)(typescript@5.0.4): + svelte-preprocess@6.0.2(@babel/core@7.24.7)(postcss@8.4.31)(sass@1.64.1)(svelte@4.2.18)(typescript@5.0.4): dependencies: - '@types/pug': 2.0.10 - detect-indent: 6.1.0 - magic-string: 0.27.0 - sorcery: 0.11.0 - strip-indent: 3.0.0 - svelte: 3.58.0 + svelte: 4.2.18 optionalDependencies: '@babel/core': 7.24.7 postcss: 8.4.31 sass: 1.64.1 typescript: 5.0.4 - svelte@3.58.0: {} + svelte@4.2.18: + dependencies: + '@ampproject/remapping': 2.3.0 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping': 0.3.25 + '@types/estree': 1.0.5 + acorn: 8.12.1 + aria-query: 5.3.0 + axobject-query: 4.0.0 + code-red: 1.0.4 + css-tree: 2.3.1 + estree-walker: 3.0.3 + is-reference: 3.0.2 + locate-character: 3.0.0 + magic-string: 0.30.10 + periscopic: 3.1.0 svg-parser@2.0.4: {} @@ -26957,7 +27100,7 @@ snapshots: terser@5.31.1: dependencies: '@jridgewell/source-map': 0.3.6 - acorn: 8.11.3 + acorn: 8.12.1 commander: 2.20.3 source-map-support: 0.5.21 @@ -26967,7 +27110,7 @@ snapshots: glob: 7.2.3 minimatch: 3.1.2 - text-decoder@1.1.0: + text-decoder@1.1.1: dependencies: b4a: 1.6.6 @@ -27194,7 +27337,7 @@ snapshots: unicorn-magic@0.1.0: {} - unified@11.0.4: + unified@11.0.5: dependencies: '@types/unist': 3.0.2 bail: 2.0.2 @@ -27239,16 +27382,16 @@ snapshots: unpipe@1.0.0: {} - unplugin@1.10.1: + unplugin@1.11.0: dependencies: - acorn: 8.11.3 + acorn: 8.12.1 chokidar: 3.6.0 webpack-sources: 3.2.3 webpack-virtual-modules: 0.6.2 untildify@4.0.0: {} - update-browserslist-db@1.0.16(browserslist@4.23.1): + update-browserslist-db@1.1.0(browserslist@4.23.1): dependencies: browserslist: 4.23.1 escalade: 3.1.2 @@ -27355,7 +27498,7 @@ snapshots: uuid@9.0.1: {} - v8-to-istanbul@9.2.0: + v8-to-istanbul@9.3.0: dependencies: '@jridgewell/trace-mapping': 0.3.25 '@types/istanbul-lib-coverage': 2.0.6 @@ -27385,7 +27528,7 @@ snapshots: dependencies: '@mapbox/point-geometry': 0.1.0 '@mapbox/vector-tile': 1.3.1 - pbf: 3.2.1 + pbf: 3.3.0 w3c-xmlserializer@4.0.0: dependencies: @@ -27394,7 +27537,7 @@ snapshots: wait-on@7.2.0: dependencies: axios: 1.6.8 - joi: 17.13.1 + joi: 17.13.3 lodash: 4.17.21 minimist: 1.2.8 rxjs: 7.8.1 @@ -27486,8 +27629,8 @@ snapshots: '@webassemblyjs/ast': 1.11.1 '@webassemblyjs/wasm-edit': 1.11.1 '@webassemblyjs/wasm-parser': 1.11.1 - acorn: 8.11.3 - acorn-import-assertions: 1.9.0(acorn@8.11.3) + acorn: 8.12.1 + acorn-import-assertions: 1.9.0(acorn@8.12.1) browserslist: 4.23.1 chrome-trace-event: 1.0.4 enhanced-resolve: 5.17.0 @@ -27667,11 +27810,11 @@ snapshots: imurmurhash: 0.1.4 signal-exit: 3.0.7 - ws@7.5.9: {} + ws@7.5.10: {} ws@8.16.0: {} - ws@8.17.0: {} + ws@8.18.0: {} xdg-basedir@4.0.0: {} @@ -27690,24 +27833,24 @@ snapshots: xtend@4.0.2: {} - y-indexeddb@9.0.12(yjs@13.6.16): + y-indexeddb@9.0.12(yjs@13.6.18): dependencies: lib0: 0.2.94 - yjs: 13.6.16 + yjs: 13.6.18 - y-protocols@1.0.6(yjs@13.6.16): + y-protocols@1.0.6(yjs@13.6.18): dependencies: lib0: 0.2.94 - yjs: 13.6.16 + yjs: 13.6.18 - y-webrtc@10.2.6(yjs@13.6.16): + y-webrtc@10.2.6(yjs@13.6.18): dependencies: lib0: 0.2.94 simple-peer: 9.11.1 - y-protocols: 1.0.6(yjs@13.6.16) - yjs: 13.6.16 + y-protocols: 1.0.6(yjs@13.6.18) + yjs: 13.6.18 optionalDependencies: - ws: 8.17.0 + ws: 8.18.0 transitivePeerDependencies: - bufferutil - supports-color @@ -27790,13 +27933,13 @@ snapshots: buffer-crc32: 0.2.13 fd-slicer: 1.1.0 - yjs@13.6.16: + yjs@13.6.18: dependencies: lib0: 0.2.94 yocto-queue@0.1.0: {} - yocto-queue@1.0.0: {} + yocto-queue@1.1.1: {} zod@3.22.3: {} diff --git a/projects/github-actions/repo-gardening/changelog/remove-gardening-guessing-of-release-dates b/projects/github-actions/repo-gardening/changelog/remove-gardening-guessing-of-release-dates new file mode 100644 index 0000000000000..d0c1d5b7f15ec --- /dev/null +++ b/projects/github-actions/repo-gardening/changelog/remove-gardening-guessing-of-release-dates @@ -0,0 +1,4 @@ +Significance: patch +Type: removed + +Check description task: Remove general guessing of release dates and code freezes. Add special casing for wpcomsh and mu-wpcom-plugin. diff --git a/projects/github-actions/repo-gardening/src/tasks/check-description/index.js b/projects/github-actions/repo-gardening/src/tasks/check-description/index.js index 00da8cb840dda..c122026c312df 100644 --- a/projects/github-actions/repo-gardening/src/tasks/check-description/index.js +++ b/projects/github-actions/repo-gardening/src/tasks/check-description/index.js @@ -65,7 +65,7 @@ async function hasProgressLabel( octokit, owner, repo, number ) { * @returns {Promise} Promise resolving to info about the release (code freeze, release date). */ async function getMilestoneDates( plugin, nextMilestone ) { - let releaseDate; + let releaseDate = 'none scheduled'; let codeFreezeDate; if ( nextMilestone && nextMilestone.hasOwnProperty( 'due_on' ) && nextMilestone.due_on ) { releaseDate = moment( nextMilestone.due_on ).format( 'LL' ); @@ -77,19 +77,11 @@ async function getMilestoneDates( plugin, nextMilestone ) { // If we have a date and it is valid, use it, otherwise set code freeze to a week before the release. if ( freezeDateDescription && moment( freezeDateDescription[ 1 ] ).isValid() ) { codeFreezeDate = moment( freezeDateDescription[ 1 ] ).format( 'LL' ); - } else { - codeFreezeDate = moment( nextMilestone.due_on ).subtract( 7, 'd' ).format( 'LL' ); } - } else { - // Fallback to raw math calculation - // Calculate next release date - const firstTuesdayOfMonth = moment().add( 1, 'months' ).startOf( 'month' ); - while ( firstTuesdayOfMonth.day() !== 2 ) { - firstTuesdayOfMonth.add( 1, 'day' ); - } - releaseDate = firstTuesdayOfMonth.format( 'LL' ); - // Calculate next code freeze date - codeFreezeDate = firstTuesdayOfMonth.subtract( 8, 'd' ).format( 'LL' ); + } else if ( plugin === 'wpcomsh' ) { + releaseDate = 'on demand (usually Mondays if not sooner)'; + } else if ( plugin === 'mu-wpcom' ) { + releaseDate = 'WordPress.com Simple releases happen daily'; } const capitalizedName = plugin @@ -113,8 +105,7 @@ ${ - Releases to self-hosted sites happen monthly. The next release is scheduled for _${ releaseDate }_ (scheduled code freeze on _${ codeFreezeDate }_).` : ` - Next scheduled release: _${ releaseDate }_. -- Scheduled code freeze: _${ codeFreezeDate }_. -` +${ codeFreezeDate ? `- Scheduled code freeze: _${ codeFreezeDate }_.\n` : '' }` } If you have any questions about the release process, please ask in the #jetpack-releases channel on Slack. diff --git a/projects/js-packages/ai-client/CHANGELOG.md b/projects/js-packages/ai-client/CHANGELOG.md index 98afc8e039de9..ebd86cb48ecec 100644 --- a/projects/js-packages/ai-client/CHANGELOG.md +++ b/projects/js-packages/ai-client/CHANGELOG.md @@ -5,6 +5,21 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.15.0] - 2024-07-22 +### Added +- Jetpack AI: Add logo generator codebase to the ai-client package. [#38391] + +### Changed +- Update and export askQuestionSync. [#38344] + +## [0.14.6] - 2024-07-15 +### Added +- AI Client: Filter suggestions starting with llama artifacts [#38208] + +## [0.14.5] - 2024-07-08 +### Changed +- Updated package dependencies. [#38132] + ## [0.14.4] - 2024-06-17 ### Changed - Updated package dependencies. [#37779] @@ -343,6 +358,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Updated package dependencies. [#31659] - Updated package dependencies. [#31785] +[0.15.0]: https://github.com/Automattic/jetpack-ai-client/compare/v0.14.6...v0.15.0 +[0.14.6]: https://github.com/Automattic/jetpack-ai-client/compare/v0.14.5...v0.14.6 +[0.14.5]: https://github.com/Automattic/jetpack-ai-client/compare/v0.14.4...v0.14.5 [0.14.4]: https://github.com/Automattic/jetpack-ai-client/compare/v0.14.3...v0.14.4 [0.14.3]: https://github.com/Automattic/jetpack-ai-client/compare/v0.14.2...v0.14.3 [0.14.2]: https://github.com/Automattic/jetpack-ai-client/compare/v0.14.1...v0.14.2 diff --git a/projects/js-packages/ai-client/changelog/update-jetpack-ai-connect-logo-generator-to-block-extension b/projects/js-packages/ai-client/changelog/update-jetpack-ai-connect-logo-generator-to-block-extension new file mode 100644 index 0000000000000..f5a9eb0528941 --- /dev/null +++ b/projects/js-packages/ai-client/changelog/update-jetpack-ai-connect-logo-generator-to-block-extension @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +AI Logo Generator: make the initial prompt update when the site name and description are fully laoded from store. diff --git a/projects/js-packages/ai-client/package.json b/projects/js-packages/ai-client/package.json index 832efd18c2d69..6ba15c4636adc 100644 --- a/projects/js-packages/ai-client/package.json +++ b/projects/js-packages/ai-client/package.json @@ -1,7 +1,7 @@ { "private": false, "name": "@automattic/jetpack-ai-client", - "version": "0.14.5-alpha", + "version": "0.15.1-alpha", "description": "A JS client for consuming Jetpack AI services", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/ai-client/#readme", "bugs": { @@ -48,7 +48,9 @@ "@automattic/jetpack-shared-extension-utils": "workspace:*", "@microsoft/fetch-event-source": "2.0.1", "@types/react": "18.3.1", + "@types/wordpress__block-editor": "11.5.14", "@wordpress/api-fetch": "7.2.0", + "@wordpress/blob": "4.2.0", "@wordpress/block-editor": "13.2.0", "@wordpress/components": "28.2.0", "@wordpress/compose": "7.2.0", diff --git a/projects/js-packages/ai-client/src/ask-question/sync.ts b/projects/js-packages/ai-client/src/ask-question/sync.ts index 62c54718d38bb..4bbbd7f571d6d 100644 --- a/projects/js-packages/ai-client/src/ask-question/sync.ts +++ b/projects/js-packages/ai-client/src/ask-question/sync.ts @@ -12,13 +12,7 @@ import type { PromptProp } from '../types.js'; /** * The response data from the AI assistant when doing a sync, not-streamed question. */ -export type ResponseData = { - choices: Array< { - message: { - content: string; - }; - } >; -}; +export type ResponseData = string; const debug = debugFactory( 'jetpack-ai-client:ask-question-sync' ); @@ -41,13 +35,9 @@ const debug = debugFactory( 'jetpack-ai-client:ask-question-sync' ); */ export default async function askQuestionSync( question: PromptProp, - { postId = null, feature, model }: AskQuestionOptionsArgProps = {} + options: AskQuestionOptionsArgProps = {} ): Promise< ResponseData > { - debug( 'Asking question with no streaming: %o. options: %o', question, { - postId, - feature, - model, - } ); + debug( 'Asking question with no streaming: %o. options: %o', question, options ); /** * The URL to the AI assistant query endpoint. @@ -63,12 +53,12 @@ export default async function askQuestionSync( return Promise.reject( error ); } + const messages = Array.isArray( question ) ? { messages: question } : { question: question }; + const body = { - question: question, + ...messages, + ...options, stream: false, - postId, - feature, - model, }; const headers = { @@ -76,16 +66,21 @@ export default async function askQuestionSync( 'Content-Type': 'application/json', }; - const data = await fetch( URL, { - method: 'POST', - headers, - body: JSON.stringify( body ), - } ).then( response => response.json() ); + try { + const data = await fetch( URL, { + method: 'POST', + headers, + body: JSON.stringify( body ), + } ).then( response => response.json() ); - if ( data?.data?.status && data?.data?.status > 200 ) { - debug( 'Error generating prompt: %o', data ); - return Promise.reject( data ); - } + if ( data?.data?.status && data?.data?.status > 200 ) { + debug( 'Error generating prompt: %o', data ); + return Promise.reject( data ); + } - return data as ResponseData; + return data.choices?.[ 0 ]?.message?.content as string; + } catch ( error ) { + debug( 'Error asking question: %o', error ); + return Promise.reject( error ); + } } diff --git a/projects/js-packages/ai-client/src/hooks/use-ai-suggestions/index.ts b/projects/js-packages/ai-client/src/hooks/use-ai-suggestions/index.ts index 810ad7d4ca4e3..8311f22273e9c 100644 --- a/projects/js-packages/ai-client/src/hooks/use-ai-suggestions/index.ts +++ b/projects/js-packages/ai-client/src/hooks/use-ai-suggestions/index.ts @@ -190,6 +190,16 @@ export function getErrorData( errorCode: SuggestionErrorCode ): RequestingErrorP } } +/** + * Remove the llama artifact from a suggestion. + * + * @param {string} suggestion - The suggestion. + * @returns {string} The suggestion without the llama artifact. + */ +export function removeLlamaArtifact( suggestion: string ): string { + return suggestion.replace( /^<\|start_header_id\|>assistant<\|end_header_id\|>[\n]+/, '' ); +} + /** * React custom hook to get suggestions from AI, * by hitting the query endpoint. @@ -224,8 +234,14 @@ export default function useAiSuggestions( { */ const handleSuggestion = useCallback( ( event: CustomEvent ) => { - setSuggestion( event?.detail ); - onSuggestion?.( event?.detail ); + const partialSuggestion = removeLlamaArtifact( event?.detail ); + + if ( ! partialSuggestion ) { + return; + } + + setSuggestion( partialSuggestion ); + onSuggestion?.( partialSuggestion ); }, [ onSuggestion ] ); @@ -239,7 +255,10 @@ export default function useAiSuggestions( { const handleDone = useCallback( ( event: CustomEvent ) => { closeEventSource(); - onDone?.( event?.detail ); + + const fullSuggestion = removeLlamaArtifact( event?.detail ); + + onDone?.( fullSuggestion ); setRequestingState( 'done' ); }, [ onDone ] diff --git a/projects/js-packages/ai-client/src/hooks/use-image-generator/index.ts b/projects/js-packages/ai-client/src/hooks/use-image-generator/index.ts index e39e8eab1d111..b5fee88814e53 100644 --- a/projects/js-packages/ai-client/src/hooks/use-image-generator/index.ts +++ b/projects/js-packages/ai-client/src/hooks/use-image-generator/index.ts @@ -150,7 +150,7 @@ const getStableDiffusionImageGenerationPrompt = async ( */ const data = await askQuestionSync( prompt, { feature } ); - return data.choices?.[ 0 ]?.message?.content; + return data; }; const useImageGenerator = () => { diff --git a/projects/js-packages/ai-client/src/hooks/use-save-to-media-library/index.ts b/projects/js-packages/ai-client/src/hooks/use-save-to-media-library/index.ts new file mode 100644 index 0000000000000..88abbceeb94f2 --- /dev/null +++ b/projects/js-packages/ai-client/src/hooks/use-save-to-media-library/index.ts @@ -0,0 +1,95 @@ +/** + * External dependencies + */ +import { isBlobURL } from '@wordpress/blob'; +import { useSelect } from '@wordpress/data'; +import { useState } from '@wordpress/element'; +import debugFactory from 'debug'; +/** + * Types + */ +import type { BlockEditorStore } from '../../types.js'; + +const debug = debugFactory( 'ai-client:save-to-media-library' ); + +/** + * Hook to save an image to the media library. + * + * @returns {object} Object with the loading state and the function to save the image to the media library. + */ +export default function useSaveToMediaLibrary() { + const [ isLoading, setIsLoading ] = useState( false ); + const { getSettings } = useSelect( + select => select( 'core/block-editor' ), + [] + ) as BlockEditorStore[ 'selectors' ]; + + const saveToMediaLibrary = ( + url: string, + name?: string + ): Promise< { id: string; url: string } > => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const settings = getSettings() as any; + + return new Promise( ( resolve, reject ) => { + setIsLoading( true ); + + debug( 'Fetching image from URL' ); + + fetch( url ) + .then( response => { + debug( 'Transforming response to blob' ); + + response + .blob() + .then( ( blob: Blob ) => { + debug( 'Uploading blob to media library' ); + const filesList = Array< File | Blob >(); + + if ( name ) { + filesList.push( new File( [ blob ], name ) ); + } else { + filesList.push( blob ); + } + + settings.mediaUpload( { + allowedTypes: [ 'image' ], + filesList, + onFileChange( [ image ] ) { + if ( isBlobURL( image?.url ) ) { + return; + } + + if ( image ) { + debug( 'Image uploaded to media library', image ); + resolve( image ); + } + + setIsLoading( false ); + }, + onError( message ) { + debug( 'Error uploading image to media library:', message ); + reject( message ); + setIsLoading( false ); + }, + } ); + } ) + .catch( e => { + debug( 'Error transforming response to blob:', e?.message ); + reject( e?.message ); + setIsLoading( false ); + } ); + } ) + .catch( e => { + debug( 'Error fetching image from URL:', e?.message ); + reject( e?.message ); + setIsLoading( false ); + } ); + } ); + }; + + return { + isLoading, + saveToMediaLibrary, + }; +} diff --git a/projects/js-packages/ai-client/src/index.ts b/projects/js-packages/ai-client/src/index.ts index b92dafb6040c3..fb185b2309ea2 100644 --- a/projects/js-packages/ai-client/src/index.ts +++ b/projects/js-packages/ai-client/src/index.ts @@ -4,6 +4,7 @@ export { default as requestJwt } from './jwt/index.js'; export { default as SuggestionsEventSource } from './suggestions-event-source/index.js'; export { default as askQuestion } from './ask-question/index.js'; +export { default as askQuestionSync } from './ask-question/sync.js'; export { default as transcribeAudio } from './audio-transcription/index.js'; /* @@ -40,3 +41,8 @@ export * from './types.js'; * Libs */ export * from './libs/index.js'; + +/* + * Logo Generator + */ +export * from './logo-generator/index.js'; diff --git a/projects/js-packages/ai-client/src/libs/index.ts b/projects/js-packages/ai-client/src/libs/index.ts index 31f354e87d07f..a73828a0c8853 100644 --- a/projects/js-packages/ai-client/src/libs/index.ts +++ b/projects/js-packages/ai-client/src/libs/index.ts @@ -3,6 +3,7 @@ export { HTMLToMarkdown, renderHTMLFromMarkdown, renderMarkdownFromHTML, + fixes, } from './markdown/index.js'; export type { RenderHTMLRules } from './markdown/index.js'; diff --git a/projects/js-packages/ai-client/src/libs/markdown/index.ts b/projects/js-packages/ai-client/src/libs/markdown/index.ts index d0a811f4e3507..024123327ca9f 100644 --- a/projects/js-packages/ai-client/src/libs/markdown/index.ts +++ b/projects/js-packages/ai-client/src/libs/markdown/index.ts @@ -2,7 +2,7 @@ * Internal dependencies */ import HTMLToMarkdown from './html-to-markdown.js'; -import MarkdownToHTML from './markdown-to-html.js'; +import MarkdownToHTML, { fixes } from './markdown-to-html.js'; /** * Types */ @@ -29,4 +29,4 @@ const renderMarkdownFromHTML = ( { content }: { content: string } ) => { return defaultHTMLConverter.render( { content } ); }; -export { MarkdownToHTML, HTMLToMarkdown, renderHTMLFromMarkdown, renderMarkdownFromHTML }; +export { MarkdownToHTML, HTMLToMarkdown, renderHTMLFromMarkdown, renderMarkdownFromHTML, fixes }; diff --git a/projects/js-packages/ai-client/src/libs/markdown/markdown-to-html.ts b/projects/js-packages/ai-client/src/libs/markdown/markdown-to-html.ts index c1e07e9969406..54924cbce4ff6 100644 --- a/projects/js-packages/ai-client/src/libs/markdown/markdown-to-html.ts +++ b/projects/js-packages/ai-client/src/libs/markdown/markdown-to-html.ts @@ -7,7 +7,7 @@ import MarkdownIt from 'markdown-it'; */ import type { Options } from 'markdown-it'; -export type Fix = 'list' | 'paragraph' | 'listItem'; +export type Fix = 'list' | 'paragraph' | 'listItem' | 'table'; const addListComments = ( content: string ) => { return ( @@ -30,9 +30,13 @@ const addListComments = ( content: string ) => { }; type Fixes = { - [ key in Fix ]: ( content: string, extension?: boolean ) => string; + [ key in Fix ]: ( + content: string, + extension?: boolean, + options?: { [ key: string ]: unknown } + ) => string; }; -const fixes: Fixes = { +export const fixes: Fixes = { list: ( content: string, extension = false ) => { // Fix list indentation const fixedIndentation = content @@ -61,6 +65,19 @@ const fixes: Fixes = { // Fix encoding of
tags return content.replaceAll( /\s*<br \/>\s*/g, '
' ); }, + table: ( content: string, extension = false, { hasFixedLayout = false } ) => { + if ( ! extension ) { + return content; + } + + if ( content.startsWith( '${ content }`; + }, }; const defaultMarkdownItOptions: Options = { diff --git a/projects/js-packages/ai-client/src/logo-generator/assets/icons/ai.tsx b/projects/js-packages/ai-client/src/logo-generator/assets/icons/ai.tsx new file mode 100644 index 0000000000000..82e6f8c64816d --- /dev/null +++ b/projects/js-packages/ai-client/src/logo-generator/assets/icons/ai.tsx @@ -0,0 +1,21 @@ +/** + * Internal dependencies + */ +import './icons.scss'; + +export default () => { + return ( + + + + + + ); +}; diff --git a/projects/js-packages/ai-client/src/logo-generator/assets/icons/check.tsx b/projects/js-packages/ai-client/src/logo-generator/assets/icons/check.tsx new file mode 100644 index 0000000000000..9c3b17395396c --- /dev/null +++ b/projects/js-packages/ai-client/src/logo-generator/assets/icons/check.tsx @@ -0,0 +1,23 @@ +/** + * Internal dependencies + */ +import './icons.scss'; + +export default () => { + return ( + + + + ); +}; diff --git a/projects/js-packages/ai-client/src/logo-generator/assets/icons/icons.scss b/projects/js-packages/ai-client/src/logo-generator/assets/icons/icons.scss new file mode 100644 index 0000000000000..1ad0242848589 --- /dev/null +++ b/projects/js-packages/ai-client/src/logo-generator/assets/icons/icons.scss @@ -0,0 +1,5 @@ +.jetpack-ai-logo-generator-icon { + path { + fill: var(--color-link, #3858e9); + } +} diff --git a/projects/js-packages/ai-client/src/logo-generator/assets/icons/logo.tsx b/projects/js-packages/ai-client/src/logo-generator/assets/icons/logo.tsx new file mode 100644 index 0000000000000..c6e8be7cb8a86 --- /dev/null +++ b/projects/js-packages/ai-client/src/logo-generator/assets/icons/logo.tsx @@ -0,0 +1,23 @@ +/** + * Internal dependencies + */ +import './icons.scss'; + +export default () => { + return ( + + + + ); +}; diff --git a/projects/js-packages/ai-client/src/logo-generator/assets/icons/media.tsx b/projects/js-packages/ai-client/src/logo-generator/assets/icons/media.tsx new file mode 100644 index 0000000000000..4b54e9560dbfb --- /dev/null +++ b/projects/js-packages/ai-client/src/logo-generator/assets/icons/media.tsx @@ -0,0 +1,24 @@ +/** + * Internal dependencies + */ +import './icons.scss'; + +export default () => { + return ( + + + + + ); +}; diff --git a/projects/js-packages/ai-client/src/logo-generator/assets/images/jetpack-logo.svg b/projects/js-packages/ai-client/src/logo-generator/assets/images/jetpack-logo.svg new file mode 100644 index 0000000000000..aa0b8f87aecf4 --- /dev/null +++ b/projects/js-packages/ai-client/src/logo-generator/assets/images/jetpack-logo.svg @@ -0,0 +1,4 @@ + + + + diff --git a/projects/js-packages/ai-client/src/logo-generator/assets/images/loader.gif b/projects/js-packages/ai-client/src/logo-generator/assets/images/loader.gif new file mode 100644 index 0000000000000..4af5a96b0ed83 Binary files /dev/null and b/projects/js-packages/ai-client/src/logo-generator/assets/images/loader.gif differ diff --git a/projects/js-packages/ai-client/src/logo-generator/assets/index.d.ts b/projects/js-packages/ai-client/src/logo-generator/assets/index.d.ts new file mode 100644 index 0000000000000..3eebb0d372164 --- /dev/null +++ b/projects/js-packages/ai-client/src/logo-generator/assets/index.d.ts @@ -0,0 +1,3 @@ +declare module '*.gif'; +declare module '*.png'; +declare module '*.svg'; diff --git a/projects/js-packages/ai-client/src/logo-generator/components/feature-fetch-failure-screen.tsx b/projects/js-packages/ai-client/src/logo-generator/components/feature-fetch-failure-screen.tsx new file mode 100644 index 0000000000000..dc26c0570e1ca --- /dev/null +++ b/projects/js-packages/ai-client/src/logo-generator/components/feature-fetch-failure-screen.tsx @@ -0,0 +1,35 @@ +/** + * External dependencies + */ +import { Button } from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; +/** + * Types + */ +import type React from 'react'; + +export const FeatureFetchFailureScreen: React.FC< { + onCancel: () => void; + onRetry: () => void; +} > = ( { onCancel, onRetry } ) => { + const errorMessage = __( + 'We are sorry. There was an error loading your Jetpack AI account settings. Please, try again.', + 'jetpack-ai-client' + ); + + return ( +
+
+ { errorMessage } +
+
+ + +
+
+ ); +}; diff --git a/projects/js-packages/ai-client/src/logo-generator/components/first-load-screen.scss b/projects/js-packages/ai-client/src/logo-generator/components/first-load-screen.scss new file mode 100644 index 0000000000000..d70d0d922f458 --- /dev/null +++ b/projects/js-packages/ai-client/src/logo-generator/components/first-load-screen.scss @@ -0,0 +1,12 @@ +.jetpack-ai-logo-generator-modal__loading-wrapper { + display: flex; + flex-direction: column; + align-items: center; + height: 100%; + justify-content: center; + flex-grow: 1; +} + +.jetpack-ai-logo-generator-modal__loader { + margin-bottom: 29px; +} diff --git a/projects/js-packages/ai-client/src/logo-generator/components/first-load-screen.tsx b/projects/js-packages/ai-client/src/logo-generator/components/first-load-screen.tsx new file mode 100644 index 0000000000000..92456812fd2c2 --- /dev/null +++ b/projects/js-packages/ai-client/src/logo-generator/components/first-load-screen.tsx @@ -0,0 +1,32 @@ +/** + * External dependencies + */ +import { __ } from '@wordpress/i18n'; +import React from 'react'; +/** + * Internal dependencies + */ +import { ImageLoader } from './image-loader.js'; +import './first-load-screen.scss'; + +export const FirstLoadScreen: React.FC< { + state?: 'loadingFeature' | 'analyzing' | 'generating'; +} > = ( { state = 'loadingFeature' } ) => { + const loadingLabel = __( 'Loading…', 'jetpack-ai-client' ); + const analyzingLabel = __( + 'Analyzing your site to create the perfect logo…', + 'jetpack-ai-client' + ); + const generatingLabel = __( 'Generating logo…', 'jetpack-ai-client' ); + + return ( +
+ + + { state === 'loadingFeature' && loadingLabel } + { state === 'analyzing' && analyzingLabel } + { state === 'generating' && generatingLabel } + +
+ ); +}; diff --git a/projects/js-packages/ai-client/src/logo-generator/components/generator-modal.scss b/projects/js-packages/ai-client/src/logo-generator/components/generator-modal.scss new file mode 100644 index 0000000000000..2221ff9ae6969 --- /dev/null +++ b/projects/js-packages/ai-client/src/logo-generator/components/generator-modal.scss @@ -0,0 +1,92 @@ +@import '@automattic/jetpack-base-styles/root-variables'; + +.jetpack-ai-logo-generator-modal { + @media (min-width: 960px) { + max-height: 90%; + } + + .components-modal__header { + border-bottom: 1px solid var(--studio-gray-5, #dcdcde); + padding: 20px 24px; + } + + .components-modal__content { + padding: 32px 32px 2px 32px; + margin-bottom: 30px; + + > div:not(.components-modal__header) { + height: 100%; + } + } + + .components-button { + &:focus:not(:disabled):not(.is-primary) { + box-shadow: 0 0 0 2px var(--color-link, #3858e9); + } + + &.is-link { + text-decoration: none; + color: var(--color-link, #3858e9); + } + } +} + +.jetpack-ai-logo-generator-modal__body { + display: flex; + flex-direction: column; + gap: 16px; + height: 100%; + + @media (min-width: 700px) { + width: 700px; + min-height: 409px; + + &.notice-modal { + width: 470px; + min-height: unset; + } + } +} + +.jetpack-ai-logo-generator__footer { + display: flex; + + .jetpack-ai-logo-generator__feedback-button { + display: flex; + gap: 4px; + align-items: center; + margin-top: 8px; + + .icon { + color: var(--studio-gray-20); + } + } +} + +.jetpack-ai-logo-generator-modal__notice-message-wrapper { + display: flex; + flex-direction: column; + gap: 24px; +} + +.jetpack-ai-logo-generator-modal__notice-message { + font-size: var( --font-body-small ); +} + +.jetpack-ai-logo-generator-modal__notice-actions { + display: flex; + justify-content: flex-end; + gap: 12px; +} + +.jetpack-ai-logo-generator__accept { + display: flex; + flex-direction: column; + gap: 64px; +} + +.jetpack-ai-logo-generator__accept-actions { + display: flex; + justify-content: flex-end; + gap: 22px; +} diff --git a/projects/js-packages/ai-client/src/logo-generator/components/generator-modal.tsx b/projects/js-packages/ai-client/src/logo-generator/components/generator-modal.tsx new file mode 100644 index 0000000000000..d7729ab2a6735 --- /dev/null +++ b/projects/js-packages/ai-client/src/logo-generator/components/generator-modal.tsx @@ -0,0 +1,291 @@ +/** + * External dependencies + */ +import { useAnalytics } from '@automattic/jetpack-shared-extension-utils'; +import { Modal, Button } from '@wordpress/components'; +import { useDispatch } from '@wordpress/data'; +import { __ } from '@wordpress/i18n'; +import { external, Icon } from '@wordpress/icons'; +import clsx from 'clsx'; +import debugFactory from 'debug'; +import { useState, useEffect, useCallback, useRef } from 'react'; +/** + * Internal dependencies + */ +import { + DEFAULT_LOGO_COST, + EVENT_MODAL_OPEN, + EVENT_FEEDBACK, + EVENT_MODAL_CLOSE, + EVENT_PLACEMENT_QUICK_LINKS, + EVENT_GENERATE, +} from '../constants.js'; +import useLogoGenerator from '../hooks/use-logo-generator.js'; +import useRequestErrors from '../hooks/use-request-errors.js'; +import { isLogoHistoryEmpty, clearDeletedMedia } from '../lib/logo-storage.js'; +import { STORE_NAME } from '../store/index.js'; +import { FeatureFetchFailureScreen } from './feature-fetch-failure-screen.js'; +import { FirstLoadScreen } from './first-load-screen.js'; +import { HistoryCarousel } from './history-carousel.js'; +import { LogoPresenter } from './logo-presenter.js'; +import { Prompt } from './prompt.js'; +import { UpgradeScreen } from './upgrade-screen.js'; +import { VisitSiteBanner } from './visit-site-banner.js'; +import './generator-modal.scss'; +/** + * Types + */ +import type { GeneratorModalProps } from '../types.js'; +import type React from 'react'; + +const debug = debugFactory( 'jetpack-ai-calypso:generator-modal' ); + +export const GeneratorModal: React.FC< GeneratorModalProps > = ( { + isOpen, + onClose, + siteDetails, + context, +} ) => { + const { tracks } = useAnalytics(); + const { recordEvent: recordTracksEvent } = tracks; + const { setSiteDetails, fetchAiAssistantFeature, loadLogoHistory } = useDispatch( STORE_NAME ); + const [ loadingState, setLoadingState ] = useState< + 'loadingFeature' | 'analyzing' | 'generating' | null + >( null ); + const [ initialPrompt, setInitialPrompt ] = useState< string | undefined >(); + const needsToHandleModalOpen = useRef< boolean >( true ); + const requestedFeatureData = useRef< boolean >( false ); + const [ needsFeature, setNeedsFeature ] = useState( false ); + const [ needsMoreRequests, setNeedsMoreRequests ] = useState( false ); + const [ upgradeURL, setUpgradeURL ] = useState( '' ); + const { selectedLogo, getAiAssistantFeature, generateFirstPrompt, generateLogo, setContext } = + useLogoGenerator(); + const { featureFetchError, firstLogoPromptFetchError, clearErrors } = useRequestErrors(); + const siteId = siteDetails?.ID; + const siteURL = siteDetails?.URL; + const [ logoAccepted, setLogoAccepted ] = useState( false ); + + // First fetch the feature data so we have the most up-to-date info from the backend. + const feature = getAiAssistantFeature(); + + const generateFirstLogo = useCallback( async () => { + try { + // First generate the prompt based on the site's data. + setLoadingState( 'analyzing' ); + recordTracksEvent( EVENT_GENERATE, { context, tool: 'first-prompt' } ); + const prompt = await generateFirstPrompt(); + setInitialPrompt( prompt ); + + // Then generate the logo based on the prompt. + setLoadingState( 'generating' ); + await generateLogo( { prompt } ); + setLoadingState( null ); + } catch ( error ) { + debug( 'Error generating first logo', error ); + setLoadingState( null ); + } + }, [ context, generateFirstPrompt, generateLogo ] ); + + /* + * Called ONCE to check the feature data to make sure the site is allowed to do the generation. + * Also, checks site history and trigger a new generation in case there are no logos to present. + */ + const initializeModal = useCallback( async () => { + try { + const hasHistory = ! isLogoHistoryEmpty( String( siteId ) ); + const logoCost = feature?.costs?.[ 'jetpack-ai-logo-generator' ]?.logo ?? DEFAULT_LOGO_COST; + const promptCreationCost = 1; + const currentLimit = feature?.currentTier?.value || 0; + const currentUsage = feature?.usagePeriod?.requestsCount || 0; + const isUnlimited = currentLimit === 1; + const hasNoNextTier = ! feature?.nextTier; // If there is no next tier, the user cannot upgrade. + + // The user needs an upgrade immediately if they have no logos and not enough requests remaining for one prompt and one logo generation. + const siteNeedsMoreRequests = + ! isUnlimited && + ! hasNoNextTier && + ! hasHistory && + currentLimit - currentUsage < logoCost + promptCreationCost; + + // If the site requires an upgrade, set the upgrade URL and show the upgrade screen immediately. + setNeedsFeature( ! feature?.hasFeature ?? true ); + setNeedsMoreRequests( siteNeedsMoreRequests ); + + if ( ! feature?.hasFeature || siteNeedsMoreRequests ) { + const siteUpgradeURL = new URL( + `${ location.origin }/checkout/${ siteDetails?.domain }/${ feature?.nextTier?.slug }` + ); + siteUpgradeURL.searchParams.set( 'redirect_to', location.href ); + setUpgradeURL( siteUpgradeURL.toString() ); + setLoadingState( null ); + return; + } + + // Load the logo history and clear any deleted media. + await clearDeletedMedia( String( siteId ) ); + loadLogoHistory( siteId ); + + // If there is any logo, we do not need to generate a first logo again. + if ( ! isLogoHistoryEmpty( String( siteId ) ) ) { + setLoadingState( null ); + return; + } + + // If the site does not require an upgrade and has no logos stored, generate the first prompt based on the site's data. + generateFirstLogo(); + } catch ( error ) { + debug( 'Error fetching feature', error ); + setLoadingState( null ); + } + }, [ + feature, + generateFirstLogo, + loadLogoHistory, + clearDeletedMedia, + isLogoHistoryEmpty, + siteId, + ] ); + + const handleModalOpen = useCallback( async () => { + setContext( context ); + recordTracksEvent( EVENT_MODAL_OPEN, { context, placement: EVENT_PLACEMENT_QUICK_LINKS } ); + + initializeModal(); + }, [ setContext, context, initializeModal ] ); + + const closeModal = () => { + // Reset the state when the modal is closed, so we trigger the modal initialization again when it's opened. + needsToHandleModalOpen.current = true; + onClose(); + setLoadingState( null ); + setNeedsFeature( false ); + setNeedsMoreRequests( false ); + clearErrors(); + setLogoAccepted( false ); + recordTracksEvent( EVENT_MODAL_CLOSE, { context, placement: EVENT_PLACEMENT_QUICK_LINKS } ); + }; + + const handleApplyLogo = () => { + setLogoAccepted( true ); + }; + + const handleCloseAndReload = () => { + closeModal(); + + setTimeout( () => { + // Reload the page to update the logo. + window.location.reload(); + }, 1000 ); + }; + + const handleFeedbackClick = () => { + recordTracksEvent( EVENT_FEEDBACK, { context } ); + }; + + // Set site details when siteId changes + useEffect( () => { + if ( siteId ) { + setSiteDetails( siteDetails ); + } + + // When the site details are set, we need to fetch the feature data. + if ( ! requestedFeatureData.current ) { + requestedFeatureData.current = true; + fetchAiAssistantFeature(); + } + }, [ siteId, siteDetails, setSiteDetails ] ); + + // Handles modal opening logic + useEffect( () => { + // While the modal is not open, the siteId is not set, or the feature data is not available, do nothing. + if ( ! isOpen || ! siteId || ! feature?.costs ) { + return; + } + + // Prevent multiple calls of the handleModalOpen function + if ( needsToHandleModalOpen.current ) { + needsToHandleModalOpen.current = false; + handleModalOpen(); + } + }, [ isOpen, siteId, handleModalOpen, feature ] ); + + let body: React.ReactNode; + + if ( loadingState ) { + body = ; + } else if ( featureFetchError || firstLogoPromptFetchError ) { + body = ; + } else if ( needsFeature || needsMoreRequests ) { + body = ( + + ); + } else { + body = ( + <> + { ! logoAccepted && } + + { logoAccepted ? ( +
+ +
+ + +
+
+ ) : ( + <> + +
+ +
+ + ) } + + ); + } + + return ( + <> + { isOpen && ( + +
+ { body } +
+
+ ) } + + ); +}; diff --git a/projects/js-packages/ai-client/src/logo-generator/components/history-carousel.scss b/projects/js-packages/ai-client/src/logo-generator/components/history-carousel.scss new file mode 100644 index 0000000000000..d7f2107cfe02e --- /dev/null +++ b/projects/js-packages/ai-client/src/logo-generator/components/history-carousel.scss @@ -0,0 +1,36 @@ +@import '@automattic/jetpack-base-styles/root-variables'; + +.jetpack-ai-logo-generator__carousel { + display: flex; + gap: 8px; + overflow-x: auto; + flex-shrink: 0; + + .components-button { + height: unset; + padding: unset; + } + + @media (min-width: 700px) { + padding: 2px; + } +} + +.jetpack-ai-logo-generator__carousel-logo { + display: flex; + justify-content: center; + align-items: center; + border: 1px solid var(--studio-gray-5, #dcdcde); + border-radius: 2px; + flex-shrink: 0; + + &.is-selected { + border-color: var(--color-link, #3858e9); + border-width: 1.5px; + } + + img { + width: 48px; + height: 48px; + } +} diff --git a/projects/js-packages/ai-client/src/logo-generator/components/history-carousel.tsx b/projects/js-packages/ai-client/src/logo-generator/components/history-carousel.tsx new file mode 100644 index 0000000000000..d82b34b82d2df --- /dev/null +++ b/projects/js-packages/ai-client/src/logo-generator/components/history-carousel.tsx @@ -0,0 +1,57 @@ +/** + * External dependencies + */ +import { useAnalytics } from '@automattic/jetpack-shared-extension-utils'; +import { Button } from '@wordpress/components'; +import clsx from 'clsx'; +/** + * Internal dependencies + */ +import { EVENT_NAVIGATE } from '../constants.js'; +import useLogoGenerator from '../hooks/use-logo-generator.js'; +import './history-carousel.scss'; +/** + * Types + */ +import type React from 'react'; + +export const HistoryCarousel: React.FC = () => { + const { tracks } = useAnalytics(); + const { recordEvent: recordTracksEvent } = tracks; + const { logos, selectedLogo, setSelectedLogoIndex, context } = useLogoGenerator(); + + const handleClick = ( index: number ) => { + recordTracksEvent( EVENT_NAVIGATE, { + context, + logos_count: logos.length, + selected_logo: index + 1, + } ); + setSelectedLogoIndex( index ); + }; + + const thumbnailFrom = ( url: string ): string => { + const thumbnailURL = new URL( url ); + + if ( ! thumbnailURL.searchParams.has( 'resize' ) ) { + thumbnailURL.searchParams.append( 'resize', '48,48' ); + } + + return thumbnailURL.toString(); + }; + + return ( +
+ { logos.map( ( logo, index ) => ( + + ) ) } +
+ ); +}; diff --git a/projects/js-packages/ai-client/src/logo-generator/components/image-loader.tsx b/projects/js-packages/ai-client/src/logo-generator/components/image-loader.tsx new file mode 100644 index 0000000000000..ea7b54a2eecec --- /dev/null +++ b/projects/js-packages/ai-client/src/logo-generator/components/image-loader.tsx @@ -0,0 +1,22 @@ +/** + * External dependencies + */ +import clsx from 'clsx'; +/** + * Internal dependencies + */ +import loader from '../assets/images/loader.gif'; +/** + * Types + */ +import type React from 'react'; + +export const ImageLoader: React.FC< { className?: string } > = ( { className = null } ) => { + return ( + Loading + ); +}; diff --git a/projects/js-packages/ai-client/src/logo-generator/components/logo-presenter.scss b/projects/js-packages/ai-client/src/logo-generator/components/logo-presenter.scss new file mode 100644 index 0000000000000..7086f47cf6638 --- /dev/null +++ b/projects/js-packages/ai-client/src/logo-generator/components/logo-presenter.scss @@ -0,0 +1,116 @@ +@import '@automattic/jetpack-base-styles/root-variables'; + +.jetpack-ai-logo-generator-modal-presenter__wrapper { + display: flex; + flex-direction: column; + gap: 8px; +} + +.jetpack-ai-logo-generator-modal-presenter { + display: flex; +} + +.jetpack-ai-logo-generator-modal-presenter__content { + border-radius: 4px; + background: var(--studio-gray-0, #f6f7f7); + display: flex; + align-items: center; + flex-grow: 1; + max-height: 229px; + + @media (max-width: 700px) { + flex-direction: column; + max-height: unset; + } +} + +.jetpack-ai-logo-generator-modal-presenter__rectangle { + position: relative; + width: 0; + + &::after { + width: 15px; + height: 15px; + transform: rotate(-45deg); + background-color: var(--studio-gray-0, #f6f7f7); + content: ""; + position: absolute; + top: -7.5px; + left: -66px; + } +} + +.jetpack-ai-logo-generator-modal-presenter__loading-text { + flex-grow: 1; + text-align: center; +} + +.jetpack-ai-logo-generator-modal-presenter__logo { + width: 198px; + height: 198px; + margin: 16px 30px 16px 16px; + + @media (max-width: 700px) { + margin: 16px; + } +} + +.jetpack-ai-logo-generator-modal-presenter__action-wrapper { + height: 100%; + flex-grow: 1; + margin-right: 32px; + display: flex; + flex-direction: column; + padding: 16px 0; + box-sizing: border-box; + + @media (max-width: 700px) { + margin-left: 32px; + } +} + +.jetpack-ai-logo-generator-modal-presenter__description { + flex-grow: 1; + padding-top: 16px; + color: var(--studio-gray-50, #646970); + overflow-y: auto; +} + +.jetpack-ai-logo-generator-modal-presenter__actions { + padding-top: 16px; + margin-top: 16px; + border-top: 1px solid var(--studio-gray-5, #dcdcde); + display: flex; + gap: 24px; +} + +.jetpack-ai-logo-generator-modal-presenter__action { + display: flex; + align-items: center; + + &.components-button, + &.components-button:hover, + &.components-button:active { + color: var(--color-link, #3858e9); + } + + .action-text { + font-size: var(--font-body-extra-small); + } + + .jetpack-ai-logo-generator-icon { + margin-right: 8px; + } +} + +.jetpack-ai-logo-generator-modal-presenter__success-wrapper { + display: flex; + flex-direction: column; + align-items: center; + gap: 16px; + width: 100%; + + @media (max-width: 700px) { + padding-bottom: 16px; + } +} diff --git a/projects/js-packages/ai-client/src/logo-generator/components/logo-presenter.tsx b/projects/js-packages/ai-client/src/logo-generator/components/logo-presenter.tsx new file mode 100644 index 0000000000000..1d8e48b50bb09 --- /dev/null +++ b/projects/js-packages/ai-client/src/logo-generator/components/logo-presenter.tsx @@ -0,0 +1,234 @@ +/** + * External dependencies + */ +import { useAnalytics } from '@automattic/jetpack-shared-extension-utils'; +import { Button, Icon } from '@wordpress/components'; +import { useDispatch } from '@wordpress/data'; +import { __ } from '@wordpress/i18n'; +import debugFactory from 'debug'; +/** + * Internal dependencies + */ +import CheckIcon from '../assets/icons/check.js'; +import LogoIcon from '../assets/icons/logo.js'; +import MediaIcon from '../assets/icons/media.js'; +import { EVENT_SAVE, EVENT_USE } from '../constants.js'; +import useLogoGenerator from '../hooks/use-logo-generator.js'; +import useRequestErrors from '../hooks/use-request-errors.js'; +import { updateLogo } from '../lib/logo-storage.js'; +import { STORE_NAME } from '../store/index.js'; +import { ImageLoader } from './image-loader.js'; +import './logo-presenter.scss'; +/** + * Types + */ +import type { Logo } from '../store/types.js'; +import type { LogoPresenterProps } from '../types.js'; +import type React from 'react'; + +const debug = debugFactory( 'jetpack-ai-calypso:logo-presenter' ); + +const SaveInLibraryButton: React.FC< { siteId: string } > = ( { siteId } ) => { + const { tracks } = useAnalytics(); + const { recordEvent: recordTracksEvent } = tracks; + const { + saveLogo, + selectedLogo, + isSavingLogoToLibrary: saving, + logos, + selectedLogoIndex, + context, + } = useLogoGenerator(); + const saved = !! selectedLogo?.mediaId; + + const { loadLogoHistory } = useDispatch( STORE_NAME ); + + const handleClick = async () => { + if ( ! saved && ! saving ) { + recordTracksEvent( EVENT_SAVE, { + context, + logos_count: logos.length, + selected_logo: selectedLogoIndex ? selectedLogoIndex + 1 : 0, + } ); + + try { + const savedLogo = await saveLogo( selectedLogo ); + + // Update localStorage + updateLogo( { + siteId, + url: selectedLogo.url, + newUrl: savedLogo.mediaURL, + mediaId: savedLogo.mediaId, + } ); + + // Update state + loadLogoHistory( siteId ); + } catch ( error ) { + debug( 'Error saving logo', error ); + } + } + }; + + const savingLabel = __( 'Saving…', 'jetpack-ai-client' ); + const savedLabel = __( 'Saved', 'jetpack-ai-client' ); + + return ! saving && ! saved ? ( + + ) : ( + + ); +}; + +const UseOnSiteButton: React.FC< { onApplyLogo: () => void } > = ( { onApplyLogo } ) => { + const { tracks } = useAnalytics(); + const { recordEvent: recordTracksEvent } = tracks; + const { + applyLogo, + isSavingLogoToLibrary, + isApplyingLogo, + selectedLogo, + logos, + selectedLogoIndex, + context, + } = useLogoGenerator(); + + const handleClick = async () => { + if ( ! isApplyingLogo && ! isSavingLogoToLibrary ) { + recordTracksEvent( EVENT_USE, { + context, + logos_count: logos.length, + selected_logo: selectedLogoIndex != null ? selectedLogoIndex + 1 : 0, + } ); + + try { + await applyLogo(); + onApplyLogo(); + } catch ( error ) { + debug( 'Error applying logo', error ); + } + } + }; + + return isApplyingLogo && ! isSavingLogoToLibrary ? ( + + ) : ( + + ); +}; + +const LogoLoading: React.FC = () => { + return ( + <> + + + { __( 'Generating new logo…', 'jetpack-ai-client' ) } + + + ); +}; + +const LogoReady: React.FC< { siteId: string; logo: Logo; onApplyLogo: () => void } > = ( { + siteId, + logo, + onApplyLogo, +} ) => { + return ( + <> + { +
+ + { logo.description } + +
+ + +
+
+ + ); +}; + +const LogoUpdated: React.FC< { logo: Logo } > = ( { logo } ) => { + return ( + <> + { +
+ } /> + { __( 'Your logo has been successfully updated!', 'jetpack-ai-client' ) } +
+ + ); +}; + +export const LogoPresenter: React.FC< LogoPresenterProps > = ( { + logo = null, + loading = false, + onApplyLogo, + logoAccepted = false, + siteId, +} ) => { + const { isRequestingImage } = useLogoGenerator(); + const { saveToLibraryError, logoUpdateError } = useRequestErrors(); + + if ( ! logo ) { + return null; + } + + let logoContent: React.ReactNode; + + if ( loading || isRequestingImage ) { + logoContent = ; + } else if ( logoAccepted ) { + logoContent = ; + } else { + logoContent = ( + + ); + } + + return ( +
+
+
{ logoContent }
+ { ! logoAccepted && ( +
+ ) } +
+ { saveToLibraryError && ( +
+ { __( 'Error saving the logo to your library. Please try again.', 'jetpack-ai-client' ) } +
+ ) } + { logoUpdateError && ( +
+ { __( 'Error applying the logo to your site. Please try again.', 'jetpack-ai-client' ) } +
+ ) } +
+ ); +}; diff --git a/projects/js-packages/ai-client/src/logo-generator/components/prompt.scss b/projects/js-packages/ai-client/src/logo-generator/components/prompt.scss new file mode 100644 index 0000000000000..bbc7be95382b5 --- /dev/null +++ b/projects/js-packages/ai-client/src/logo-generator/components/prompt.scss @@ -0,0 +1,102 @@ +@import '@automattic/jetpack-base-styles/root-variables'; + +.jetpack-ai-logo-generator__prompt { + display: flex; + flex-direction: column; + gap: 8px; + font-size: var(--font-body-small); +} + +.jetpack-ai-logo-generator__prompt-header { + display: flex; + justify-content: space-between; + align-items: flex-start; + align-self: stretch; + + .jetpack-ai-logo-generator__prompt-label { + font-weight: 500; + } + + .jetpack-ai-logo-generator__prompt-actions { + display: flex; + font-size: var(--font-body-extra-small); + line-height: 20px; + + .jetpack-ai-logo-generator-icon { + margin-right: 4px; + } + } +} + +.jetpack-ai-logo-generator__prompt-query { + display: flex; + padding: 8px 8px 8px var(--grid-unit-15, 16px); + justify-content: space-between; + align-items: flex-end; + align-self: stretch; + border-radius: calc(4px * 2); + border: 1px solid var(--studio-gray-10, #ccc); + background: var(--studio-white, #fff); + gap: 8px; + + @media (min-width: 700px) { + gap: 48px; + } + + .prompt-query__input { + border: 0; + resize: none; + flex-grow: 1; + padding: 6px 0; + vertical-align: baseline; + color: var(--studio-gray-100); + line-height: 1.6; + word-break: break-word; + + &:focus, + &:active { + outline: 0; + } + + &[contentEditable="false"] { + color: var(--studio-gray-50, #646970); + } + + &[data-placeholder]:empty::before { + content: attr(data-placeholder); + color: var(--studio-gray-50, #646970); + } + + &[data-placeholder]:empty:focus::before { + content: ""; + } + } +} + +.jetpack-ai-logo-generator__prompt-footer { + display: flex; + flex-direction: column; + gap: 8px; +} + +.jetpack-ai-logo-generator__prompt-requests { + color: var(--studio-gray-50, #646970); + font-size: var(--font-body-extra-small); + line-height: 21px; + display: flex; + + & .prompt-footer__icon { + height: 20px; + width: 20px; + + path { + fill: var(--studio-gray-20, #a7aaad); + } + } +} + +.jetpack-ai-logo-generator__prompt-error { + color: var(--studio-red-50, #d63638); + font-size: var(--font-body-extra-small); + line-height: 21px; +} diff --git a/projects/js-packages/ai-client/src/logo-generator/components/prompt.tsx b/projects/js-packages/ai-client/src/logo-generator/components/prompt.tsx new file mode 100644 index 0000000000000..8f75fd9cbbc91 --- /dev/null +++ b/projects/js-packages/ai-client/src/logo-generator/components/prompt.tsx @@ -0,0 +1,211 @@ +/** + * External dependencies + */ +import { useAnalytics } from '@automattic/jetpack-shared-extension-utils'; +import { Button, Tooltip } from '@wordpress/components'; +import { __, sprintf } from '@wordpress/i18n'; +import { Icon, info } from '@wordpress/icons'; +import debugFactory from 'debug'; +import { useCallback, useEffect, useState, useRef } from 'react'; +/** + * Internal dependencies + */ +import AiIcon from '../assets/icons/ai.js'; +import { + EVENT_GENERATE, + MINIMUM_PROMPT_LENGTH, + EVENT_UPGRADE, + EVENT_PLACEMENT_INPUT_FOOTER, +} from '../constants.js'; +import { useCheckout } from '../hooks/use-checkout.js'; +import useLogoGenerator from '../hooks/use-logo-generator.js'; +import useRequestErrors from '../hooks/use-request-errors.js'; +import { UpgradeNudge } from './upgrade-nudge.js'; +import './prompt.scss'; + +const debug = debugFactory( 'jetpack-ai-calypso:prompt-box' ); + +export const Prompt: React.FC< { initialPrompt?: string } > = ( { initialPrompt = '' } ) => { + const { tracks } = useAnalytics(); + const { recordEvent: recordTracksEvent } = tracks; + const [ prompt, setPrompt ] = useState< string >( initialPrompt ); + const [ requestsRemaining, setRequestsRemaining ] = useState( 0 ); + const { enhancePromptFetchError, logoFetchError } = useRequestErrors(); + const { nextTierCheckoutURL: checkoutUrl, hasNextTier } = useCheckout(); + const hasPrompt = prompt?.length >= MINIMUM_PROMPT_LENGTH; + + const { + generateLogo, + enhancePrompt, + setIsEnhancingPrompt, + isBusy, + isEnhancingPrompt, + site, + getAiAssistantFeature, + requireUpgrade, + context, + } = useLogoGenerator(); + + const enhancingLabel = __( 'Enhancing…', 'jetpack-ai-client' ); + const enhanceLabel = __( 'Enhance prompt', 'jetpack-ai-client' ); + const enhanceButtonLabel = isEnhancingPrompt ? enhancingLabel : enhanceLabel; + + const inputRef = useRef< HTMLDivElement | null >( null ); + + const onEnhance = useCallback( async () => { + debug( 'Enhancing prompt', prompt ); + setIsEnhancingPrompt( true ); + recordTracksEvent( EVENT_GENERATE, { context, tool: 'enhance-prompt' } ); + + try { + const enhancedPrompt = await enhancePrompt( { prompt } ); + setPrompt( enhancedPrompt ); + setIsEnhancingPrompt( false ); + } catch ( error ) { + debug( 'Error enhancing prompt', error ); + setIsEnhancingPrompt( false ); + } + }, [ context, enhancePrompt, prompt, setIsEnhancingPrompt ] ); + + const featureData = getAiAssistantFeature( String( site?.id || '' ) ); + + const currentLimit = featureData?.currentTier?.value || 0; + const currentUsage = featureData?.usagePeriod?.requestsCount || 0; + const isUnlimited = currentLimit === 1; + + useEffect( () => { + if ( currentLimit - currentUsage <= 0 ) { + setRequestsRemaining( 0 ); + } else { + setRequestsRemaining( currentLimit - currentUsage ); + } + }, [ currentLimit, currentUsage ] ); + + useEffect( () => { + // Update prompt text node after enhancement + if ( inputRef.current && inputRef.current.textContent !== prompt ) { + inputRef.current.textContent = prompt; + } + }, [ prompt ] ); + + const onGenerate = useCallback( async () => { + recordTracksEvent( EVENT_GENERATE, { context, tool: 'image' } ); + generateLogo( { prompt } ); + }, [ context, generateLogo, prompt ] ); + + const onPromptInput = ( event: React.ChangeEvent< HTMLInputElement > ) => { + setPrompt( event.target.textContent || '' ); + }; + + const onPromptPaste = ( event: React.ClipboardEvent< HTMLInputElement > ) => { + event.preventDefault(); + + // Paste plain text only + const text = event.clipboardData.getData( 'text/plain' ); + + const selection = window.getSelection(); + if ( ! selection || ! selection.rangeCount ) { + return; + } + selection.deleteFromDocument(); + const range = selection.getRangeAt( 0 ); + range.insertNode( document.createTextNode( text ) ); + selection.collapseToEnd(); + + setPrompt( inputRef.current?.textContent || '' ); + }; + + const onUpgradeClick = () => { + recordTracksEvent( EVENT_UPGRADE, { context, placement: EVENT_PLACEMENT_INPUT_FOOTER } ); + }; + + return ( +
+
+
+ { __( 'Describe your site:', 'jetpack-ai-client' ) } +
+
+ +
+
+
+
+ +
+
+ { ! isUnlimited && ! requireUpgrade && ( +
+
+ { sprintf( + // translators: %u is the number of requests + __( '%u requests remaining.', 'jetpack-ai-client' ), + requestsRemaining + ) } +
+ { hasNextTier && ( + <> +   + + + ) } +   + + + +
+ ) } + { ! isUnlimited && requireUpgrade && } + { enhancePromptFetchError && ( +
+ { __( 'Error enhancing prompt. Please try again.', 'jetpack-ai-client' ) } +
+ ) } + { logoFetchError && ( +
+ { __( 'Error generating logo. Please try again.', 'jetpack-ai-client' ) } +
+ ) } +
+
+ ); +}; diff --git a/projects/js-packages/ai-client/src/logo-generator/components/upgrade-nudge.scss b/projects/js-packages/ai-client/src/logo-generator/components/upgrade-nudge.scss new file mode 100644 index 0000000000000..e7178248255ae --- /dev/null +++ b/projects/js-packages/ai-client/src/logo-generator/components/upgrade-nudge.scss @@ -0,0 +1,43 @@ +@import '@automattic/jetpack-base-styles/root-variables'; + +.jetpack-upgrade-plan-banner .jetpack-upgrade-plan-banner__wrapper { + display: flex; + justify-content: space-between; + align-items: center; + font-size: var(--font-body-small); + background: var(--jp-black); + padding: 8px 16px; + border-radius: 2px; +} + +.jetpack-upgrade-plan-banner .jetpack-upgrade-plan-banner__wrapper .jetpack-upgrade-plan-banner__banner-description { + color: var(--jp-white); + line-height: 21px; + word-wrap: break-word; + vertical-align: middle; +} + +.jetpack-upgrade-plan-banner .jetpack-upgrade-plan-banner__wrapper .jetpack-upgrade-plan-banner__icon { + width: 24px; + height: 24px; + position: relative; + vertical-align: middle; + margin-right: 8px; + + path { + fill: var(--jp-gray-30); + } +} + +.jetpack-upgrade-plan-banner .jetpack-upgrade-plan-banner__wrapper .components-button { + height: auto; + line-height: 20px; + font-size: var(--font-body-extra-small); + font-weight: 600; + padding: 4px 8px; +} + +.jetpack-upgrade-plan-banner .jetpack-upgrade-plan-banner__wrapper .components-button.is-primary { + background: var(--jp-white); + color: var(--jp-black); +} diff --git a/projects/js-packages/ai-client/src/logo-generator/components/upgrade-nudge.tsx b/projects/js-packages/ai-client/src/logo-generator/components/upgrade-nudge.tsx new file mode 100644 index 0000000000000..8e96434ceaa39 --- /dev/null +++ b/projects/js-packages/ai-client/src/logo-generator/components/upgrade-nudge.tsx @@ -0,0 +1,58 @@ +/** + * External dependencies + */ +import { useAnalytics } from '@automattic/jetpack-shared-extension-utils'; +import { Button } from '@wordpress/components'; +import { createInterpolateElement } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { Icon, warning } from '@wordpress/icons'; +/** + * Internal dependencies + */ +import { EVENT_PLACEMENT_UPGRADE_PROMPT, EVENT_UPGRADE } from '../constants.js'; +import { useCheckout } from '../hooks/use-checkout.js'; +import useLogoGenerator from '../hooks/use-logo-generator.js'; +import './upgrade-nudge.scss'; + +export const UpgradeNudge = () => { + const { tracks } = useAnalytics(); + const { recordEvent: recordTracksEvent } = tracks; + const buttonText = __( 'Upgrade', 'jetpack-ai-client' ); + const upgradeMessage = createInterpolateElement( + __( + 'Not enough requests left to generate a logo. Upgrade now to increase it.', + 'jetpack-ai-client' + ), + { + strong: , + } + ); + + const { nextTierCheckoutURL: checkoutUrl } = useCheckout(); + const { context } = useLogoGenerator(); + + const handleUpgradeClick = () => { + recordTracksEvent( EVENT_UPGRADE, { context, placement: EVENT_PLACEMENT_UPGRADE_PROMPT } ); + }; + + return ( +
+
+
+ + + { upgradeMessage } + +
+ +
+
+ ); +}; diff --git a/projects/js-packages/ai-client/src/logo-generator/components/upgrade-screen.tsx b/projects/js-packages/ai-client/src/logo-generator/components/upgrade-screen.tsx new file mode 100644 index 0000000000000..8fa8f5c5c2ad9 --- /dev/null +++ b/projects/js-packages/ai-client/src/logo-generator/components/upgrade-screen.tsx @@ -0,0 +1,67 @@ +/** + * External dependencies + */ +import { useAnalytics } from '@automattic/jetpack-shared-extension-utils'; +import { Button } from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; +/** + * Internal dependencies + */ +import { EVENT_PLACEMENT_FREE_USER_SCREEN, EVENT_UPGRADE } from '../constants.js'; +import useLogoGenerator from '../hooks/use-logo-generator.js'; +/** + * Types + */ +import type React from 'react'; + +export const UpgradeScreen: React.FC< { + onCancel: () => void; + upgradeURL: string; + reason: 'feature' | 'requests'; +} > = ( { onCancel, upgradeURL, reason } ) => { + const { tracks } = useAnalytics(); + const { recordEvent: recordTracksEvent } = tracks; + const upgradeMessageFeature = __( + 'Upgrade your Jetpack AI for access to exclusive features, including logo generation. This upgrade will also increase the amount of requests you can use in all AI-powered features.', + 'jetpack-ai-client' + ); + + const upgradeMessageRequests = __( + 'Not enough requests left to generate a logo. Upgrade your Jetpack AI to increase the amount of requests you can use in all AI-powered features.', + 'jetpack-ai-client' + ); + + const { context } = useLogoGenerator(); + + const handleUpgradeClick = () => { + recordTracksEvent( EVENT_UPGRADE, { context, placement: EVENT_PLACEMENT_FREE_USER_SCREEN } ); + onCancel(); + }; + + return ( +
+
+ + { reason === 'feature' ? upgradeMessageFeature : upgradeMessageRequests } + +   + +
+
+ + +
+
+ ); +}; diff --git a/projects/js-packages/ai-client/src/logo-generator/components/visit-site-banner.scss b/projects/js-packages/ai-client/src/logo-generator/components/visit-site-banner.scss new file mode 100644 index 0000000000000..51335abe22401 --- /dev/null +++ b/projects/js-packages/ai-client/src/logo-generator/components/visit-site-banner.scss @@ -0,0 +1,29 @@ +@import '@automattic/jetpack-base-styles/root-variables'; + +.jetpack-ai-logo-generator-modal-visit-site-banner { + border-radius: 4px; + background: var(--studio-gray-0, #f6f7f7); + padding: 16px 20px; + display: flex; + gap: 16px; + + @media (max-width: 700px) { + flex-direction: column; + } +} + +.jetpack-ai-logo-generator-modal-visit-site-banner__jetpack-logo { + display: flex; + justify-content: center; + align-items: center; +} + +.jetpack-ai-logo-generator-modal-visit-site-banner__content { + font-size: var(--font-body-small, 14px); + display: flex; + flex-direction: column; + + @media (max-width: 700px) { + gap: 8px; + } +} diff --git a/projects/js-packages/ai-client/src/logo-generator/components/visit-site-banner.tsx b/projects/js-packages/ai-client/src/logo-generator/components/visit-site-banner.tsx new file mode 100644 index 0000000000000..c9cb45868b027 --- /dev/null +++ b/projects/js-packages/ai-client/src/logo-generator/components/visit-site-banner.tsx @@ -0,0 +1,50 @@ +/** + * External dependencies + */ +import { Button, Icon } from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; +import { external } from '@wordpress/icons'; +import clsx from 'clsx'; +/** + * Internal dependencies + */ +import jetpackLogo from '../assets/images/jetpack-logo.svg'; +import './visit-site-banner.scss'; +/** + * Types + */ +import type React from 'react'; + +export const VisitSiteBanner: React.FC< { + className?: string; + siteURL?: string; + onVisitBlankTarget: () => void; +} > = ( { className = null, siteURL = '#', onVisitBlankTarget } ) => { + return ( +
+
+ Jetpack +
+
+ + { __( + 'Do you want to know all the amazing things you can do with Jetpack AI?', + 'jetpack-ai-client' + ) } + + + { __( + 'Generate and tweak content, create forms, get feedback and much more.', + 'jetpack-ai-client' + ) } + +
+ +
+
+
+ ); +}; diff --git a/projects/js-packages/ai-client/src/logo-generator/constants.ts b/projects/js-packages/ai-client/src/logo-generator/constants.ts new file mode 100644 index 0000000000000..073b29f9006d8 --- /dev/null +++ b/projects/js-packages/ai-client/src/logo-generator/constants.ts @@ -0,0 +1,22 @@ +export const JWT_TOKEN_ID = 'jetpack-ai-jwt'; +export const JWT_TOKEN_EXPIRATION_TIME = 2 * 60 * 1000; // 2 minutes + +// Tracks event names +export const EVENT_MODAL_OPEN = 'jetpack_ai_logo_generator_modal_open'; +export const EVENT_MODAL_CLOSE = 'jetpack_ai_logo_generator_modal_close'; +export const EVENT_GENERATE = 'jetpack_ai_logo_generator_generate'; +export const EVENT_SAVE = 'jetpack_ai_logo_generator_save'; +export const EVENT_USE = 'jetpack_ai_logo_generator_use'; +export const EVENT_NAVIGATE = 'jetpack_ai_logo_generator_navigate'; +export const EVENT_FEEDBACK = 'jetpack_ai_logo_generator_feedback'; +export const EVENT_UPGRADE = 'jetpack_ai_upgrade_button'; + +// Event placement constants +export const EVENT_PLACEMENT_QUICK_LINKS = 'quick_links'; +export const EVENT_PLACEMENT_INPUT_FOOTER = 'input_footer'; +export const EVENT_PLACEMENT_FREE_USER_SCREEN = 'free_user_screen'; +export const EVENT_PLACEMENT_UPGRADE_PROMPT = 'upgrade_prompt'; + +// Feature constants +export const MINIMUM_PROMPT_LENGTH = 3; +export const DEFAULT_LOGO_COST = 10; diff --git a/projects/js-packages/ai-client/src/logo-generator/hooks/use-checkout.ts b/projects/js-packages/ai-client/src/logo-generator/hooks/use-checkout.ts new file mode 100644 index 0000000000000..d1a3ccdb72f38 --- /dev/null +++ b/projects/js-packages/ai-client/src/logo-generator/hooks/use-checkout.ts @@ -0,0 +1,37 @@ +/** + * External dependencies + */ +import { useSelect } from '@wordpress/data'; +import debugFactory from 'debug'; +/** + * Internal dependencies + */ +import { STORE_NAME } from '../store/index.js'; +/** + * Types + */ +import type { Selectors } from '../store/types.js'; + +const debug = debugFactory( 'ai-client:logo-generator:use-checkout' ); + +export const useCheckout = () => { + const { nextTier, siteDetails } = useSelect( select => { + const selectors: Selectors = select( STORE_NAME ); + return { + nextTier: selectors.getAiAssistantFeature().nextTier, + siteDetails: selectors.getSiteDetails(), + }; + }, [] ); + + const upgradeURL = new URL( + `${ location.origin }/checkout/${ siteDetails?.domain }/${ nextTier?.slug }` + ); + upgradeURL.searchParams.set( 'redirect_to', location.href ); + + debug( 'Next tier checkout URL: ', upgradeURL.toString() ); + + return { + nextTierCheckoutURL: upgradeURL.toString(), + hasNextTier: !! nextTier, + }; +}; diff --git a/projects/js-packages/ai-client/src/logo-generator/hooks/use-logo-generator.ts b/projects/js-packages/ai-client/src/logo-generator/hooks/use-logo-generator.ts new file mode 100644 index 0000000000000..b6acf39ed34f2 --- /dev/null +++ b/projects/js-packages/ai-client/src/logo-generator/hooks/use-logo-generator.ts @@ -0,0 +1,389 @@ +/** + * External dependencies + */ +import { useDispatch, useSelect } from '@wordpress/data'; +import debugFactory from 'debug'; +import { useCallback } from 'react'; +/** + * Internal dependencies + */ +import useImageGenerator from '../../hooks/use-image-generator/index.js'; +import useSaveToMediaLibrary from '../../hooks/use-save-to-media-library/index.js'; +import requestJwt from '../../jwt/index.js'; +import { stashLogo } from '../lib/logo-storage.js'; +import { setSiteLogo } from '../lib/set-site-logo.js'; +import { STORE_NAME } from '../store/index.js'; +import useRequestErrors from './use-request-errors.js'; +/** + * Types + */ +import type { Logo, Selectors, SaveLogo } from '../store/types.js'; + +const debug = debugFactory( 'jetpack-ai-calypso:use-logo-generator' ); + +const useLogoGenerator = () => { + const { + setSelectedLogoIndex, + setIsSavingLogoToLibrary, + setIsApplyingLogo, + setIsRequestingImage, + setIsEnhancingPrompt, + increaseAiAssistantRequestsCount, + addLogoToHistory, + setContext, + } = useDispatch( STORE_NAME ); + + const { + logos, + selectedLogoIndex, + selectedLogo, + siteDetails, + isSavingLogoToLibrary, + isApplyingLogo, + isEnhancingPrompt, + isBusy, + isRequestingImage, + getAiAssistantFeature, + requireUpgrade, + context, + } = useSelect( select => { + const selectors: Selectors = select( STORE_NAME ); + + return { + logos: selectors.getLogos(), + selectedLogoIndex: selectors.getSelectedLogoIndex(), + selectedLogo: selectors.getSelectedLogo(), + siteDetails: selectors.getSiteDetails(), + isSavingLogoToLibrary: selectors.getIsSavingLogoToLibrary(), + isApplyingLogo: selectors.getIsApplyingLogo(), + isRequestingImage: selectors.getIsRequestingImage(), + isEnhancingPrompt: selectors.getIsEnhancingPrompt(), + isBusy: selectors.getIsBusy(), + getAiAssistantFeature: selectors.getAiAssistantFeature, + requireUpgrade: selectors.getRequireUpgrade(), + context: selectors.getContext(), + }; + }, [] ); + + const { + setFirstLogoPromptFetchError, + setEnhancePromptFetchError, + setLogoFetchError, + setSaveToLibraryError, + setLogoUpdateError, + } = useRequestErrors(); + + const { generateImageWithParameters } = useImageGenerator(); + const { saveToMediaLibrary } = useSaveToMediaLibrary(); + + const { ID = null, name = null, description = null } = siteDetails || {}; + const siteId = ID ? String( ID ) : null; + + const aiAssistantFeatureData = getAiAssistantFeature( siteId ); + const logoGenerationCost = aiAssistantFeatureData?.costs?.[ 'jetpack-ai-logo-generator' ]?.logo; + + const generateFirstPrompt = useCallback( + async function (): Promise< string > { + setFirstLogoPromptFetchError( null ); + increaseAiAssistantRequestsCount(); + + try { + const tokenData = await requestJwt(); + + if ( ! tokenData || ! tokenData.token ) { + throw new Error( 'No token provided' ); + } + + debug( 'Generating first prompt for site' ); + + const firstPromptGenerationPrompt = `Generate a simple and short prompt asking for a logo based on the site's name and description, keeping the same language. +Example for a site named "The minimalist fashion blog", described as "Daily inspiration for all things fashion": A logo for a minimalist fashion site focused on daily sartorial inspiration with a clean and modern aesthetic that is sleek and sophisticated. +Another example, now for a site called "El observatorio de aves", described as "Un sitio dedicado a nuestros compañeros y compañeras entusiastas de la observación de aves.": Un logo para un sitio web dedicado a la observación de aves, capturando la esencia de la naturaleza y la pasión por la avifauna en un diseño elegante y representativo, reflejando una estética natural y apasionada por la vida silvestre. + +Site name: ${ name } +Site description: ${ description }`; + + const body = { + question: firstPromptGenerationPrompt, + feature: 'jetpack-ai-logo-generator', + stream: false, + }; + + const URL = 'https://public-api.wordpress.com/wpcom/v2/jetpack-ai-query'; + const headers = { + Authorization: `Bearer ${ tokenData.token }`, + 'Content-Type': 'application/json', + }; + + const data = await fetch( URL, { + method: 'POST', + headers, + body: JSON.stringify( body ), + } ).then( response => response.json() ); + + return data?.choices?.[ 0 ]?.message?.content; + } catch ( error ) { + increaseAiAssistantRequestsCount( -1 ); + setFirstLogoPromptFetchError( error ); + throw error; + } + }, + [ setFirstLogoPromptFetchError, increaseAiAssistantRequestsCount, name, description ] + ); + + const enhancePrompt = async function ( { prompt }: { prompt: string } ): Promise< string > { + setEnhancePromptFetchError( null ); + increaseAiAssistantRequestsCount(); + + try { + const tokenData = await requestJwt(); + + if ( ! tokenData || ! tokenData.token ) { + throw new Error( 'No token provided' ); + } + + debug( 'Enhancing prompt', prompt ); + + const systemMessage = `Enhance the prompt you receive. +The prompt is meant for generating a logo. Return the same prompt enhanced, and make each enhancement wrapped in brackets. +Do not add any mention to text, letters, typography or the name of the site in the prompt. +For example: user's prompt: A logo for an ice cream shop. Returned prompt: A logo for an ice cream shop [that is pink] [and vibrant].`; + + const messages = [ + { + role: 'system', + content: systemMessage, + }, + { + role: 'user', + content: prompt, + }, + ]; + + const body = { + messages, + feature: 'jetpack-ai-logo-generator', + stream: false, + }; + + const URL = 'https://public-api.wordpress.com/wpcom/v2/jetpack-ai-query'; + const headers = { + Authorization: `Bearer ${ tokenData.token }`, + 'Content-Type': 'application/json', + }; + + const data = await fetch( URL, { + method: 'POST', + headers, + body: JSON.stringify( body ), + } ).then( response => response.json() ); + + return data?.choices?.[ 0 ]?.message?.content; + } catch ( error ) { + increaseAiAssistantRequestsCount( -1 ); + setEnhancePromptFetchError( error ); + throw error; + } + }; + + const generateImage = useCallback( async function ( { + prompt, + }: { + prompt: string; + } ): Promise< { data: Array< { url: string } > } > { + setLogoFetchError( null ); + + try { + const tokenData = await requestJwt(); + + if ( ! tokenData || ! tokenData.token ) { + throw new Error( 'No token provided' ); + } + + debug( 'Generating image with prompt', prompt ); + + const imageGenerationPrompt = `I NEED to test how the tool works with extremely simple prompts. DO NOT add any detail, just use it AS-IS: +Create a single text-free iconic vector logo that symbolically represents the user request, using abstract or symbolic imagery. +The design should be modern, with either a vivid color scheme full of gradients or a color scheme that's monochromatic. Use any of those styles based on the user request mood. +Ensure the logo is set against a clean solid background. +Ensure the logo works in small sizes. +The imagery in the logo should subtly hint at the mood of the user request but DO NOT use any text, letters, or the name of the site on the imagery. +The image should contain a single icon, without variations, color palettes or different versions. + +User request:${ prompt }`; + + const body = { + prompt: imageGenerationPrompt, + feature: 'jetpack-ai-logo-generator', + response_format: 'b64_json', + }; + + const data = await generateImageWithParameters( body ); + + return data as { data: { url: string }[] }; + } catch ( error ) { + setLogoFetchError( error ); + throw error; + } + }, [] ); + + const saveLogo = useCallback< SaveLogo >( + async logo => { + setSaveToLibraryError( null ); + + try { + debug( 'Saving logo for site' ); + + // If the logo is already saved, return its mediaId and mediaURL. + if ( logo.mediaId ) { + return { mediaId: logo.mediaId, mediaURL: logo.url }; + } + + const savedLogo = { + mediaId: 0, + mediaURL: '', + }; + + setIsSavingLogoToLibrary( true ); + + const { id: mediaId, url: mediaURL } = await saveToMediaLibrary( + logo.url, + 'site-logo.png' + ); + + savedLogo.mediaId = parseInt( mediaId ); + savedLogo.mediaURL = mediaURL; + + return savedLogo; + } catch ( error ) { + setSaveToLibraryError( error ); + throw error; + } finally { + setIsSavingLogoToLibrary( false ); + } + }, + [ setIsSavingLogoToLibrary, setSaveToLibraryError ] + ); + + const applyLogo = useCallback( async () => { + setLogoUpdateError( null ); + + try { + if ( ! siteId || ! selectedLogo ) { + throw new Error( 'Missing siteId or logo' ); + } + + debug( 'Applying logo for site', siteId ); + + setIsApplyingLogo( true ); + + const { mediaId } = selectedLogo; + + if ( ! mediaId ) { + throw new Error( 'Missing mediaId' ); + } + + await setSiteLogo( { + siteId: siteId, + imageId: String( mediaId ), + } ); + } catch ( error ) { + setLogoUpdateError( error ); + throw error; + } finally { + setIsApplyingLogo( false ); + } + }, [ selectedLogo, setIsApplyingLogo, setLogoUpdateError, siteId ] ); + + const storeLogo = useCallback( + ( logo: Logo ) => { + addLogoToHistory( logo ); + stashLogo( { ...logo, siteId: String( siteId ) } ); + }, + [ siteId, addLogoToHistory, stashLogo ] + ); + + const generateLogo = useCallback( + async function ( { prompt }: { prompt: string } ): Promise< void > { + debug( 'Generating logo for site' ); + + setIsRequestingImage( true ); + + try { + if ( ! logoGenerationCost ) { + throw new Error( 'Missing cost information' ); + } + + increaseAiAssistantRequestsCount( logoGenerationCost ); + + let image; + + try { + image = await generateImage( { prompt } ); + + if ( ! image || ! image.data.length ) { + throw new Error( 'No image returned' ); + } + } catch ( error ) { + increaseAiAssistantRequestsCount( -logoGenerationCost ); + throw error; + } + + // response_format=url returns object with url, otherwise b64_json + const logo: Logo = { + url: 'data:image/png;base64,' + image.data[ 0 ].b64_json, + description: prompt, + }; + + try { + const savedLogo = await saveLogo( logo ); + storeLogo( { + url: savedLogo.mediaURL, + description: prompt, + mediaId: savedLogo.mediaId, + } ); + } catch ( error ) { + storeLogo( logo ); + throw error; + } + } finally { + setIsRequestingImage( false ); + } + }, + [ logoGenerationCost, increaseAiAssistantRequestsCount, saveLogo, storeLogo, generateImage ] + ); + + return { + logos, + selectedLogoIndex, + selectedLogo, + setSelectedLogoIndex, + site: { + id: siteId, + name, + description, + }, + generateFirstPrompt, + saveLogo, + applyLogo, + generateImage, + enhancePrompt, + storeLogo, + generateLogo, + setIsEnhancingPrompt, + setIsRequestingImage, + setIsSavingLogoToLibrary, + setIsApplyingLogo, + setContext, + isEnhancingPrompt, + isRequestingImage, + isSavingLogoToLibrary, + isApplyingLogo, + isBusy, + getAiAssistantFeature, + requireUpgrade, + context, + }; +}; + +export default useLogoGenerator; diff --git a/projects/js-packages/ai-client/src/logo-generator/hooks/use-request-errors.ts b/projects/js-packages/ai-client/src/logo-generator/hooks/use-request-errors.ts new file mode 100644 index 0000000000000..8fd579b727073 --- /dev/null +++ b/projects/js-packages/ai-client/src/logo-generator/hooks/use-request-errors.ts @@ -0,0 +1,70 @@ +/** + * External dependencies + */ +import { useDispatch, useSelect } from '@wordpress/data'; +/** + * Internal dependencies + */ +import { STORE_NAME } from '../store/index.js'; +/** + * Types + */ +import type { Selectors } from '../store/types.js'; + +const useRequestErrors = () => { + const { + setFeatureFetchError, + setFirstLogoPromptFetchError, + setEnhancePromptFetchError, + setLogoFetchError, + setSaveToLibraryError, + setLogoUpdateError, + } = useDispatch( STORE_NAME ); + + const { + featureFetchError, + firstLogoPromptFetchError, + enhancePromptFetchError, + logoFetchError, + saveToLibraryError, + logoUpdateError, + } = useSelect( select => { + const selectors: Selectors = select( STORE_NAME ); + + return { + featureFetchError: selectors.getFeatureFetchError(), + firstLogoPromptFetchError: selectors.getFirstLogoPromptFetchError(), + enhancePromptFetchError: selectors.getEnhancePromptFetchError(), + logoFetchError: selectors.getLogoFetchError(), + saveToLibraryError: selectors.getSaveToLibraryError(), + logoUpdateError: selectors.getLogoUpdateError(), + }; + }, [] ); + + const clearErrors = () => { + setFeatureFetchError( null ); + setFirstLogoPromptFetchError( null ); + setEnhancePromptFetchError( null ); + setLogoFetchError( null ); + setSaveToLibraryError( null ); + setLogoUpdateError( null ); + }; + + return { + setFeatureFetchError, + setFirstLogoPromptFetchError, + setEnhancePromptFetchError, + setLogoFetchError, + setSaveToLibraryError, + setLogoUpdateError, + clearErrors, + featureFetchError, + firstLogoPromptFetchError, + enhancePromptFetchError, + logoFetchError, + saveToLibraryError, + logoUpdateError, + }; +}; + +export default useRequestErrors; diff --git a/projects/js-packages/ai-client/src/logo-generator/index.ts b/projects/js-packages/ai-client/src/logo-generator/index.ts new file mode 100644 index 0000000000000..e6a9b9dec67ce --- /dev/null +++ b/projects/js-packages/ai-client/src/logo-generator/index.ts @@ -0,0 +1 @@ +export * from './components/generator-modal.js'; diff --git a/projects/js-packages/ai-client/src/logo-generator/lib/logo-storage.ts b/projects/js-packages/ai-client/src/logo-generator/lib/logo-storage.ts new file mode 100644 index 0000000000000..a80c47f4b0507 --- /dev/null +++ b/projects/js-packages/ai-client/src/logo-generator/lib/logo-storage.ts @@ -0,0 +1,166 @@ +/** + * Types + */ +import { Logo } from '../store/types.js'; +import { RemoveFromStorageProps, SaveToStorageProps, UpdateInStorageProps } from '../types.js'; +import { mediaExists } from './media-exists.js'; + +const MAX_LOGOS = 10; + +/** + * Add an entry to the site's logo history. + * + * @param {SaveToStorageProps} saveToStorageProps - The properties to save to storage + * @param {SaveToStorageProps.siteId} saveToStorageProps.siteId - The site ID + * @param {SaveToStorageProps.url} saveToStorageProps.url - The URL of the logo + * @param {SaveToStorageProps.description} saveToStorageProps.description - The description of the logo, based on the prompt used to generate it + * @param {SaveToStorageProps.mediaId} saveToStorageProps.mediaId - The media ID of the logo on the backend + * + * @returns {Logo} The logo that was saved + */ +export function stashLogo( { siteId, url, description, mediaId }: SaveToStorageProps ) { + const storedContent = getSiteLogoHistory( siteId ); + + const logo: Logo = { + url, + description, + mediaId, + }; + + storedContent.push( logo ); + + localStorage.setItem( + `logo-history-${ siteId }`, + JSON.stringify( storedContent.slice( -MAX_LOGOS ) ) + ); + + return logo; +} + +/** + * Update an entry in the site's logo history. + * + * @param {UpdateInStorageProps} updateInStorageProps - The properties to update in storage + * @param {UpdateInStorageProps.siteId} updateInStorageProps.siteId - The site ID + * @param {UpdateInStorageProps.url} updateInStorageProps.url - The URL of the logo to update + * @param {UpdateInStorageProps.newUrl} updateInStorageProps.newUrl - The new URL of the logo + * @param {UpdateInStorageProps.mediaId} updateInStorageProps.mediaId - The new media ID of the logo + * @returns {Logo} The logo that was updated + */ +export function updateLogo( { siteId, url, newUrl, mediaId }: UpdateInStorageProps ) { + const storedContent = getSiteLogoHistory( siteId ); + + const index = storedContent.findIndex( logo => logo.url === url ); + + if ( index > -1 ) { + storedContent[ index ].url = newUrl; + storedContent[ index ].mediaId = mediaId; + } + + localStorage.setItem( + `logo-history-${ siteId }`, + JSON.stringify( storedContent.slice( -MAX_LOGOS ) ) + ); + + return storedContent[ index ]; +} + +/** + * Get the logo history for a site. + * + * @param {string} siteId - The site ID to get the logo history for + * @returns {Logo[]} The logo history for the site + */ +export function getSiteLogoHistory( siteId: string ) { + const storedString = localStorage.getItem( `logo-history-${ siteId }` ); + let storedContent: Logo[] = storedString ? JSON.parse( storedString ) : []; + + // Ensure that the stored content is an array + if ( ! Array.isArray( storedContent ) ) { + storedContent = []; + } + + // Ensure a maximum of 10 logos are stored + storedContent = storedContent.slice( -MAX_LOGOS ); + + // Ensure that the stored content is an array of Logo objects + storedContent = storedContent + .filter( logo => { + return ( + typeof logo === 'object' && + typeof logo.url === 'string' && + typeof logo.description === 'string' + ); + } ) + .map( logo => ( { + url: logo.url, + description: logo.description, + mediaId: logo.mediaId, + } ) ); + + return storedContent; +} + +/** + * Check if the logo history for a site is empty. + * + * @param {string }siteId - The site ID to check the logo history for + * @returns {boolean} Whether the logo history for the site is empty + */ +export function isLogoHistoryEmpty( siteId: string ) { + const storedContent = getSiteLogoHistory( siteId ); + + return storedContent.length === 0; +} + +/** + * Remove an entry from the site's logo history. + * + * @param {RemoveFromStorageProps} removeFromStorageProps - The properties to remove from storage + * @param {RemoveFromStorageProps.siteId} removeFromStorageProps.siteId - The site ID + * @param {RemoveFromStorageProps.mediaId} removeFromStorageProps.mediaId - The media ID of the logo to remove + * @returns {void} + */ +export function removeLogo( { siteId, mediaId }: RemoveFromStorageProps ) { + const storedContent = getSiteLogoHistory( siteId ); + const index = storedContent.findIndex( logo => logo.mediaId === mediaId ); + + if ( index === -1 ) { + return; + } + + storedContent.splice( index, 1 ); + localStorage.setItem( `logo-history-${ siteId }`, JSON.stringify( storedContent ) ); +} + +/** + * Clear deleted media from the site's logo history, checking if the media still exists on the backend. + * + * @param {string} siteId - The site ID to clear deleted media for + * @returns {Promise} + */ +export async function clearDeletedMedia( siteId: string ) { + const storedContent = getSiteLogoHistory( siteId ); + + const checks = storedContent + .filter( ( { mediaId } ) => mediaId !== undefined ) + .map( + ( { mediaId } ) => + new Promise( ( resolve, reject ) => { + mediaExists( { siteId, mediaId } ) + .then( exists => resolve( { mediaId, exists } ) ) + .catch( error => reject( error ) ); + } ) + ); + + try { + const responses = ( await Promise.all( checks ) ) as { + mediaId: Logo[ 'mediaId' ]; + exists: boolean; + }[]; + + responses + .filter( ( { exists } ) => ! exists ) + .forEach( ( { mediaId } ) => removeLogo( { siteId, mediaId } ) ); + } catch ( error ) {} // Assume that the media exists if there was a network error and do nothing to avoid data loss. +} diff --git a/projects/js-packages/ai-client/src/logo-generator/lib/media-exists.ts b/projects/js-packages/ai-client/src/logo-generator/lib/media-exists.ts new file mode 100644 index 0000000000000..9df5950e1f6a2 --- /dev/null +++ b/projects/js-packages/ai-client/src/logo-generator/lib/media-exists.ts @@ -0,0 +1,42 @@ +/** + * External dependencies + */ +import apiFetch from '../../api-fetch/index.js'; +/** + * Types + */ +import type { CheckMediaProps } from '../types.js'; + +/** + * Uses the media information to confirm it exists or not on the server. + * + * @param {CheckMediaProps} checkMediaProps - the media details to check + * @param {CheckMediaProps.mediaId} checkMediaProps.mediaId - the id of the media to check + * @returns {Promise} - true if the media exists, false otherwise + */ +export async function mediaExists( { mediaId }: CheckMediaProps ): Promise< boolean > { + const id = Number( mediaId ); + + if ( Number.isNaN( id ) ) { + return false; + } + + try { + // Using apiFetch directly here because we don't want to limit the number of concurrent media checks + // We store at most 10 logos in the local storage, so the number of concurrent requests should be limited + await apiFetch( { + path: `/wp/v2/media/${ Number( mediaId ) }`, + method: 'GET', + } ); + + return true; + } catch ( error ) { + const status = ( error as { data?: { status?: number } } )?.data?.status; + + if ( status === 404 ) { + return false; + } + + throw error; + } +} diff --git a/projects/js-packages/ai-client/src/logo-generator/lib/set-site-logo.ts b/projects/js-packages/ai-client/src/logo-generator/lib/set-site-logo.ts new file mode 100644 index 0000000000000..a677c74137c70 --- /dev/null +++ b/projects/js-packages/ai-client/src/logo-generator/lib/set-site-logo.ts @@ -0,0 +1,32 @@ +/** + * Internal dependencies + */ +import wpcomLimitedRequest from './wpcom-limited-request.js'; +/** + * Types + */ +import type { SetSiteLogoProps, SetSiteLogoResponseProps } from '../types.js'; + +/** + * Set the site logo using a backend request. + * + * @param {SetSiteLogoProps} setSiteLogoProps - The properties to set the site logo + * @param {SetSiteLogoProps.siteId} setSiteLogoProps.siteId - The site ID + * @param {SetSiteLogoProps.imageId} setSiteLogoProps.imageId - The image ID to set as the site logo + * @returns {Promise} The response from the request + */ +export async function setSiteLogo( { siteId, imageId }: SetSiteLogoProps ) { + const body = { + site_logo: imageId, + site_icon: imageId, + }; + + return wpcomLimitedRequest< SetSiteLogoResponseProps >( { + path: `/sites/${ String( siteId ) }/settings`, + apiVersion: 'v2', + apiNamespace: 'wp/v2', + body, + query: 'source=jetpack-ai', + method: 'POST', + } ); +} diff --git a/projects/js-packages/ai-client/src/logo-generator/lib/wpcom-limited-request.ts b/projects/js-packages/ai-client/src/logo-generator/lib/wpcom-limited-request.ts new file mode 100644 index 0000000000000..d38cdbd383367 --- /dev/null +++ b/projects/js-packages/ai-client/src/logo-generator/lib/wpcom-limited-request.ts @@ -0,0 +1,41 @@ +/** + * External dependencies + */ +import apiFetch from '../../api-fetch/index.js'; +/** + * Types + */ + +const MAX_CONCURRENT_REQUESTS = 5; + +let concurrentCounter = 0; +let lastCallTimestamp: number | null = null; + +/** + * Concurrency-limited request to wpcom-proxy-request. + * @param { object } params - The request params, as expected by apiFetch. + * @returns { Promise } The response. + * @throws { Error } If there are too many concurrent requests. + */ +export default async function wpcomLimitedRequest< T >( params: object ): Promise< T > { + concurrentCounter += 1; + + if ( concurrentCounter > MAX_CONCURRENT_REQUESTS ) { + concurrentCounter -= 1; + throw new Error( 'Too many requests' ); + } + + const now = Date.now(); + + // Check if the last call was made less than 100 milliseconds ago + if ( lastCallTimestamp && now - lastCallTimestamp < 100 ) { + concurrentCounter -= 1; + throw new Error( 'Too many requests' ); + } + + lastCallTimestamp = now; // Update the timestamp + + return apiFetch< T >( params ).finally( () => { + concurrentCounter -= 1; + } ); +} diff --git a/projects/js-packages/ai-client/src/logo-generator/store/actions.ts b/projects/js-packages/ai-client/src/logo-generator/store/actions.ts new file mode 100644 index 0000000000000..a166edf0b3939 --- /dev/null +++ b/projects/js-packages/ai-client/src/logo-generator/store/actions.ts @@ -0,0 +1,251 @@ +/** + * Internal dependencies + */ +import { getSiteLogoHistory } from '../lib/logo-storage.js'; +import wpcomLimitedRequest from '../lib/wpcom-limited-request.js'; +/** + * Types & Constants + */ +import { + ACTION_INCREASE_AI_ASSISTANT_REQUESTS_COUNT, + ACTION_REQUEST_AI_ASSISTANT_FEATURE, + ACTION_SET_AI_ASSISTANT_FEATURE_REQUIRE_UPGRADE, + ACTION_SET_SITE_DETAILS, + ACTION_STORE_AI_ASSISTANT_FEATURE, + ACTION_SET_TIER_PLANS_ENABLED, + ACTION_SET_SELECTED_LOGO_INDEX, + ACTION_ADD_LOGO_TO_HISTORY, + ACTION_SET_IS_SAVING_LOGO_TO_LIBRARY, + ACTION_SAVE_SELECTED_LOGO, + ACTION_SET_IS_REQUESTING_IMAGE, + ACTION_SET_IS_APPLYING_LOGO, + ACTION_SET_IS_ENHANCING_PROMPT, + ACTION_SET_SITE_HISTORY, + ACTION_SET_FEATURE_FETCH_ERROR, + ACTION_SET_FIRST_LOGO_PROMPT_FETCH_ERROR, + ACTION_SET_ENHANCE_PROMPT_FETCH_ERROR, + ACTION_SET_LOGO_FETCH_ERROR, + ACTION_SET_LOGO_UPDATE_ERROR, + ACTION_SET_SAVE_TO_LIBRARY_ERROR, + ACTION_SET_CONTEXT, +} from './constants.js'; +import type { + AiFeatureProps, + AiAssistantFeatureEndpointResponseProps, + Logo, + RequestError, +} from './types.js'; +import type { SiteDetails } from '../types.js'; + +/** + * Map the response from the `sites/$site/ai-assistant-feature` + * endpoint to the AI Assistant feature props. + * @param { AiAssistantFeatureEndpointResponseProps } response - The response from the endpoint. + * @returns { AiFeatureProps } The AI Assistant feature props. + */ +export function mapAiFeatureResponseToAiFeatureProps( + response: AiAssistantFeatureEndpointResponseProps +): AiFeatureProps { + return { + hasFeature: !! response[ 'has-feature' ], + isOverLimit: !! response[ 'is-over-limit' ], + requestsCount: response[ 'requests-count' ], + requestsLimit: response[ 'requests-limit' ], + requireUpgrade: !! response[ 'site-require-upgrade' ], + errorMessage: response[ 'error-message' ], + errorCode: response[ 'error-code' ], + upgradeType: response[ 'upgrade-type' ], + usagePeriod: { + currentStart: response[ 'usage-period' ]?.[ 'current-start' ], + nextStart: response[ 'usage-period' ]?.[ 'next-start' ], + requestsCount: response[ 'usage-period' ]?.[ 'requests-count' ] || 0, + }, + currentTier: response[ 'current-tier' ], + nextTier: response[ 'next-tier' ], + tierPlansEnabled: !! response[ 'tier-plans-enabled' ], + costs: response.costs, + }; +} + +const actions = { + storeAiAssistantFeature( feature: AiFeatureProps ) { + return { + type: ACTION_STORE_AI_ASSISTANT_FEATURE, + feature, + }; + }, + + /** + * Thunk action to fetch the AI Assistant feature from the API. + * @returns {Function} The thunk action. + */ + fetchAiAssistantFeature() { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return async ( { dispatch }: { dispatch: any } ) => { + // Dispatch isFetching action. + dispatch( { type: ACTION_REQUEST_AI_ASSISTANT_FEATURE } ); + + try { + const response: AiAssistantFeatureEndpointResponseProps = await wpcomLimitedRequest( { + path: '/wpcom/v2/jetpack-ai/ai-assistant-feature', + query: 'force=wpcom', + } ); + + // Store the feature in the store. + dispatch( + actions.storeAiAssistantFeature( mapAiFeatureResponseToAiFeatureProps( response ) ) + ); + } catch ( err ) { + // Mark the fetch as failed. + dispatch( { type: ACTION_SET_FEATURE_FETCH_ERROR, error: err } ); + } + }; + }, + + /** + * This thunk action is used to increase + * the requests count for the current usage period. + * @param {number} count - The number of requests to increase. Default is 1. + * @returns {Function} The thunk action. + */ + increaseAiAssistantRequestsCount( count: number = 1 ) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return ( { dispatch }: { dispatch: any } ) => { + dispatch( { + type: ACTION_INCREASE_AI_ASSISTANT_REQUESTS_COUNT, + count, + } ); + }; + }, + + setAiAssistantFeatureRequireUpgrade( requireUpgrade: boolean = true ) { + return { + type: ACTION_SET_AI_ASSISTANT_FEATURE_REQUIRE_UPGRADE, + requireUpgrade, + }; + }, + + setTierPlansEnabled( tierPlansEnabled: boolean = true ) { + return { + type: ACTION_SET_TIER_PLANS_ENABLED, + tierPlansEnabled, + }; + }, + + setSiteDetails( siteDetails: SiteDetails ) { + return { + type: ACTION_SET_SITE_DETAILS, + siteDetails, + }; + }, + + setSelectedLogoIndex( selectedLogoIndex: number ) { + return { + type: ACTION_SET_SELECTED_LOGO_INDEX, + selectedLogoIndex, + }; + }, + + addLogoToHistory( logo: Logo ) { + return { + type: ACTION_ADD_LOGO_TO_HISTORY, + logo, + }; + }, + + setIsSavingLogoToLibrary( isSavingLogoToLibrary: boolean ) { + return { + type: ACTION_SET_IS_SAVING_LOGO_TO_LIBRARY, + isSavingLogoToLibrary, + }; + }, + + setIsApplyingLogo( isApplyingLogo: boolean ) { + return { + type: ACTION_SET_IS_APPLYING_LOGO, + isApplyingLogo, + }; + }, + + updateSelectedLogo( mediaId: string, url: string ) { + return { + type: ACTION_SAVE_SELECTED_LOGO, + mediaId, + url, + }; + }, + + setIsRequestingImage( isRequestingImage: boolean ) { + return { + type: ACTION_SET_IS_REQUESTING_IMAGE, + isRequestingImage, + }; + }, + + setIsEnhancingPrompt( isEnhancingPrompt: boolean ) { + return { + type: ACTION_SET_IS_ENHANCING_PROMPT, + isEnhancingPrompt, + }; + }, + + loadLogoHistory( siteId: string ) { + const history = getSiteLogoHistory( siteId ); + + return { + type: ACTION_SET_SITE_HISTORY, + history, + }; + }, + + setFeatureFetchError( error: RequestError ) { + return { + type: ACTION_SET_FEATURE_FETCH_ERROR, + error, + }; + }, + + setFirstLogoPromptFetchError( error: RequestError ) { + return { + type: ACTION_SET_FIRST_LOGO_PROMPT_FETCH_ERROR, + error, + }; + }, + + setEnhancePromptFetchError( error: RequestError ) { + return { + type: ACTION_SET_ENHANCE_PROMPT_FETCH_ERROR, + error, + }; + }, + + setLogoFetchError( error: RequestError ) { + return { + type: ACTION_SET_LOGO_FETCH_ERROR, + error, + }; + }, + + setSaveToLibraryError( error: RequestError ) { + return { + type: ACTION_SET_SAVE_TO_LIBRARY_ERROR, + error, + }; + }, + + setLogoUpdateError( error: RequestError ) { + return { + type: ACTION_SET_LOGO_UPDATE_ERROR, + error, + }; + }, + + setContext( context: string ) { + return { + type: ACTION_SET_CONTEXT, + context, + }; + }, +}; + +export default actions; diff --git a/projects/js-packages/ai-client/src/logo-generator/store/constants.ts b/projects/js-packages/ai-client/src/logo-generator/store/constants.ts new file mode 100644 index 0000000000000..c9cc2d62eaba3 --- /dev/null +++ b/projects/js-packages/ai-client/src/logo-generator/store/constants.ts @@ -0,0 +1,49 @@ +/** + * AI Assistant feature actions + */ +export const ACTION_STORE_AI_ASSISTANT_FEATURE = 'STORE_AI_ASSISTANT_FEATURE'; +export const ACTION_REQUEST_AI_ASSISTANT_FEATURE = 'REQUEST_AI_ASSISTANT_FEATURE'; +export const ACTION_INCREASE_AI_ASSISTANT_REQUESTS_COUNT = 'INCREASE_AI_ASSISTANT_REQUESTS_COUNT'; +export const ACTION_SET_AI_ASSISTANT_FEATURE_REQUIRE_UPGRADE = + 'SET_AI_ASSISTANT_FEATURE_REQUIRE_UPGRADE'; +export const ACTION_SET_TIER_PLANS_ENABLED = 'SET_TIER_PLANS_ENABLED'; +export const ACTION_SET_SITE_DETAILS = 'SET_SITE_DETAILS'; + +/** + * Endpoints + */ +export const ENDPOINT_AI_ASSISTANT_FEATURE = '/wpcom/v2/jetpack-ai/ai-assistant-feature'; + +/** + * New AI Assistant feature async request + */ +export const FREE_PLAN_REQUESTS_LIMIT = 20; +export const UNLIMITED_PLAN_REQUESTS_LIMIT = 999999999; +export const ASYNC_REQUEST_COUNTDOWN_INIT_VALUE = 3; +export const NEW_ASYNC_REQUEST_TIMER_INTERVAL = 5000; +export const ACTION_DECREASE_NEW_ASYNC_REQUEST_COUNTDOWN = 'DECREASE_NEW_ASYNC_REQUEST_COUNTDOWN'; +export const ACTION_ENQUEUE_ASYNC_REQUEST = 'ENQUEUE_ASYNC_COUNTDOWN_REQUEST'; +export const ACTION_DEQUEUE_ASYNC_REQUEST = 'DEQUEUE_ASYNC_COUNTDOWN_REQUEST'; + +/** + * Logo generator actions + */ +export const ACTION_SET_CONTEXT = 'SET_CONTEXT'; +export const ACTION_SET_SELECTED_LOGO_INDEX = 'SET_SELECTED_LOGO_INDEX'; +export const ACTION_ADD_LOGO_TO_HISTORY = 'ADD_LOGO_TO_HISTORY'; +export const ACTION_SET_IS_SAVING_LOGO_TO_LIBRARY = 'SET_IS_SAVING_LOGO_TO_LIBRARY'; +export const ACTION_SET_IS_APPLYING_LOGO = 'SET_IS_APPLYING_LOGO'; +export const ACTION_SAVE_SELECTED_LOGO = 'SAVE_SELECTED_LOGO'; +export const ACTION_SET_IS_REQUESTING_IMAGE = 'SET_IS_REQUESTING_IMAGE'; +export const ACTION_SET_IS_ENHANCING_PROMPT = 'SET_IS_ENHANCING_PROMPT'; +export const ACTION_SET_SITE_HISTORY = 'SET_SITE_HISTORY'; + +/** + * Logo generator error actions + */ +export const ACTION_SET_FEATURE_FETCH_ERROR = 'SET_FEATURE_FETCH_ERROR'; +export const ACTION_SET_FIRST_LOGO_PROMPT_FETCH_ERROR = 'SET_FIRST_LOGO_PROMPT_FETCH_ERROR'; +export const ACTION_SET_ENHANCE_PROMPT_FETCH_ERROR = 'SET_ENHANCE_PROMPT_FETCH_ERROR'; +export const ACTION_SET_LOGO_FETCH_ERROR = 'SET_LOGO_FETCH_ERROR'; +export const ACTION_SET_SAVE_TO_LIBRARY_ERROR = 'SET_SAVE_TO_LIBRARY_ERROR'; +export const ACTION_SET_LOGO_UPDATE_ERROR = 'SET_LOGO_UPDATE_ERROR'; diff --git a/projects/js-packages/ai-client/src/logo-generator/store/index.ts b/projects/js-packages/ai-client/src/logo-generator/store/index.ts new file mode 100644 index 0000000000000..12b4e6c8c7ef8 --- /dev/null +++ b/projects/js-packages/ai-client/src/logo-generator/store/index.ts @@ -0,0 +1,25 @@ +/** + * External dependencies + */ +import { createReduxStore, register } from '@wordpress/data'; +/** + * Internal dependencies + */ +import actions from './actions.js'; +import reducer from './reducer.js'; +import selectors from './selectors.js'; + +export const STORE_NAME = 'jetpack-ai/logo-generator'; + +const jetpackAiLogoGeneratorStore = createReduxStore( STORE_NAME, { + // @ts-expect-error -- TSCONVERSION + __experimentalUseThunks: true, + + actions, + + reducer, + + selectors, +} ); + +register( jetpackAiLogoGeneratorStore ); diff --git a/projects/js-packages/ai-client/src/logo-generator/store/initial-state.ts b/projects/js-packages/ai-client/src/logo-generator/store/initial-state.ts new file mode 100644 index 0000000000000..7b130dc12e31e --- /dev/null +++ b/projects/js-packages/ai-client/src/logo-generator/store/initial-state.ts @@ -0,0 +1,43 @@ +/** + * Types & Constants + */ +import { ASYNC_REQUEST_COUNTDOWN_INIT_VALUE, FREE_PLAN_REQUESTS_LIMIT } from './constants.js'; +import { LogoGeneratorStateProp } from './types.js'; + +const INITIAL_STATE: LogoGeneratorStateProp = { + siteDetails: {}, + features: { + aiAssistantFeature: { + hasFeature: true, + isOverLimit: false, + requestsCount: 0, + requestsLimit: FREE_PLAN_REQUESTS_LIMIT, + requireUpgrade: false, + errorMessage: '', + errorCode: '', + upgradeType: 'default', + currentTier: { + slug: 'ai-assistant-tier-free', + value: 0, + limit: 20, + }, + usagePeriod: { + currentStart: '', + nextStart: '', + requestsCount: 0, + }, + nextTier: null, + tierPlansEnabled: false, + _meta: { + isRequesting: false, + asyncRequestCountdown: ASYNC_REQUEST_COUNTDOWN_INIT_VALUE, + asyncRequestTimerId: 0, + isRequestingImage: false, + }, + }, + }, + history: [], + selectedLogoIndex: 0, +}; + +export default INITIAL_STATE; diff --git a/projects/js-packages/ai-client/src/logo-generator/store/reducer.ts b/projects/js-packages/ai-client/src/logo-generator/store/reducer.ts new file mode 100644 index 0000000000000..50db694226385 --- /dev/null +++ b/projects/js-packages/ai-client/src/logo-generator/store/reducer.ts @@ -0,0 +1,387 @@ +/** + * Types & Constants + */ +import { DEFAULT_LOGO_COST } from '../constants.js'; +import { + ACTION_INCREASE_AI_ASSISTANT_REQUESTS_COUNT, + ACTION_REQUEST_AI_ASSISTANT_FEATURE, + ACTION_SET_AI_ASSISTANT_FEATURE_REQUIRE_UPGRADE, + ACTION_STORE_AI_ASSISTANT_FEATURE, + ASYNC_REQUEST_COUNTDOWN_INIT_VALUE, + FREE_PLAN_REQUESTS_LIMIT, + UNLIMITED_PLAN_REQUESTS_LIMIT, + ACTION_SET_TIER_PLANS_ENABLED, + ACTION_SET_SITE_DETAILS, + ACTION_SET_SELECTED_LOGO_INDEX, + ACTION_ADD_LOGO_TO_HISTORY, + ACTION_SAVE_SELECTED_LOGO, + ACTION_SET_IS_SAVING_LOGO_TO_LIBRARY, + ACTION_SET_IS_REQUESTING_IMAGE, + ACTION_SET_IS_APPLYING_LOGO, + ACTION_SET_IS_ENHANCING_PROMPT, + ACTION_SET_SITE_HISTORY, + ACTION_SET_FEATURE_FETCH_ERROR, + ACTION_SET_FIRST_LOGO_PROMPT_FETCH_ERROR, + ACTION_SET_ENHANCE_PROMPT_FETCH_ERROR, + ACTION_SET_LOGO_FETCH_ERROR, + ACTION_SET_SAVE_TO_LIBRARY_ERROR, + ACTION_SET_LOGO_UPDATE_ERROR, + ACTION_SET_CONTEXT, +} from './constants.js'; +import INITIAL_STATE from './initial-state.js'; +import type { + AiFeatureStateProps, + LogoGeneratorStateProp, + RequestError, + TierLimitProp, +} from './types.js'; +import type { SiteDetails } from '../types.js'; + +/** + * Reducer for the Logo Generator store. + * + * @param {LogoGeneratorStateProp} state - The current state + * @param {object} action - The action to apply, as described by the properties below + * @param {string} action.type - The action type + * @param {AiFeatureStateProps} action.feature - The AI Assistant feature state + * @param {number} action.count - The number of requests to increase the counter by + * @param {boolean} action.requireUpgrade - Whether an upgrade is required + * @param {boolean} action.tierPlansEnabled - Whether tier plans are enabled + * @param {SiteDetails} action.siteDetails - The site details + * @param {number} action.selectedLogoIndex - The selected logo index + * @param {boolean} action.isSavingLogoToLibrary - Whether a logo is being saved to the library + * @param {boolean} action.isApplyingLogo - Whether a logo is being applied + * @param {object} action.logo - The logo to save, as described by the properties below + * @param {string} action.logo.url - The logo URL + * @param {string} action.logo.description - The logo description + * @param {number} action.mediaId - The media ID from backend + * @param {string} action.url - The URL to save + * @param {boolean} action.isRequestingImage - Whether an image is being requested + * @param {boolean} action.isEnhancingPrompt - Whether a prompt enhancement is being requested + * @param {Array< { url: string; description: string; mediaId?: number } >} action.history - The logo history + * @param {RequestError} action.error - The error to set + * @param {string} action.context - The context where the tool is being used + * @returns {LogoGeneratorStateProp} The new state + */ +export default function reducer( + state = INITIAL_STATE, + action: { + type: string; + feature?: AiFeatureStateProps; + count?: number; + requireUpgrade?: boolean; + tierPlansEnabled?: boolean; + siteDetails?: SiteDetails; + selectedLogoIndex?: number; + isSavingLogoToLibrary?: boolean; + isApplyingLogo?: boolean; + logo?: { url: string; description: string }; + mediaId?: number; + url?: string; + isRequestingImage?: boolean; + isEnhancingPrompt?: boolean; + history?: Array< { url: string; description: string; mediaId?: number } >; + error?: RequestError; + context?: string; + } +) { + switch ( action.type ) { + case ACTION_REQUEST_AI_ASSISTANT_FEATURE: + return { + ...state, + _meta: { + ...( state._meta ?? {} ), + // Reset the error state when requesting the feature. + featureFetchError: null, + }, + features: { + ...state.features, + aiAssistantFeature: { + ...state.features.aiAssistantFeature, + _meta: { + ...state?.features?.aiAssistantFeature?._meta, + isRequesting: true, + asyncRequestCountdown: ASYNC_REQUEST_COUNTDOWN_INIT_VALUE, // restore the countdown + asyncRequestTimerId: 0, // reset the timer id + }, + }, + }, + }; + + case ACTION_STORE_AI_ASSISTANT_FEATURE: { + const defaultCosts = { + 'jetpack-ai-logo-generator': { + logo: DEFAULT_LOGO_COST, + }, + }; + + return { + ...state, + features: { + ...state.features, + aiAssistantFeature: { + costs: defaultCosts, + ...action.feature, + // re evaluate requireUpgrade as the logo generator does not allow free usage + requireUpgrade: + action.feature?.requireUpgrade || action.feature?.currentTier?.value === 0, + _meta: { + ...state?.features?.aiAssistantFeature?._meta, + isRequesting: false, + }, + }, + }, + }; + } + + case ACTION_INCREASE_AI_ASSISTANT_REQUESTS_COUNT: { + // Usage Period data + const usagePeriod = state?.features?.aiAssistantFeature?.usagePeriod || { requestsCount: 0 }; + + // Increase requests counters + const requestsCount = + ( state?.features?.aiAssistantFeature?.requestsCount || 0 ) + ( action.count ?? 1 ); + usagePeriod.requestsCount += action.count ?? 1; + + // Current tier value + const currentTierValue = state?.features?.aiAssistantFeature?.currentTier?.value; + + const isFreeTierPlan = + ( typeof currentTierValue === 'undefined' && + ! state?.features?.aiAssistantFeature?.hasFeature ) || + currentTierValue === 0; + + const isUnlimitedTierPlan = + ( typeof currentTierValue === 'undefined' && + state?.features?.aiAssistantFeature?.hasFeature ) || + currentTierValue === 1; + + // Request limit defined with the current tier limit by default. + let requestsLimit: TierLimitProp = + state?.features?.aiAssistantFeature?.currentTier?.limit || FREE_PLAN_REQUESTS_LIMIT; + + if ( isUnlimitedTierPlan ) { + requestsLimit = UNLIMITED_PLAN_REQUESTS_LIMIT; + } else if ( isFreeTierPlan ) { + requestsLimit = state?.features?.aiAssistantFeature?.requestsLimit as TierLimitProp; + } + + const currentCount = + isUnlimitedTierPlan || isFreeTierPlan // @todo: update once tier data is available + ? requestsCount + : state?.features?.aiAssistantFeature?.usagePeriod?.requestsCount || 0; + + /** + * Compute the AI Assistant Feature data optimistically, + * based on the Jetpack_AI_Helper::get_ai_assistance_feature() helper. + * @see _inc/lib/class-jetpack-ai-helper.php + */ + const isOverLimit = currentCount >= requestsLimit; + + // highest tier holds a soft limit so requireUpgrade is false on that case (nextTier null means highest tier) + const requireUpgrade = + isFreeTierPlan || ( isOverLimit && state?.features?.aiAssistantFeature?.nextTier !== null ); + + return { + ...state, + features: { + ...state.features, + aiAssistantFeature: { + ...state.features.aiAssistantFeature, + isOverLimit, + requestsCount, + requireUpgrade, + usagePeriod: { ...usagePeriod }, + }, + }, + }; + } + + case ACTION_SET_AI_ASSISTANT_FEATURE_REQUIRE_UPGRADE: { + /* + * If we require an upgrade, we are also over the limit; + * The opposite is not true, we can be over the limit without + * requiring an upgrade, for example when we are on the highest tier. + * In this case, we don't want to set isOverLimit to false. + */ + return { + ...state, + features: { + ...state.features, + aiAssistantFeature: { + ...state.features.aiAssistantFeature, + requireUpgrade: action.requireUpgrade, + ...( action.requireUpgrade ? { isOverLimit: true } : {} ), + }, + }, + }; + } + + case ACTION_SET_TIER_PLANS_ENABLED: { + return { + ...state, + features: { + ...state.features, + aiAssistantFeature: { + ...state.features.aiAssistantFeature, + tierPlansEnabled: action.tierPlansEnabled, + }, + }, + }; + } + + case ACTION_SET_SITE_DETAILS: { + return { + ...state, + siteDetails: action.siteDetails, + }; + } + + case ACTION_SET_SELECTED_LOGO_INDEX: { + return { + ...state, + selectedLogoIndex: action.selectedLogoIndex, + }; + } + + case ACTION_ADD_LOGO_TO_HISTORY: { + const history = [ ...state.history, action.logo ]; + + return { + ...state, + history, + selectedLogoIndex: history.length - 1, + }; + } + + case ACTION_SET_IS_SAVING_LOGO_TO_LIBRARY: { + return { + ...state, + _meta: { + ...( state._meta ?? {} ), + isSavingLogoToLibrary: action.isSavingLogoToLibrary, + }, + }; + } + + case ACTION_SET_IS_APPLYING_LOGO: { + return { + ...state, + _meta: { + ...( state._meta ?? {} ), + isApplyingLogo: action.isApplyingLogo, + }, + }; + } + + case ACTION_SAVE_SELECTED_LOGO: { + const selectedLogo = state.history?.[ state.selectedLogoIndex ]; + + return { + ...state, + history: [ + ...state.history.slice( 0, state.selectedLogoIndex ), + { + ...selectedLogo, + mediaId: action.mediaId, + url: action.url, + }, + ...state.history.slice( state.selectedLogoIndex + 1 ), + ], + }; + } + + case ACTION_SET_IS_REQUESTING_IMAGE: { + return { + ...state, + _meta: { + ...( state._meta ?? {} ), + isRequestingImage: action.isRequestingImage, + }, + }; + } + + case ACTION_SET_IS_ENHANCING_PROMPT: { + return { + ...state, + _meta: { + ...( state._meta ?? {} ), + isEnhancingPrompt: action.isEnhancingPrompt, + }, + }; + } + + case ACTION_SET_SITE_HISTORY: { + return { + ...state, + history: action.history, + selectedLogoIndex: action.history?.length ? action.history.length - 1 : 0, + }; + } + + case ACTION_SET_FEATURE_FETCH_ERROR: + return { + ...state, + _meta: { + ...( state._meta ?? {} ), + featureFetchError: action.error, + }, + }; + + case ACTION_SET_FIRST_LOGO_PROMPT_FETCH_ERROR: + return { + ...state, + _meta: { + ...( state._meta ?? {} ), + firstLogoPromptFetchError: action.error, + }, + }; + + case ACTION_SET_ENHANCE_PROMPT_FETCH_ERROR: + return { + ...state, + _meta: { + ...( state._meta ?? {} ), + enhancePromptFetchError: action.error, + }, + }; + + case ACTION_SET_LOGO_FETCH_ERROR: + return { + ...state, + _meta: { + ...( state._meta ?? {} ), + logoFetchError: action.error, + }, + }; + + case ACTION_SET_SAVE_TO_LIBRARY_ERROR: + return { + ...state, + _meta: { + ...( state._meta ?? {} ), + saveToLibraryError: action.error, + }, + }; + + case ACTION_SET_LOGO_UPDATE_ERROR: + return { + ...state, + _meta: { + ...( state._meta ?? {} ), + logoUpdateError: action.error, + }, + }; + + case ACTION_SET_CONTEXT: + return { + ...state, + _meta: { + ...( state._meta ?? {} ), + context: action.context, + }, + }; + } + + return state; +} diff --git a/projects/js-packages/ai-client/src/logo-generator/store/selectors.ts b/projects/js-packages/ai-client/src/logo-generator/store/selectors.ts new file mode 100644 index 0000000000000..c9445b73ab635 --- /dev/null +++ b/projects/js-packages/ai-client/src/logo-generator/store/selectors.ts @@ -0,0 +1,201 @@ +/** + * Types + */ +import { DEFAULT_LOGO_COST } from '../constants.js'; +import type { AiFeatureProps, LogoGeneratorStateProp, Logo, RequestError } from './types.js'; +import type { SiteDetails } from '../types.js'; + +const selectors = { + /** + * Return the AI Assistant feature. + * @param {LogoGeneratorStateProp} state - The app state tree. + * @returns {Partial} The AI Assistant feature data. + */ + getAiAssistantFeature( state: LogoGeneratorStateProp ): Partial< AiFeatureProps > { + // Clean up the _meta property. + const data = { ...state.features.aiAssistantFeature }; + delete data._meta; + + return data; + }, + + /** + * Return the site details. + * @param {LogoGeneratorStateProp} state - The app state tree. + * @returns {Partial | undefined} The site details. + */ + getSiteDetails( state: LogoGeneratorStateProp ): Partial< SiteDetails > | undefined { + return state.siteDetails; + }, + + /** + * Get the isRequesting flag for the AI Assistant feature. + * @param {LogoGeneratorStateProp} state - The app state tree. + * @returns {boolean} The isRequesting flag. + */ + getIsRequestingAiAssistantFeature( state: LogoGeneratorStateProp ): boolean { + return state.features.aiAssistantFeature?._meta?.isRequesting ?? false; + }, + + /** + * Get the logos history. + * @param {LogoGeneratorStateProp} state - The app state tree. + * @returns {Array} The logos history array. + */ + getLogos( state: LogoGeneratorStateProp ): Array< Logo > { + return state.history ?? []; + }, + + /** + * Get the selected logo index. + * @param {LogoGeneratorStateProp} state - The app state tree. + * @returns {number | null} The selected logo index. + */ + getSelectedLogoIndex( state: LogoGeneratorStateProp ): number | null { + return state.selectedLogoIndex ?? null; + }, + + /** + * Get the selected logo. + * @param {LogoGeneratorStateProp} state - The app state tree. + * @returns {Logo} The selected logo. + */ + getSelectedLogo( state: LogoGeneratorStateProp ): Logo { + return state.history?.[ state.selectedLogoIndex ] ?? null; + }, + + /** + * Get the isSavingToLibrary flag. + * @param {LogoGeneratorStateProp} state - The app state tree. + * @returns {boolean} The isSavingToLibrary flag. + */ + getIsSavingLogoToLibrary( state: LogoGeneratorStateProp ): boolean { + return state._meta?.isSavingLogoToLibrary ?? false; + }, + + /** + * Get the isApplyingLogo flag. + * @param {LogoGeneratorStateProp} state - The app state tree. + * @returns {boolean} The isApplyingLogo flag. + */ + getIsApplyingLogo( state: LogoGeneratorStateProp ): boolean { + return state._meta?.isApplyingLogo ?? false; + }, + + /** + * Get the isEnhancingPrompt flag. + * @param {LogoGeneratorStateProp} state - The app state tree. + * @returns {boolean} The isEnhancingPrompt flag. + */ + getIsEnhancingPrompt( state: LogoGeneratorStateProp ): boolean { + return state._meta?.isEnhancingPrompt ?? false; + }, + + /** + * Get the isRequestingImage flag. + * @param {LogoGeneratorStateProp} state - The app state tree. + * @returns {boolean} The isRequestingImage flag. + */ + getIsRequestingImage( state: LogoGeneratorStateProp ): boolean { + return state._meta?.isRequestingImage ?? false; + }, + + /** + * Get an aggregated isBusy flag, based on the loading states of the app. + * @param {LogoGeneratorStateProp} state - The app state tree. + * @returns {boolean} The isBusy flag. + */ + getIsBusy( state: LogoGeneratorStateProp ): boolean { + return ( + selectors.getIsApplyingLogo( state ) || + selectors.getIsSavingLogoToLibrary( state ) || + selectors.getIsRequestingImage( state ) || + selectors.getIsEnhancingPrompt( state ) + ); + }, + + /** + * Get the requireUpgrade value from aiAssistantFeature + * @param {LogoGeneratorStateProp} state - The app state tree. + * @returns {boolean} The requireUpgrade flag. + */ + getRequireUpgrade( state: LogoGeneratorStateProp ): boolean { + const feature = state.features.aiAssistantFeature; + const logoCost = feature?.costs?.[ 'jetpack-ai-logo-generator' ]?.logo ?? DEFAULT_LOGO_COST; + const currentLimit = feature?.currentTier?.value || 0; + const currentUsage = feature?.usagePeriod?.requestsCount || 0; + const isUnlimited = currentLimit === 1; + const hasNoNextTier = ! feature?.nextTier; // If there is no next tier, the user cannot upgrade. + + // Add a local check on top of the feature flag, based on the current usage and logo cost. + return ( + state.features.aiAssistantFeature?.requireUpgrade || + ( ! isUnlimited && ! hasNoNextTier && currentLimit - currentUsage < logoCost ) + ); + }, + + /** + * Get the featureFetchError value. + * @param {LogoGeneratorStateProp} state - The app state tree. + * @returns {RequestError} The featureFetchError value. + */ + getFeatureFetchError( state: LogoGeneratorStateProp ): RequestError { + return state._meta?.featureFetchError ?? null; + }, + + /** + * Get the firstLogoPromptFetchError value. + * @param {LogoGeneratorStateProp} state - The app state tree. + * @returns {RequestError} The firstLogoPromptFetchError value. + */ + getFirstLogoPromptFetchError( state: LogoGeneratorStateProp ): RequestError { + return state._meta?.firstLogoPromptFetchError ?? null; + }, + + /** + * Get the enhancePromptFetchError value. + * @param {LogoGeneratorStateProp} state - The app state tree. + * @returns {RequestError} The enhancePromptFetchError value. + */ + getEnhancePromptFetchError( state: LogoGeneratorStateProp ): RequestError { + return state._meta?.enhancePromptFetchError ?? null; + }, + + /** + * Get the logoFetchError value. + * @param {LogoGeneratorStateProp} state - The app state tree. + * @returns {RequestError} The logoFetchError value. + */ + getLogoFetchError( state: LogoGeneratorStateProp ): RequestError { + return state._meta?.logoFetchError ?? null; + }, + + /** + * Get the saveToLibraryError value. + * @param {LogoGeneratorStateProp} state - The app state tree. + * @returns {RequestError} The saveToLibraryError value. + */ + getSaveToLibraryError( state: LogoGeneratorStateProp ): RequestError { + return state._meta?.saveToLibraryError ?? null; + }, + + /** + * Get the logoUpdateError value. + * @param {LogoGeneratorStateProp} state - The app state tree. + * @returns {RequestError} The logoUpdateError value. + */ + getLogoUpdateError( state: LogoGeneratorStateProp ): RequestError { + return state._meta?.logoUpdateError ?? null; + }, + + /** + * Get the context value. + * @param {LogoGeneratorStateProp} state - The app state tree. + * @returns {string} The context value. + */ + getContext( state: LogoGeneratorStateProp ): string { + return state._meta?.context ?? ''; + }, +}; + +export default selectors; diff --git a/projects/js-packages/ai-client/src/logo-generator/store/types.ts b/projects/js-packages/ai-client/src/logo-generator/store/types.ts new file mode 100644 index 0000000000000..a26e96a0cc9c1 --- /dev/null +++ b/projects/js-packages/ai-client/src/logo-generator/store/types.ts @@ -0,0 +1,207 @@ +import type { SiteDetails } from '../types.js'; + +/** + * Types for the AI Assistant feature. + */ +export type Plan = { + product_id: number; + product_name: string; + product_slug: string; +}; +// AI Assistant feature props +export type UpgradeTypeProp = 'vip' | 'default'; + +export type TierUnlimitedProps = { + slug: 'ai-assistant-tier-unlimited'; + limit: 999999999; + value: 1; + readableLimit: string; +}; + +export type TierFreeProps = { + slug: 'ai-assistant-tier-free'; + limit: 20; + value: 0; +}; + +export type Tier100Props = { + slug: 'ai-assistant-tier-100'; + limit: 100; + value: 100; +}; + +export type Tier200Props = { + slug: 'ai-assistant-tier-200'; + limit: 200; + value: 200; +}; + +export type Tier500Props = { + slug: 'ai-assistant-tier-500'; + limit: 500; + value: 500; +}; + +export type Tier750Props = { + slug: 'ai-assistant-tier-750'; + limit: 750; + value: 750; +}; + +export type Tier1000Props = { + slug: 'ai-assistant-tier-1000'; + limit: 1000; + value: 1000; +}; + +export type TierProp = { + slug: TierSlugProp; + limit: TierLimitProp; + value: TierValueProp; + readableLimit?: string; +}; + +export type TierLimitProp = + | TierUnlimitedProps[ 'limit' ] + | TierFreeProps[ 'limit' ] + | Tier100Props[ 'limit' ] + | Tier200Props[ 'limit' ] + | Tier500Props[ 'limit' ] + | Tier750Props[ 'limit' ] + | Tier1000Props[ 'limit' ]; + +export type TierSlugProp = + | TierUnlimitedProps[ 'slug' ] + | TierFreeProps[ 'slug' ] + | Tier100Props[ 'slug' ] + | Tier200Props[ 'slug' ] + | Tier500Props[ 'slug' ] + | Tier750Props[ 'slug' ] + | Tier1000Props[ 'slug' ]; + +export type TierValueProp = + | TierUnlimitedProps[ 'value' ] + | TierFreeProps[ 'value' ] + | Tier100Props[ 'value' ] + | Tier200Props[ 'value' ] + | Tier500Props[ 'value' ] + | Tier750Props[ 'value' ] + | Tier1000Props[ 'value' ]; + +export type AiFeatureProps = { + hasFeature: boolean; + isOverLimit: boolean; + requestsCount: number; + requestsLimit: number; + requireUpgrade: boolean; + errorMessage?: string; + errorCode?: string; + upgradeType: UpgradeTypeProp; + currentTier?: TierProp; + usagePeriod?: { + currentStart: string; + nextStart: string; + requestsCount: number; + }; + nextTier?: TierProp | null; + tierPlansEnabled?: boolean; + costs?: { + 'jetpack-ai-logo-generator': { + logo: number; + }; + }; +}; + +// Type used in the `wordpress-com/plans` store. +export type AiFeatureStateProps = AiFeatureProps & { + _meta?: { + isRequesting: boolean; + asyncRequestCountdown: number; + asyncRequestTimerId: number; + isRequestingImage: boolean; + }; +}; + +export type Logo = { + url: string; + description: string; + mediaId?: number; +}; + +export type RequestError = string | Error | null; + +export type LogoGeneratorStateProp = { + _meta?: { + isSavingLogoToLibrary: boolean; + isApplyingLogo: boolean; + isRequestingImage: boolean; + isEnhancingPrompt: boolean; + featureFetchError?: RequestError; + firstLogoPromptFetchError?: RequestError; + enhancePromptFetchError?: RequestError; + logoFetchError?: RequestError; + saveToLibraryError?: RequestError; + logoUpdateError?: RequestError; + context: string; + }; + siteDetails?: SiteDetails | Record< string, never >; + features: { + aiAssistantFeature?: AiFeatureStateProps; + }; + history: Array< Logo >; + selectedLogoIndex: number; +}; + +export type Selectors = { + getAiAssistantFeature( siteId?: string ): Partial< AiFeatureProps >; + getIsRequestingAiAssistantFeature(): boolean; + getLogos(): Array< Logo >; + getSelectedLogoIndex(): number | null; + getSelectedLogo(): Logo; + getSiteDetails(): SiteDetails; + getIsSavingLogoToLibrary(): boolean; + getIsApplyingLogo(): boolean; + getIsRequestingImage(): boolean; + getIsEnhancingPrompt(): boolean; + getIsBusy(): boolean; + getRequireUpgrade(): boolean; + getFeatureFetchError(): RequestError; + getFirstLogoPromptFetchError(): RequestError; + getEnhancePromptFetchError(): RequestError; + getLogoFetchError(): RequestError; + getSaveToLibraryError(): RequestError; + getLogoUpdateError(): RequestError; + getContext(): string; +}; + +/* + * `sites/$site/ai-assistant-feature` endpoint response body props + */ +export type AiAssistantFeatureEndpointResponseProps = { + 'is-enabled': boolean; + 'has-feature': boolean; + 'is-over-limit': boolean; + 'requests-count': number; + 'requests-limit': number; + 'usage-period': { + 'current-start': string; + 'next-start': string; + 'requests-count': number; + }; + 'site-require-upgrade': boolean; + 'error-message'?: string; + 'error-code'?: string; + 'is-playground-visible'?: boolean; + 'upgrade-type': UpgradeTypeProp; + 'current-tier': TierProp; + 'tier-plans': Array< TierProp >; + 'next-tier'?: TierProp | null; + 'tier-plans-enabled': boolean; + costs: { + 'jetpack-ai-logo-generator': { + logo: number; + }; + }; +}; + +export type SaveLogo = ( logo: Logo ) => Promise< { mediaId: number; mediaURL: string } >; diff --git a/projects/js-packages/ai-client/src/logo-generator/types.ts b/projects/js-packages/ai-client/src/logo-generator/types.ts new file mode 100644 index 0000000000000..43ad5947878f7 --- /dev/null +++ b/projects/js-packages/ai-client/src/logo-generator/types.ts @@ -0,0 +1,97 @@ +/** + * Types + */ +import type { Logo } from './store/types.js'; + +export type SiteDetails = { + ID: number; + URL: string; + domain: string; + name: string; + description: string; +}; + +export interface GeneratorModalProps { + siteDetails?: SiteDetails; + isOpen: boolean; + onClose: () => void; + context: string; +} + +export interface LogoPresenterProps { + logo?: Logo; + loading?: boolean; + onApplyLogo: () => void; + logoAccepted?: boolean; + siteId: string | number; +} + +export type SaveToMediaLibraryProps = { + siteId: string | number; + url: string; + attrs?: { + caption?: string; + description?: string; + title?: string; + alt?: string; + }; +}; + +export type SaveToMediaLibraryResponseProps = { + code: number; + media: [ + { + ID: number; + URL: string; + }, + ]; +}; + +export type CheckMediaProps = { + siteId: string | number; + mediaId: Logo[ 'mediaId' ]; +}; + +export type SetSiteLogoProps = { + siteId: string | number; + imageId: string | number; +}; + +export type SetSiteLogoResponseProps = { + id: number; + url: string; +}; + +// Token +export type RequestTokenOptions = { + siteDetails?: SiteDetails; + isJetpackSite?: boolean; + expirationTime?: number; +}; + +export type TokenDataProps = { + token: string; + blogId: string | undefined; + expire: number; +}; + +export type TokenDataEndpointResponseProps = { + token: string; + blog_id: string; +}; + +export type SaveToStorageProps = Logo & { + siteId: string; +}; + +export type UpdateInStorageProps = { + siteId: string; + url: Logo[ 'url' ]; + newUrl: Logo[ 'url' ]; + mediaId: Logo[ 'mediaId' ]; +}; + +export type RemoveFromStorageProps = { + mediaId: Logo[ 'mediaId' ]; + siteId: string; +}; diff --git a/projects/js-packages/ai-client/src/types.ts b/projects/js-packages/ai-client/src/types.ts index 3ab53f3fcd109..d984a1c49471a 100644 --- a/projects/js-packages/ai-client/src/types.ts +++ b/projects/js-packages/ai-client/src/types.ts @@ -1,3 +1,5 @@ +import type * as BlockEditorSelectors from '@wordpress/block-editor/store/selectors.js'; + export const ERROR_SERVICE_UNAVAILABLE = 'error_service_unavailable' as const; export const ERROR_QUOTA_EXCEEDED = 'error_quota_exceeded' as const; export const ERROR_MODERATION = 'error_moderation' as const; @@ -113,3 +115,9 @@ export type TranscriptionState = RecordingState | 'validating' | 'processing' | * Lib types */ export type { RenderHTMLRules } from './libs/index.js'; + +export interface BlockEditorStore { + selectors: { + [ key in keyof typeof BlockEditorSelectors ]: ( typeof BlockEditorSelectors )[ key ]; + }; +} diff --git a/projects/js-packages/api/CHANGELOG.md b/projects/js-packages/api/CHANGELOG.md index d0c21669d7d9e..b8c99fdae57b9 100644 --- a/projects/js-packages/api/CHANGELOG.md +++ b/projects/js-packages/api/CHANGELOG.md @@ -2,6 +2,10 @@ ### This is a list detailing changes for the Jetpack RNA Components package releases. +## [0.17.9] - 2024-07-03 +### Changed +- Updated package dependencies. [#38132] + ## [0.17.8] - 2024-06-05 ### Changed - Updated package dependencies. [#37669] @@ -334,6 +338,7 @@ - Add the API methods left behind by the previous PR. - Initial release of jetpack-api package +[0.17.9]: https://github.com/Automattic/jetpack-api/compare/v0.17.8...v0.17.9 [0.17.8]: https://github.com/Automattic/jetpack-api/compare/v0.17.7...v0.17.8 [0.17.7]: https://github.com/Automattic/jetpack-api/compare/v0.17.6...v0.17.7 [0.17.6]: https://github.com/Automattic/jetpack-api/compare/v0.17.5...v0.17.6 diff --git a/projects/js-packages/api/package.json b/projects/js-packages/api/package.json index 44151cf1b34a2..8f77f7a57c21c 100644 --- a/projects/js-packages/api/package.json +++ b/projects/js-packages/api/package.json @@ -1,6 +1,6 @@ { "name": "@automattic/jetpack-api", - "version": "0.17.9-alpha", + "version": "0.17.9", "description": "Jetpack Api Package", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/api/#readme", "bugs": { diff --git a/projects/js-packages/base-styles/CHANGELOG.md b/projects/js-packages/base-styles/CHANGELOG.md index 8e93a25cb3cd0..0352dcea8046d 100644 --- a/projects/js-packages/base-styles/CHANGELOG.md +++ b/projects/js-packages/base-styles/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.6.28] - 2024-07-03 +### Changed +- Updated package dependencies. [#38132] + ## [0.6.27] - 2024-06-17 ### Added - Add color variable [#37802] @@ -293,6 +297,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Updated package dependencies. - Use Node 16.7.0 in tooling. This shouldn't change the behavior of the code itself. +[0.6.28]: https://github.com/Automattic/jetpack-base-styles/compare/0.6.27...0.6.28 [0.6.27]: https://github.com/Automattic/jetpack-base-styles/compare/0.6.26...0.6.27 [0.6.26]: https://github.com/Automattic/jetpack-base-styles/compare/0.6.25...0.6.26 [0.6.25]: https://github.com/Automattic/jetpack-base-styles/compare/0.6.24...0.6.25 diff --git a/projects/js-packages/base-styles/package.json b/projects/js-packages/base-styles/package.json index c09e3c4337bb6..c8ae9e3431955 100644 --- a/projects/js-packages/base-styles/package.json +++ b/projects/js-packages/base-styles/package.json @@ -1,6 +1,6 @@ { "name": "@automattic/jetpack-base-styles", - "version": "0.6.28-alpha", + "version": "0.6.28", "description": "Jetpack components base styles", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/base-styles/#readme", "bugs": { diff --git a/projects/js-packages/boost-score-api/CHANGELOG.md b/projects/js-packages/boost-score-api/CHANGELOG.md index b1c01729f50a6..89d718060608f 100644 --- a/projects/js-packages/boost-score-api/CHANGELOG.md +++ b/projects/js-packages/boost-score-api/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.1.33] - 2024-07-03 +### Changed +- Updated package dependencies. [#38132] + ## [0.1.32] - 2024-06-13 ### Changed - Updated package dependencies. [#37830] @@ -143,6 +147,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Create package for the boost score bar API [#30781] +[0.1.33]: https://github.com/Automattic/jetpack-boost-score-api/compare/v0.1.32...v0.1.33 [0.1.32]: https://github.com/Automattic/jetpack-boost-score-api/compare/v0.1.31...v0.1.32 [0.1.31]: https://github.com/Automattic/jetpack-boost-score-api/compare/v0.1.30...v0.1.31 [0.1.30]: https://github.com/Automattic/jetpack-boost-score-api/compare/v0.1.29...v0.1.30 diff --git a/projects/js-packages/boost-score-api/package.json b/projects/js-packages/boost-score-api/package.json index e1ed1fc85b636..b5e0ad40adc08 100644 --- a/projects/js-packages/boost-score-api/package.json +++ b/projects/js-packages/boost-score-api/package.json @@ -1,6 +1,6 @@ { "name": "@automattic/jetpack-boost-score-api", - "version": "0.1.33-alpha", + "version": "0.1.33", "description": "A package to get the Jetpack Boost score of a site", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/boost-score-api/#readme", "bugs": { diff --git a/projects/js-packages/components/CHANGELOG.md b/projects/js-packages/components/CHANGELOG.md index ade67d24c98b4..f96a15aee4353 100644 --- a/projects/js-packages/components/CHANGELOG.md +++ b/projects/js-packages/components/CHANGELOG.md @@ -2,6 +2,18 @@ ### This is a list detailing changes for the Jetpack RNA Components package releases. +## [0.55.0] - 2024-07-22 +### Removed +- Remove compatibility with WordPress 6.4. [#38386] + +## [0.54.4] - 2024-07-18 +### Changed +- Internal updates. + +## [0.54.3] - 2024-07-03 +### Changed +- Updated package dependencies. [#38132] + ## [0.54.2] - 2024-06-25 ### Added - Added social preview for Threads [#38003] @@ -1079,6 +1091,9 @@ ### Changed - Update node version requirement to 14.16.1 +[0.55.0]: https://github.com/Automattic/jetpack-components/compare/0.54.4...0.55.0 +[0.54.4]: https://github.com/Automattic/jetpack-components/compare/0.54.3...0.54.4 +[0.54.3]: https://github.com/Automattic/jetpack-components/compare/0.54.2...0.54.3 [0.54.2]: https://github.com/Automattic/jetpack-components/compare/0.54.1...0.54.2 [0.54.1]: https://github.com/Automattic/jetpack-components/compare/0.54.0...0.54.1 [0.54.0]: https://github.com/Automattic/jetpack-components/compare/0.53.10...0.54.0 diff --git a/projects/js-packages/components/changelog/fix-create-root-react-19-compat b/projects/js-packages/components/changelog/fix-create-root-react-19-compat new file mode 100644 index 0000000000000..6ff9263d24bd8 --- /dev/null +++ b/projects/js-packages/components/changelog/fix-create-root-react-19-compat @@ -0,0 +1,4 @@ +Significance: patch +Type: changed + +React compatibility: Ensuring createRoot is not called more than once. diff --git a/projects/js-packages/components/changelog/update-improve-social-broken-connection-notices b/projects/js-packages/components/changelog/update-improve-social-broken-connection-notices new file mode 100644 index 0000000000000..8c7ecd7e8a2a2 --- /dev/null +++ b/projects/js-packages/components/changelog/update-improve-social-broken-connection-notices @@ -0,0 +1,4 @@ +Significance: patch +Type: added + +Added `className` prop to `Alert` component diff --git a/projects/js-packages/components/components/admin-section/types.ts b/projects/js-packages/components/components/admin-section/types.ts index 22a81369344b3..333ec9c90bf49 100644 --- a/projects/js-packages/components/components/admin-section/types.ts +++ b/projects/js-packages/components/components/admin-section/types.ts @@ -3,4 +3,5 @@ export type AdminSectionBaseProps = { * The section content */ children: React.ReactNode; + className?: string; }; diff --git a/projects/js-packages/components/components/alert/index.tsx b/projects/js-packages/components/components/alert/index.tsx index 7209a0db1787b..1a98b7c5014e2 100644 --- a/projects/js-packages/components/components/alert/index.tsx +++ b/projects/js-packages/components/components/alert/index.tsx @@ -12,6 +12,9 @@ type AlertProps = { /** Children to be rendered inside the alert. */ children: React.ReactNode; + + /** Wrapper class name */ + className?: string; }; const getIconByLevel = ( level: AlertProps[ 'level' ] ) => { @@ -35,11 +38,17 @@ const getIconByLevel = ( level: AlertProps[ 'level' ] ) => { * @param {object} props - The component properties. * @param {string} props.level - The alert level: error, warning, info, success. * @param {boolean} props.showIcon - Whether to show the alert icon. + * @param {string} props.className - The wrapper class name. * @param {React.Component} props.children - The alert content. * @returns {React.ReactElement} The `Alert` component. */ -const Alert: React.FC< AlertProps > = ( { level = 'warning', children, showIcon = true } ) => { - const classes = clsx( styles.container, styles[ `is-${ level }` ] ); +const Alert: React.FC< AlertProps > = ( { + level = 'warning', + children, + showIcon = true, + className, +} ) => { + const classes = clsx( styles.container, styles[ `is-${ level }` ], className ); return (
diff --git a/projects/js-packages/components/components/boost-score-graph/tooltips-plugin.ts b/projects/js-packages/components/components/boost-score-graph/tooltips-plugin.ts index aeea80f33a0ee..5d0dbfe45fa88 100644 --- a/projects/js-packages/components/components/boost-score-graph/tooltips-plugin.ts +++ b/projects/js-packages/components/components/boost-score-graph/tooltips-plugin.ts @@ -22,8 +22,9 @@ export function tooltipsPlugin( periods ) { */ function init( u: uPlot, _opts: object ) { container.classList.add( 'jb-score-tooltips-container' ); - - reactDom = ReactDOM.createRoot( reactRoot ); + if ( ! reactDom ) { + reactDom = ReactDOM.createRoot( reactRoot ); + } reactRoot.style.position = 'absolute'; reactRoot.style.bottom = -20 + 'px'; reactRoot.style.translate = '-50% calc( 100% - 20px )'; diff --git a/projects/js-packages/components/components/stat-card/index.tsx b/projects/js-packages/components/components/stat-card/index.tsx index 6b47b244d605a..93db43246d695 100644 --- a/projects/js-packages/components/components/stat-card/index.tsx +++ b/projects/js-packages/components/components/stat-card/index.tsx @@ -31,8 +31,7 @@ const StatCard = ( { className, icon, label, value, variant = 'square' }: StatCa
{ label } { variant === 'square' ? ( - // @todo Switch to `placement` once WordPress 6.4 is the minimum. - + { compactValue } diff --git a/projects/js-packages/components/components/stat-card/test/component.tsx b/projects/js-packages/components/components/stat-card/test/component.tsx index 4dd4dc4bde5b9..350777634f5eb 100644 --- a/projects/js-packages/components/components/stat-card/test/component.tsx +++ b/projects/js-packages/components/components/stat-card/test/component.tsx @@ -12,11 +12,6 @@ describe( 'StatCard', () => { it( 'renders the compact value', () => { render( ); expect( screen.getByText( '1.8K' ) ).toBeInTheDocument(); - - // @todo Remove this once WordPress 6.4 is the minimum. - expect( console ).toHaveWarned( - '`position` prop in wp.components.tooltip is deprecated since version 6.4. Please use `placement` prop instead.' - ); } ); } ); diff --git a/projects/js-packages/components/package.json b/projects/js-packages/components/package.json index 3526c97c71e6c..4a621734fe8a8 100644 --- a/projects/js-packages/components/package.json +++ b/projects/js-packages/components/package.json @@ -1,6 +1,6 @@ { "name": "@automattic/jetpack-components", - "version": "0.54.3-alpha", + "version": "0.55.1-alpha", "description": "Jetpack Components Package", "author": "Automattic", "license": "GPL-2.0-or-later", diff --git a/projects/js-packages/connection/CHANGELOG.md b/projects/js-packages/connection/CHANGELOG.md index 33ba4e5683d3e..d0f2badde18a6 100644 --- a/projects/js-packages/connection/CHANGELOG.md +++ b/projects/js-packages/connection/CHANGELOG.md @@ -2,6 +2,18 @@ ### This is a list detailing changes for the Jetpack RNA Connection Component releases. +## [0.34.1] - 2024-07-22 +### Added +- Display the proper error message for suspended sites on site registration. [#38359] + +## [0.34.0] - 2024-07-18 +### Changed +- Connection Screen: remove mention of Stats from the list of available free features. [#38328] + +## [0.33.19] - 2024-07-03 +### Changed +- Updated package dependencies. [#38132] + ## [0.33.18] - 2024-06-21 ### Changed - Update dependencies. @@ -793,6 +805,9 @@ - `Main` and `ConnectUser` components added. - `JetpackRestApiClient` API client added. +[0.34.1]: https://github.com/Automattic/jetpack-connection-js/compare/v0.34.0...v0.34.1 +[0.34.0]: https://github.com/Automattic/jetpack-connection-js/compare/v0.33.19...v0.34.0 +[0.33.19]: https://github.com/Automattic/jetpack-connection-js/compare/v0.33.18...v0.33.19 [0.33.18]: https://github.com/Automattic/jetpack-connection-js/compare/v0.33.17...v0.33.18 [0.33.17]: https://github.com/Automattic/jetpack-connection-js/compare/v0.33.16...v0.33.17 [0.33.16]: https://github.com/Automattic/jetpack-connection-js/compare/v0.33.15...v0.33.16 diff --git a/projects/js-packages/connection/components/connect-screen/basic/stories/index.stories.jsx b/projects/js-packages/connection/components/connect-screen/basic/stories/index.stories.jsx index fbd8991d62d9e..e8e0e63529f13 100644 --- a/projects/js-packages/connection/components/connect-screen/basic/stories/index.stories.jsx +++ b/projects/js-packages/connection/components/connect-screen/basic/stories/index.stories.jsx @@ -14,7 +14,6 @@ const Template = props => (

Secure and speed up your site for free with Jetpack's powerful WordPress tools

    -
  • Measure your impact with beautiful stats
  • Speed up your site with optimized images
  • Protect your site against bot attacks
  • Get notifications if your site goes offline
  • diff --git a/projects/js-packages/connection/components/connect-screen/basic/visual.tsx b/projects/js-packages/connection/components/connect-screen/basic/visual.tsx index 123b779e3d83e..e2139ee048c70 100644 --- a/projects/js-packages/connection/components/connect-screen/basic/visual.tsx +++ b/projects/js-packages/connection/components/connect-screen/basic/visual.tsx @@ -48,6 +48,8 @@ const getErrorMessage = ( errorCode, isOfflineMode ) => { 'Your site host is on a private network. Jetpack can only connect to public sites.', 'jetpack' ); + case 'connection_disabled': + return __( 'This site has been suspended.', 'jetpack' ); } if ( isOfflineMode ) { diff --git a/projects/js-packages/connection/package.json b/projects/js-packages/connection/package.json index 4095cd7659aec..0ac499a5fb4b1 100644 --- a/projects/js-packages/connection/package.json +++ b/projects/js-packages/connection/package.json @@ -1,6 +1,6 @@ { "name": "@automattic/jetpack-connection", - "version": "0.33.19-alpha", + "version": "0.34.1", "description": "Jetpack Connection Component", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/connection/#readme", "bugs": { diff --git a/projects/js-packages/i18n-loader-webpack-plugin/CHANGELOG.md b/projects/js-packages/i18n-loader-webpack-plugin/CHANGELOG.md index 08b2135f2505c..b08baacb4a968 100644 --- a/projects/js-packages/i18n-loader-webpack-plugin/CHANGELOG.md +++ b/projects/js-packages/i18n-loader-webpack-plugin/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2.0.53] - 2024-07-03 +### Changed +- Updated package dependencies. [#38132] + ## [2.0.52] - 2024-06-05 ### Changed - Updated package dependencies. [#37669] @@ -237,6 +241,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Initial release. +[2.0.53]: https://github.com/Automattic/i18n-loader-webpack-plugin/compare/v2.0.52...v2.0.53 [2.0.52]: https://github.com/Automattic/i18n-loader-webpack-plugin/compare/v2.0.51...v2.0.52 [2.0.51]: https://github.com/Automattic/i18n-loader-webpack-plugin/compare/v2.0.50...v2.0.51 [2.0.50]: https://github.com/Automattic/i18n-loader-webpack-plugin/compare/v2.0.49...v2.0.50 diff --git a/projects/js-packages/i18n-loader-webpack-plugin/package.json b/projects/js-packages/i18n-loader-webpack-plugin/package.json index 4396a9dd701ac..d11744eee7c71 100644 --- a/projects/js-packages/i18n-loader-webpack-plugin/package.json +++ b/projects/js-packages/i18n-loader-webpack-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@automattic/i18n-loader-webpack-plugin", - "version": "2.0.53-alpha", + "version": "2.0.53", "description": "A Webpack plugin to load WordPress i18n when Webpack lazy-loads a bundle.", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/i18n-loader-webpack-plugin/#readme", "bugs": { diff --git a/projects/js-packages/idc/CHANGELOG.md b/projects/js-packages/idc/CHANGELOG.md index ee456b99fca35..4a01885e1db41 100644 --- a/projects/js-packages/idc/CHANGELOG.md +++ b/projects/js-packages/idc/CHANGELOG.md @@ -2,6 +2,18 @@ ### This is a list detailing changes for the Jetpack RNA IDC package releases. +## 0.11.5 - 2024-07-23 +### Fixed +- Updated package dependencies. [#38464] + +## 0.11.4 - 2024-07-22 +### Changed +- Update dependencies. + +## 0.11.3 - 2024-07-03 +### Changed +- Updated package dependencies. [#38132] + ## 0.11.2 - 2024-06-21 ### Changed - Update dependencies. diff --git a/projects/js-packages/idc/package.json b/projects/js-packages/idc/package.json index 4e4603626b813..f1572a4ad8bfd 100644 --- a/projects/js-packages/idc/package.json +++ b/projects/js-packages/idc/package.json @@ -1,6 +1,6 @@ { "name": "@automattic/jetpack-idc", - "version": "0.11.3-alpha", + "version": "0.11.5", "description": "Jetpack Connection Component", "author": "Automattic", "license": "GPL-2.0-or-later", diff --git a/projects/js-packages/image-guide/CHANGELOG.md b/projects/js-packages/image-guide/CHANGELOG.md index 0390f2488db70..e773c91e2dbfb 100644 --- a/projects/js-packages/image-guide/CHANGELOG.md +++ b/projects/js-packages/image-guide/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.5.10] - 2024-07-10 +### Changed +- Updated package dependencies. [#38092] + ## [0.5.9] - 2024-06-21 ### Changed - Updated package dependencies. [#37796] @@ -109,6 +113,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Removed - Minor package.json change - removing private entry. +[0.5.10]: https://github.com/Automattic/jetpack-image-guide/compare/v0.5.9...v0.5.10 [0.5.9]: https://github.com/Automattic/jetpack-image-guide/compare/v0.5.8...v0.5.9 [0.5.8]: https://github.com/Automattic/jetpack-image-guide/compare/v0.5.7...v0.5.8 [0.5.7]: https://github.com/Automattic/jetpack-image-guide/compare/v0.5.6...v0.5.7 diff --git a/projects/js-packages/ai-client/changelog/renovate-wordpress-monorepo b/projects/js-packages/image-guide/changelog/renovate-rollup-plugin-svelte-7.x similarity index 100% rename from projects/js-packages/ai-client/changelog/renovate-wordpress-monorepo rename to projects/js-packages/image-guide/changelog/renovate-rollup-plugin-svelte-7.x diff --git a/projects/js-packages/api/changelog/renovate-wordpress-monorepo b/projects/js-packages/image-guide/changelog/renovate-svelte-4.x similarity index 100% rename from projects/js-packages/api/changelog/renovate-wordpress-monorepo rename to projects/js-packages/image-guide/changelog/renovate-svelte-4.x diff --git a/projects/js-packages/base-styles/changelog/renovate-wordpress-monorepo b/projects/js-packages/image-guide/changelog/renovate-svelte-preprocess-6.x similarity index 100% rename from projects/js-packages/base-styles/changelog/renovate-wordpress-monorepo rename to projects/js-packages/image-guide/changelog/renovate-svelte-preprocess-6.x diff --git a/projects/js-packages/image-guide/changelog/update-fix-image-guide-svgs b/projects/js-packages/image-guide/changelog/update-fix-image-guide-svgs new file mode 100644 index 0000000000000..694da4b8779ef --- /dev/null +++ b/projects/js-packages/image-guide/changelog/update-fix-image-guide-svgs @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Fixed a bug where image guide would show up for svg images. diff --git a/projects/js-packages/image-guide/changelog/update-replace-eslint-plugin-svelte3 b/projects/js-packages/image-guide/changelog/update-replace-eslint-plugin-svelte3 deleted file mode 100644 index b43161369d4f3..0000000000000 --- a/projects/js-packages/image-guide/changelog/update-replace-eslint-plugin-svelte3 +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: removed -Comment: Remove svelte variables unused since #31145. No change to functionality. - - diff --git a/projects/js-packages/image-guide/changelog/update-rollup-plugin-svelte-svg b/projects/js-packages/image-guide/changelog/update-rollup-plugin-svelte-svg deleted file mode 100644 index 5029859c729b3..0000000000000 --- a/projects/js-packages/image-guide/changelog/update-rollup-plugin-svelte-svg +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: changed -Comment: Update rollup-plugin-svelte-svg dependency. No change to functionality. - - diff --git a/projects/js-packages/image-guide/package.json b/projects/js-packages/image-guide/package.json index 9e4aaba5d6e35..3c2bfa39a1d18 100644 --- a/projects/js-packages/image-guide/package.json +++ b/projects/js-packages/image-guide/package.json @@ -1,6 +1,6 @@ { "name": "@automattic/jetpack-image-guide", - "version": "0.5.10-alpha", + "version": "0.5.11-alpha", "description": "Go through the dom to analyze image size on screen vs actual file size.", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/image-guide/#readme", "type": "module", @@ -49,11 +49,11 @@ "postcss": "8.4.31", "rollup": "2.79.1", "rollup-plugin-postcss": "4.0.2", - "rollup-plugin-svelte": "7.1.0", + "rollup-plugin-svelte": "7.2.2", "rollup-plugin-svelte-svg": "1.0.0-beta.6", "sass": "1.64.1", - "svelte": "3.58.0", - "svelte-preprocess": "5.0.4", + "svelte": "4.2.18", + "svelte-preprocess": "6.0.2", "tslib": "2.5.0", "typescript": "5.0.4", "webpack": "5.76.0", diff --git a/projects/js-packages/image-guide/src/find-image-elements.ts b/projects/js-packages/image-guide/src/find-image-elements.ts index ecc60455dfd3e..802082c7a8020 100644 --- a/projects/js-packages/image-guide/src/find-image-elements.ts +++ b/projects/js-packages/image-guide/src/find-image-elements.ts @@ -1,4 +1,4 @@ -import { FetchFn, MeasurableImage } from './MeasurableImage.js'; +import { MeasurableImage, type FetchFn } from './MeasurableImage.js'; /** * Get elements that either are image tags or have a background image. @@ -9,6 +9,9 @@ import { FetchFn, MeasurableImage } from './MeasurableImage.js'; export function findMeasurableElements( nodes: Element[] ): HTMLElement[] | HTMLImageElement[] { return nodes.filter( ( el ): el is HTMLElement | HTMLImageElement => { if ( el instanceof HTMLImageElement ) { + if ( isSvgUrl( el.src ) ) { + return false; + } return true; } if ( el instanceof HTMLElement ) { @@ -55,6 +58,11 @@ export function backgroundImageSource( node: HTMLElement ) { return null; } +function isSvgUrl( srcUrl: string ): boolean { + const url = new URL( srcUrl ); + return url.pathname.toLowerCase().endsWith( '.svg' ); +} + /** * Create MeasurableImage objects from a list of nodes * and remove any nodes that can't be measured. @@ -79,7 +87,6 @@ export async function getMeasurableImages( */ return null; } - return new MeasurableImage( node, backgroundImageSource ); } diff --git a/projects/js-packages/image-guide/src/index.ts b/projects/js-packages/image-guide/src/index.ts index 27e3dd50f8e74..24e376ac7a08d 100644 --- a/projects/js-packages/image-guide/src/index.ts +++ b/projects/js-packages/image-guide/src/index.ts @@ -1,5 +1,5 @@ -import { Dimensions, FetchFn, MeasurableImage, Weight } from './MeasurableImage.js'; -import { TracksCallback } from './analytics.js'; +import { MeasurableImage, type Dimensions, type FetchFn, type Weight } from './MeasurableImage.js'; +import { type TracksCallback } from './analytics.js'; import { getMeasurableImages } from './find-image-elements.js'; import { setupLoadListener } from './initialize.js'; import AdminBarToggle from './ui/AdminBarToggle.svelte'; diff --git a/projects/js-packages/image-guide/src/stores/GuideState.ts b/projects/js-packages/image-guide/src/stores/GuideState.ts index 77c422ba815cf..5844cd3204e30 100644 --- a/projects/js-packages/image-guide/src/stores/GuideState.ts +++ b/projects/js-packages/image-guide/src/stores/GuideState.ts @@ -1,4 +1,4 @@ -import { derived, Writable, writable } from 'svelte/store'; +import { derived, writable, type Writable } from 'svelte/store'; /** * Guide State is a Svelte Store that keeps track diff --git a/projects/js-packages/image-guide/src/stores/MeasurableImageStore.ts b/projects/js-packages/image-guide/src/stores/MeasurableImageStore.ts index 768cf3f548a5e..3d96607464a90 100644 --- a/projects/js-packages/image-guide/src/stores/MeasurableImageStore.ts +++ b/projects/js-packages/image-guide/src/stores/MeasurableImageStore.ts @@ -1,4 +1,4 @@ -import { Writable, Readable, writable, derived } from 'svelte/store'; +import { writable, derived, type Writable, type Readable } from 'svelte/store'; import { MeasurableImage } from '../MeasurableImage.js'; import type { Dimensions, Weight } from '../MeasurableImage.js'; diff --git a/projects/js-packages/image-guide/src/ui/Bubble.svelte b/projects/js-packages/image-guide/src/ui/Bubble.svelte index 535852059389b..9b3e40fbfa049 100644 --- a/projects/js-packages/image-guide/src/ui/Bubble.svelte +++ b/projects/js-packages/image-guide/src/ui/Bubble.svelte @@ -35,6 +35,9 @@ } + + +
    { - stores.forEach( store => store.updateDimensions() ); - } ); + onMount(() => { + stores.forEach(store => store.updateDimensions()); + }); - function closeDetails( e ) { + function closeDetails(e) { // Don't exit when hovering the Portal if ( e.relatedTarget && // Don't exit when hovering the Popup - e.relatedTarget.classList.contains( 'keep-guide-open' ) + e.relatedTarget.classList.contains('keep-guide-open') ) { return; } @@ -30,42 +31,53 @@ show = false; } - function getGuideSize( width = -1, height = -1 ): GuideSize { - if ( width < 200 || height < 200 ) { + function getGuideSize(width = -1, height = -1): GuideSize { + if (width < 200 || height < 200) { return 'micro'; - } else if ( width < 400 || height < 400 ) { + } else if (width < 400 || height < 400) { return 'small'; } return 'normal'; } - function toggleBackdrop( on = false ) { - if ( on ) { - stores.forEach( store => store.node.classList.add( 'jetpack-boost-guide__backdrop' ) ); + function toggleBackdrop(on = false) { + if (on) { + stores.forEach(store => store.node.classList.add('jetpack-boost-guide__backdrop')); } else { - stores.forEach( store => store.node.classList.remove( 'jetpack-boost-guide__backdrop' ) ); + stores.forEach(store => store.node.classList.remove('jetpack-boost-guide__backdrop')); } } // Use the first image available in the stores to determine the guide size - const sizeOnPage = stores[ 0 ].sizeOnPage; - $: size = getGuideSize( $sizeOnPage.width, $sizeOnPage.height ); + const sizeOnPage = stores[0].sizeOnPage; + $: size = getGuideSize($sizeOnPage.width, $sizeOnPage.height); - $: toggleBackdrop( show !== false ); + $: toggleBackdrop(show !== false); let position = { top: 0, left: 0, }; - function hover( e: CustomEvent ) { + function hover(e: CustomEvent) { const detail = e.detail; const index = detail.index; position = detail.position; show = index; } + + /** + * Only show image guide if at least one of the images + * has a file size available. + */ + const hasItemsWithFileSize = derived(stores.map(s => s.fileSize), $fileSizes => + $fileSizes.some(fileSize => fileSize.width !== -1 && fileSize.height !== -1) + ); -{#if $guideState === 'active'} +{#if $guideState === 'active' && $hasItemsWithFileSize} + + +
    component source for details. --> - + {/if}
    {/if} diff --git a/projects/js-packages/image-guide/src/ui/Popup.svelte b/projects/js-packages/image-guide/src/ui/Popup.svelte index ed01ee3f095dd..cc23659dae727 100644 --- a/projects/js-packages/image-guide/src/ui/Popup.svelte +++ b/projects/js-packages/image-guide/src/ui/Popup.svelte @@ -67,6 +67,9 @@ + + +
    { service ? __( 'There is an issue with this connection.', 'jetpack' ) - : _x( 'This platform is no longer supported.', '', 'jetpack' ) } + : createInterpolateElement( + sprintf( + '%1$s %2$s', + __( 'This platform is no longer supported.', 'jetpack' ), + __( 'You can use our Manual Sharing feature instead.', 'jetpack' ) + ), + { + link: ( + + ), + } + ) }   { service ? ( diff --git a/projects/js-packages/publicize-components/src/components/connection-management/disconnect.tsx b/projects/js-packages/publicize-components/src/components/connection-management/disconnect.tsx index 0088d19d5d154..c41a1ba2ec2c3 100644 --- a/projects/js-packages/publicize-components/src/components/connection-management/disconnect.tsx +++ b/projects/js-packages/publicize-components/src/components/connection-management/disconnect.tsx @@ -1,6 +1,5 @@ import { Button } from '@automattic/jetpack-components'; -// eslint-disable-next-line wpcalypso/no-unsafe-wp-apis -import { __experimentalConfirmDialog as ConfirmDialog } from '@wordpress/components'; +import { __experimentalConfirmDialog as ConfirmDialog } from '@wordpress/components'; // eslint-disable-line @wordpress/no-unsafe-wp-apis import { useDispatch, useSelect } from '@wordpress/data'; import { createInterpolateElement, useCallback, useReducer } from '@wordpress/element'; import { __, _x, sprintf } from '@wordpress/i18n'; diff --git a/projects/js-packages/publicize-components/src/components/connection-management/reconnect.tsx b/projects/js-packages/publicize-components/src/components/connection-management/reconnect.tsx index bab4d746457b3..7dedd9865b39d 100644 --- a/projects/js-packages/publicize-components/src/components/connection-management/reconnect.tsx +++ b/projects/js-packages/publicize-components/src/components/connection-management/reconnect.tsx @@ -21,7 +21,7 @@ export type ReconnectProps = { * @returns {import('react').ReactNode} - React element */ export function Reconnect( { connection, service, variant = 'link' }: ReconnectProps ) { - const { deleteConnectionById, setKeyringResult, openConnectionsModal } = + const { deleteConnectionById, setKeyringResult, openConnectionsModal, setReconnectingAccount } = useDispatch( socialStore ); const { isDisconnecting } = useSelect( @@ -58,6 +58,12 @@ export function Reconnect( { connection, service, variant = 'link' }: ReconnectP return; } + await setReconnectingAccount( + // Join service name and external ID + // just in case the external ID alone is not unique. + `${ connection.service_name }:${ connection.external_id }` + ); + const formData = new FormData(); if ( service.ID === 'mastodon' ) { @@ -65,24 +71,22 @@ export function Reconnect( { connection, service, variant = 'link' }: ReconnectP } requestAccess( formData ); - }, [ connection, deleteConnectionById, requestAccess, service.ID ] ); + }, [ connection, deleteConnectionById, requestAccess, service.ID, setReconnectingAccount ] ); if ( ! connection.can_disconnect ) { return null; } return ( - <> - - + ); } diff --git a/projects/js-packages/publicize-components/src/components/connection-verify/index.js b/projects/js-packages/publicize-components/src/components/connection-verify/index.js deleted file mode 100644 index 17343f0628ce3..0000000000000 --- a/projects/js-packages/publicize-components/src/components/connection-verify/index.js +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Publicize connections verification component. - * - * Component to create Ajax request to check - * all connections. If any connection tests failed, - * a refresh link may be provided to the user. If - * no connection tests fail, this component will - * not render anything. - */ - -import { compose } from '@wordpress/compose'; -import { withDispatch, withSelect } from '@wordpress/data'; -import { Component } from '@wordpress/element'; -import { SOCIAL_STORE_ID } from '../../social-store'; - -class PublicizeConnectionVerify extends Component { - componentDidMount() { - this.props.refreshConnections(); - } - - render() { - return null; - } -} - -export default compose( [ - withSelect( select => ( { - failedConnections: select( SOCIAL_STORE_ID ).getFailedConnections(), - } ) ), - withDispatch( dispatch => ( { - refreshConnections: dispatch( SOCIAL_STORE_ID ).refreshConnectionTestResults, - } ) ), -] )( PublicizeConnectionVerify ); diff --git a/projects/js-packages/publicize-components/src/components/form/broken-connections-notice.tsx b/projects/js-packages/publicize-components/src/components/form/broken-connections-notice.tsx index fd30a614099bf..e5335a1f41c09 100644 --- a/projects/js-packages/publicize-components/src/components/form/broken-connections-notice.tsx +++ b/projects/js-packages/publicize-components/src/components/form/broken-connections-notice.tsx @@ -1,12 +1,14 @@ import { Button } from '@automattic/jetpack-components'; import { ExternalLink } from '@wordpress/components'; import { useDispatch, useSelect } from '@wordpress/data'; -import { createInterpolateElement } from '@wordpress/element'; -import { _n } from '@wordpress/i18n'; +import { createInterpolateElement, Fragment } from '@wordpress/element'; +import { __, _x } from '@wordpress/i18n'; import usePublicizeConfig from '../../hooks/use-publicize-config'; import useSocialMediaConnections from '../../hooks/use-social-media-connections'; import { store } from '../../social-store'; +import { Connection } from '../../social-store/types'; import Notice from '../notice'; +import { SupportedService, useSupportedServices } from '../services/use-supported-services'; import styles from './styles.module.scss'; import { checkConnectionCode } from './utils'; @@ -38,14 +40,77 @@ export const BrokenConnectionsNotice: React.FC = () => { ); + const supportedServices = useSupportedServices(); + + if ( ! brokenConnections.length ) { + return null; + } + + const servicesMap = supportedServices.reduce< Record< string, SupportedService > >( + ( acc, service ) => { + acc[ service.ID ] = service; + return acc; + }, + {} + ); + + // Group broken connections by service + // Since Object.groupBy is not supported widely yet, we use a manual grouping + const brokenConnectionsList = brokenConnections.reduce< Record< string, Array< Connection > > >( + ( acc, connection ) => { + if ( ! acc[ connection.service_name ] ) { + acc[ connection.service_name ] = []; + } + acc[ connection.service_name ].push( connection ); + return acc; + }, + {} + ); + return ( brokenConnections.length > 0 && ( + { __( 'Your following connections need to be reconnected:', 'jetpack' ) } +
      + { Object.entries( brokenConnectionsList ).map( ( [ service_name, connectionsList ] ) => { + const serviceLabel = + // For Jetpack sites, we should have the service in the map + // But for WPCOM sites, we might not have the service in the map yet + servicesMap[ service_name ]?.label || + // So we capitalize the service name + service_name[ 0 ].toUpperCase() + service_name.substring( 1 ); + + return ( +
    • +
      + + { serviceLabel } + { _x( ':', 'Colon to display before the list of connections', 'jetpack' ) } +   + + { + // Since Intl.ListFormat is not allowed in Jetpack yet, + // we join the connections with a comma and space + connectionsList.map( ( { display_name, external_display, id }, i ) => ( + + + { display_name || external_display } + + { i < connectionsList.length - 1 && + _x( ',', 'Comma to separate list of social media accounts', 'jetpack' ) + + ' ' } + + ) ) + } +
      +
    • + ); + } ) } +
    { createInterpolateElement( - _n( - 'One of your social connections is broken. Reconnect them in the connections management section.', - 'Some of your social connections are broken. Reconnect them in the connections management section.', - brokenConnections.length, + _x( + 'Please reconnect them in the connections management section.', + '"them" refers to the broken connections.', 'jetpack' ), { diff --git a/projects/js-packages/publicize-components/src/components/form/connections-list.tsx b/projects/js-packages/publicize-components/src/components/form/connections-list.tsx index 55b29b3b75def..6ef92f1bf8c7e 100644 --- a/projects/js-packages/publicize-components/src/components/form/connections-list.tsx +++ b/projects/js-packages/publicize-components/src/components/form/connections-list.tsx @@ -1,6 +1,7 @@ import usePublicizeConfig from '../../hooks/use-publicize-config'; import useSocialMediaConnections from '../../hooks/use-social-media-connections'; import PublicizeConnection from '../connection'; +import { EnabledConnectionsNotice } from './enabled-connections-notice'; import { SettingsButton } from './settings-button'; import styles from './styles.module.scss'; import { useConnectionState } from './use-connection-state'; @@ -33,6 +34,7 @@ export const ConnectionsList: React.FC = () => { ); } ) }
+ { ! needsUserConnection ? : null }
); diff --git a/projects/js-packages/publicize-components/src/components/form/index.tsx b/projects/js-packages/publicize-components/src/components/form/index.tsx index 33fd252508e30..0da1c62e717bb 100644 --- a/projects/js-packages/publicize-components/src/components/form/index.tsx +++ b/projects/js-packages/publicize-components/src/components/form/index.tsx @@ -18,7 +18,6 @@ import useImageGeneratorConfig from '../../hooks/use-image-generator-config'; import useMediaDetails from '../../hooks/use-media-details'; import useMediaRestrictions, { NO_MEDIA_ERROR } from '../../hooks/use-media-restrictions'; import useRefreshAutoConversionSettings from '../../hooks/use-refresh-auto-conversion-settings'; -import useRefreshConnections from '../../hooks/use-refresh-connections'; import useSocialMediaConnections from '../../hooks/use-social-media-connections'; import { store as socialStore } from '../../social-store'; import { ThemedConnectionsModal as ManageConnectionsModal } from '../manage-connections-modal'; @@ -26,7 +25,6 @@ import { AdvancedPlanNudge } from './advanced-plan-nudge'; import { AutoConversionNotice } from './auto-conversion-notice'; import { BrokenConnectionsNotice } from './broken-connections-notice'; import { ConnectionsList } from './connections-list'; -import { EnabledConnectionsNotice } from './enabled-connections-notice'; import { InstagramNoMediaNotice } from './instagram-no-media-notice'; import { SettingsButton } from './settings-button'; import { ShareCountInfo } from './share-count-info'; @@ -42,7 +40,6 @@ import { ValidationNotice } from './validation-notice'; */ export default function PublicizeForm() { const { connections, hasConnections, hasEnabledConnections } = useSocialMediaConnections(); - const refreshConnections = useRefreshConnections(); const { isEnabled: isSocialImageGeneratorEnabledForPost } = useImageGeneratorConfig(); const { shouldShowNotice, NOTICES } = useDismissNotice(); const { @@ -95,8 +92,6 @@ export default function PublicizeForm() { refreshAutoConversionSettings(); } - refreshConnections(); - return ( { @@ -108,7 +103,6 @@ export default function PublicizeForm() { - diff --git a/projects/js-packages/publicize-components/src/components/form/settings-button.tsx b/projects/js-packages/publicize-components/src/components/form/settings-button.tsx index 60dc1dd61e599..8f591da12a2f7 100644 --- a/projects/js-packages/publicize-components/src/components/form/settings-button.tsx +++ b/projects/js-packages/publicize-components/src/components/form/settings-button.tsx @@ -24,21 +24,23 @@ type SettingsButtonProps = { * @returns {import('react').ReactNode} The button/link component. */ export function SettingsButton( { label, variant = 'primary' }: SettingsButtonProps ) { - const { useAdminUiV1 } = useSelect( select => { + const { useAdminUiV1, connections } = useSelect( select => { return { useAdminUiV1: select( store ).useAdminUiV1(), + connections: select( store ).getConnections(), }; }, [] ); const { openConnectionsModal } = useDispatch( store ); const { connectionsAdminUrl } = usePublicizeConfig(); const text = label || __( 'Manage connections', 'jetpack' ); + const hasConnections = connections.length > 0; return useAdminUiV1 ? ( - + + { canFix + ? __( + 'Please fix the broken connections or disconnect them to create more connections.', + 'jetpack' + ) + : _n( 'Broken connection', 'Broken connections', brokenConnections.length, 'jetpack' ) } + ); } diff --git a/projects/js-packages/publicize-components/src/components/services/style.module.scss b/projects/js-packages/publicize-components/src/components/services/style.module.scss index 2f4a6bc91c2bd..aefd3d52d95ef 100644 --- a/projects/js-packages/publicize-components/src/components/services/style.module.scss +++ b/projects/js-packages/publicize-components/src/components/services/style.module.scss @@ -119,16 +119,9 @@ white-space: nowrap; } - .broken-connection { - white-space: nowrap; - font-weight: 500; - - // Add some specificity to override the default button styles - &:global(.components-button.is-link) { - color: var(--jp-yellow-40); - text-decoration: none; - font-size: inherit; - } + .broken-connection-alert { + margin-block: 0px; + font-size: var(--font-body-small); } .service-connection-list { diff --git a/projects/js-packages/publicize-components/src/components/social-image-generator/panel/index.js b/projects/js-packages/publicize-components/src/components/social-image-generator/panel/index.js index 30d0af363963e..fd5f7ec71fd26 100644 --- a/projects/js-packages/publicize-components/src/components/social-image-generator/panel/index.js +++ b/projects/js-packages/publicize-components/src/components/social-image-generator/panel/index.js @@ -1,10 +1,9 @@ -/* eslint-disable wpcalypso/no-unsafe-wp-apis */ import { useGlobalNotices } from '@automattic/jetpack-components'; import { PanelBody, ToggleControl, Button, - __experimentalHStack as HStack, + __experimentalHStack as HStack, // eslint-disable-line @wordpress/no-unsafe-wp-apis } from '@wordpress/components'; import { useCallback, useState, Fragment } from '@wordpress/element'; import { __, _x } from '@wordpress/i18n'; diff --git a/projects/js-packages/publicize-components/src/components/social-previews/use-available-services.js b/projects/js-packages/publicize-components/src/components/social-previews/use-available-services.js index 2050da1c9d6f6..e479b1994d510 100644 --- a/projects/js-packages/publicize-components/src/components/social-previews/use-available-services.js +++ b/projects/js-packages/publicize-components/src/components/social-previews/use-available-services.js @@ -1,12 +1,7 @@ import { SocialServiceIcon } from '@automattic/jetpack-components'; import { __, _x } from '@wordpress/i18n'; import React, { useMemo } from 'react'; -import { - CONNECTION_SERVICE_INSTAGRAM_BUSINESS, - CONNECTION_SERVICE_MASTODON, - CONNECTION_SERVICE_NEXTDOOR, - CONNECTION_SERVICE_THREADS, -} from '../../social-store'; +import { CONNECTION_SERVICE_THREADS } from '../../social-store'; import { getSupportedAdditionalConnections } from '../../utils'; import FacebookPreview from './facebook'; import GoogleSearch from './google-search'; @@ -25,11 +20,6 @@ import Twitter from './twitter'; */ export function useAvailableSerivces() { const additionalConnections = getSupportedAdditionalConnections(); - const isInstagramSupported = additionalConnections.includes( - CONNECTION_SERVICE_INSTAGRAM_BUSINESS - ); - const isMastodonSupported = additionalConnections.includes( CONNECTION_SERVICE_MASTODON ); - const isNextdoorSupported = additionalConnections.includes( CONNECTION_SERVICE_NEXTDOOR ); const isThreadsSupported = additionalConnections.includes( CONNECTION_SERVICE_THREADS ); return useMemo( @@ -53,14 +43,12 @@ export function useAvailableSerivces() { name: 'facebook', preview: FacebookPreview, }, - isInstagramSupported - ? { - title: __( 'Instagram', 'jetpack' ), - icon: props => , - name: 'instagram', - preview: Instagram, - } - : null, + { + title: __( 'Instagram', 'jetpack' ), + icon: props => , + name: 'instagram', + preview: Instagram, + }, isThreadsSupported ? { title: _x( @@ -79,29 +67,25 @@ export function useAvailableSerivces() { name: 'linkedin', preview: LinkedIn, }, - isNextdoorSupported - ? { - title: __( 'Nextdoor', 'jetpack' ), - icon: props => , - name: 'nextdoor', - preview: Nextdoor, - } - : null, + { + title: __( 'Nextdoor', 'jetpack' ), + icon: props => , + name: 'nextdoor', + preview: Nextdoor, + }, { title: __( 'Tumblr', 'jetpack' ), icon: props => , name: 'tumblr', preview: TumblrPreview, }, - isMastodonSupported - ? { - title: __( 'Mastodon', 'jetpack' ), - icon: props => , - name: 'mastodon', - preview: MastodonPreview, - } - : null, + { + title: __( 'Mastodon', 'jetpack' ), + icon: props => , + name: 'mastodon', + preview: MastodonPreview, + }, ].filter( Boolean ), - [ isInstagramSupported, isMastodonSupported, isNextdoorSupported, isThreadsSupported ] + [ isThreadsSupported ] ); } diff --git a/projects/js-packages/publicize-components/src/hooks/use-dismiss-notice/index.js b/projects/js-packages/publicize-components/src/hooks/use-dismiss-notice/index.js index 56bedf670762b..b95ef88e06085 100644 --- a/projects/js-packages/publicize-components/src/hooks/use-dismiss-notice/index.js +++ b/projects/js-packages/publicize-components/src/hooks/use-dismiss-notice/index.js @@ -11,7 +11,6 @@ import { useCallback, useMemo, useState } from '@wordpress/element'; */ const NOTICES = { - instagram: 'instagram', advancedUpgradeEditor: 'advanced-upgrade-nudge-editor', advancedUpgradeAdmin: 'advanced-upgrade-nudge-admin', autoConversion: 'auto-conversion-editor-notice', diff --git a/projects/js-packages/publicize-components/src/hooks/use-media-restrictions/index.js b/projects/js-packages/publicize-components/src/hooks/use-media-restrictions/index.js index 47e7f5c859c0d..a9411f0e65bca 100644 --- a/projects/js-packages/publicize-components/src/hooks/use-media-restrictions/index.js +++ b/projects/js-packages/publicize-components/src/hooks/use-media-restrictions/index.js @@ -1,4 +1,5 @@ import { useRef, useMemo } from '@wordpress/element'; +import useAttachedMedia from '../use-attached-media'; import { DEFAULT_RESTRICTIONS, GLOBAL_MAX_SIZE, @@ -128,9 +129,10 @@ const getVideoValidationError = ( sizeInMb, length, width, height, videoLimits ) * @param {object} metaData - Media metadata, mime, fileSize and length. * @param {object} mediaData - Data for media, width, height, source_url etc. * @param {string} serviceName - The name of the social media service we want to validate against. facebook, tumblr etc. + * @param {boolean} hasAttachedMedia - Whether the media is attached. * @returns {(FILE_SIZE_ERROR | FILE_TYPE_ERROR | VIDEO_LENGTH_TOO_SHORT_ERROR | VIDEO_LENGTH_TOO_LONG_ERROR)} Returns validation error. */ -const getValidationError = ( metaData, mediaData, serviceName ) => { +const getValidationError = ( metaData, mediaData, serviceName, hasAttachedMedia ) => { const restrictions = RESTRICTIONS[ serviceName ] ?? DEFAULT_RESTRICTIONS; if ( ! metaData || Object.keys( metaData ).length === 0 ) { @@ -139,6 +141,11 @@ const getValidationError = ( metaData, mediaData, serviceName ) => { const { mime, fileSize } = metaData; + // If the media is not required and there is no attached media, we don't need to validate it. + if ( ! restrictions.requiresMedia && ! hasAttachedMedia ) { + return null; + } + if ( ! ( mime && restrictions.allowedMediaTypes.includes( mime.toLowerCase() ) ) ) { return FILE_TYPE_ERROR; } @@ -169,13 +176,20 @@ const getValidationError = ( metaData, mediaData, serviceName ) => { * @returns {object} Social media connection handler. */ const useMediaRestrictions = ( connections, media, { isSocialImageGeneratorEnabledForPost } ) => { + const { attachedMedia } = useAttachedMedia(); + const hasAttachedMedia = attachedMedia.length > 0; const errors = useRef( {} ); return useMemo( () => { const newErrors = isSocialImageGeneratorEnabledForPost ? {} : connections.reduce( ( errs, { connection_id, service_name } ) => { - const error = getValidationError( media.metaData, media.mediaData, service_name ); + const error = getValidationError( + media.metaData, + media.mediaData, + service_name, + hasAttachedMedia + ); if ( error ) { errs[ connection_id ] = error; } @@ -189,7 +203,13 @@ const useMediaRestrictions = ( connections, media, { isSocialImageGeneratorEnabl validationErrors: errors.current, isConvertible: isMediaConvertible( media.metaData ), }; - }, [ isSocialImageGeneratorEnabledForPost, connections, media.metaData, media.mediaData ] ); + }, [ + isSocialImageGeneratorEnabledForPost, + connections, + media.metaData, + media.mediaData, + hasAttachedMedia, + ] ); }; export default useMediaRestrictions; diff --git a/projects/js-packages/publicize-components/src/hooks/use-media-restrictions/test/index.test.js b/projects/js-packages/publicize-components/src/hooks/use-media-restrictions/test/index.test.js index 98c99805a397d..f7d0bb302c978 100644 --- a/projects/js-packages/publicize-components/src/hooks/use-media-restrictions/test/index.test.js +++ b/projects/js-packages/publicize-components/src/hooks/use-media-restrictions/test/index.test.js @@ -1,4 +1,5 @@ import { renderHook } from '@testing-library/react'; +import useAttachedMedia from '../../use-attached-media'; import useMediaRestrictions, { FILE_SIZE_ERROR, FILE_TYPE_ERROR, @@ -6,6 +7,8 @@ import useMediaRestrictions, { VIDEO_LENGTH_TOO_SHORT_ERROR, } from '../index'; +jest.mock( '../../use-attached-media', () => jest.fn() ); + const DUMMY_CONNECTIONS = [ { service_name: 'facebook', @@ -58,6 +61,33 @@ const getHookProps = ( ]; describe( 'useMediaRestrictions hook', () => { + beforeEach( () => { + // Mocking attached media to allow validation + useAttachedMedia.mockReturnValue( { attachedMedia: [ 1 ] } ); + } ); + + test( 'Should not run validation if a connection does not require media or if attachedMedia is empty', () => { + // Mocking useAttachedMedia to return an empty array to simulate no attached media + useAttachedMedia.mockReturnValue( { attachedMedia: [] } ); + + // Define media that would normally fail validation + const INVALID_MEDIA = { + metaData: { mime: 'image/jpg', fileSize: 100000000 }, // Large file size + mediaData: { width: 400, height: 500 }, + }; + + const { result } = renderHook( () => + useMediaRestrictions( + ...getHookProps( { media: INVALID_MEDIA, connections: DUMMY_CONNECTIONS } ) + ) + ); + + // Expect only the Instagram connection to have an error since it requires media + // and we validate it even if there is no attached media + expect( result.current.validationErrors ).toEqual( { + 'instagram-business': 'FILE_SIZE_ERROR', + } ); + } ); test( 'should not get any errors for image that accepted by all platforms', () => { VALID_MEDIA_ALL.forEach( media => { const { result } = renderHook( () => useMediaRestrictions( ...getHookProps( { media } ) ) ); diff --git a/projects/js-packages/publicize-components/src/hooks/use-refresh-connections/index.js b/projects/js-packages/publicize-components/src/hooks/use-refresh-connections/index.js index 8ac24950829f5..9a4c0556372cc 100644 --- a/projects/js-packages/publicize-components/src/hooks/use-refresh-connections/index.js +++ b/projects/js-packages/publicize-components/src/hooks/use-refresh-connections/index.js @@ -10,11 +10,20 @@ import useSelectSocialMediaConnections from '../use-social-media-connections'; */ export default function useRefreshConnections() { const shouldAutoRefresh = useRef( false ); + const isInitialRefresh = useRef( true ); + const pageHasFocus = usePageVisibility(); const { refresh: refreshConnections } = useSelectSocialMediaConnections(); + + const initialRefresh = useDebounce( refreshConnections, 0 ); const debouncedRefresh = useDebounce( refreshConnections, 2000 ); return () => { + if ( isInitialRefresh.current ) { + initialRefresh(); + isInitialRefresh.current = false; + } + if ( ! pageHasFocus ) { shouldAutoRefresh.current = true; debouncedRefresh.cancel(); diff --git a/projects/js-packages/publicize-components/src/social-store/actions/connection-data.js b/projects/js-packages/publicize-components/src/social-store/actions/connection-data.js index f1497f06a4f28..5e2c7ec0aae9d 100644 --- a/projects/js-packages/publicize-components/src/social-store/actions/connection-data.js +++ b/projects/js-packages/publicize-components/src/social-store/actions/connection-data.js @@ -7,12 +7,16 @@ import { ADD_CONNECTION, DELETE_CONNECTION, DELETING_CONNECTION, + SET_RECONNECTING_ACCOUNT, SET_CONNECTIONS, SET_KEYRING_RESULT, TOGGLE_CONNECTION, TOGGLE_CONNECTIONS_MODAL, UPDATE_CONNECTION, UPDATING_CONNECTION, + REQUEST_TYPE_REFRESH_CONNECTIONS, + ADD_ABORT_CONTROLLER, + REMOVE_ABORT_CONTROLLERS, } from './constants'; /** @@ -106,6 +110,64 @@ export function mergeConnections( freshConnections ) { }; } +/** + * Create an abort controller. + * @param {AbortController} abortController - Abort controller. + * @param {string} requestType - Type of abort request. + * + * @returns {object} - an action object. + */ +export function createAbortController( abortController, requestType ) { + return { + type: ADD_ABORT_CONTROLLER, + requestType, + abortController, + }; +} + +/** + * Remove abort controllers. + * + * @param {string} requestType - Type of abort request. + * + * @returns {object} - an action object. + */ +export function removeAbortControllers( requestType ) { + return { + type: REMOVE_ABORT_CONTROLLERS, + requestType, + }; +} + +/** + * Abort a request. + * + * @param {string} requestType - Type of abort request. + * + * @returns {Function} - a function to abort a request. + */ +export function abortRequest( requestType ) { + return function ( { dispatch, select } ) { + const abortControllers = select.getAbortControllers( requestType ); + + for ( const controller of abortControllers ) { + controller.abort(); + } + + // Remove the abort controllers. + dispatch( removeAbortControllers( requestType ) ); + }; +} + +/** + * Abort the refresh connections request. + * + * @returns {Function} - a function to abort a request. + */ +export function abortRefreshConnectionsRequest() { + return abortRequest( REQUEST_TYPE_REFRESH_CONNECTIONS ); +} + /** * Effect handler which will refresh the connection test results. * @@ -117,7 +179,20 @@ export function refreshConnectionTestResults( syncToMeta = false ) { try { const path = select.connectionRefreshPath() || '/wpcom/v2/publicize/connection-test-results'; - const freshConnections = await apiFetch( { path } ); + // Wait until all connections are done updating/deleting. + while ( + select.getUpdatingConnections().length > 0 || + select.getDeletingConnections().length > 0 + ) { + await new Promise( resolve => setTimeout( resolve, 100 ) ); + } + + const abortController = new AbortController(); + + dispatch( createAbortController( abortController, REQUEST_TYPE_REFRESH_CONNECTIONS ) ); + + // Pass the abort controller signal to the fetch request. + const freshConnections = await apiFetch( { path, signal: abortController.signal } ); dispatch( mergeConnections( freshConnections ) ); @@ -125,7 +200,11 @@ export function refreshConnectionTestResults( syncToMeta = false ) { dispatch( syncConnectionsToPostMeta() ); } } catch ( e ) { - // Do nothing. + // If the request was aborted. + if ( 'AbortError' === e.name ) { + // Fire it again to run after the current operation that cancelled the request. + dispatch( refreshConnectionTestResults( syncToMeta ) ); + } } }; } @@ -209,6 +288,9 @@ export function deleteConnectionById( { connectionId, showSuccessNotice = true } try { const path = `/jetpack/v4/social/connections/${ connectionId }`; + // Abort the refresh connections request. + dispatch( abortRefreshConnectionsRequest() ); + dispatch( deletingConnection( connectionId ) ); await apiFetch( { method: 'DELETE', path } ); @@ -268,6 +350,9 @@ export function createConnection( data, optimisticData = {} ) { ...optimisticData, } ) ); + // Abort the refresh connections request. + dispatch( abortRefreshConnectionsRequest() ); + // Mark the connection as updating to show the spinner. dispatch( updatingConnection( tempId ) ); @@ -352,6 +437,20 @@ export function updatingConnection( connectionId, updating = true ) { }; } +/** + * Sets the reconnecting account. + * + * @param {string} reconnectingAccount - Account being reconnected. + * + * @returns {object} Reconnecting account action. + */ +export function setReconnectingAccount( reconnectingAccount ) { + return { + type: SET_RECONNECTING_ACCOUNT, + reconnectingAccount, + }; +} + /** * Updates a connection. * @@ -368,6 +467,9 @@ export function updateConnectionById( connectionId, data ) { try { const path = `/jetpack/v4/social/connections/${ connectionId }`; + // Abort the refresh connections request. + dispatch( abortRefreshConnectionsRequest() ); + // Optimistically update the connection. dispatch( updateConnection( connectionId, data ) ); diff --git a/projects/js-packages/publicize-components/src/social-store/actions/constants.ts b/projects/js-packages/publicize-components/src/social-store/actions/constants.ts index d716c35127330..634acf1a23fdd 100644 --- a/projects/js-packages/publicize-components/src/social-store/actions/constants.ts +++ b/projects/js-packages/publicize-components/src/social-store/actions/constants.ts @@ -12,6 +12,16 @@ export const UPDATE_CONNECTION = 'UPDATE_CONNECTION'; export const UPDATING_CONNECTION = 'UPDATING_CONNECTION'; +export const SET_RECONNECTING_ACCOUNT = 'SET_RECONNECTING_ACCOUNT'; + export const SET_KEYRING_RESULT = 'SET_KEYRING_RESULT'; export const TOGGLE_CONNECTIONS_MODAL = 'TOGGLE_CONNECTIONS_MODAL'; + +export const ADD_ABORT_CONTROLLER = 'ADD_ABORT_CONTROLLER'; + +export const REMOVE_ABORT_CONTROLLERS = 'REMOVE_ABORT_CONTROLLERS'; + +export const REQUEST_TYPE_DEFAULT = 'DEFAULT'; + +export const REQUEST_TYPE_REFRESH_CONNECTIONS = 'REFRESH_CONNECTIONS'; diff --git a/projects/js-packages/publicize-components/src/social-store/reducer/connection-data.js b/projects/js-packages/publicize-components/src/social-store/reducer/connection-data.js index 5bba209f1d660..b99683e85605e 100644 --- a/projects/js-packages/publicize-components/src/social-store/reducer/connection-data.js +++ b/projects/js-packages/publicize-components/src/social-store/reducer/connection-data.js @@ -2,12 +2,16 @@ import { ADD_CONNECTION, DELETE_CONNECTION, DELETING_CONNECTION, + SET_RECONNECTING_ACCOUNT, SET_CONNECTIONS, SET_KEYRING_RESULT, TOGGLE_CONNECTION, TOGGLE_CONNECTIONS_MODAL, UPDATE_CONNECTION, UPDATING_CONNECTION, + ADD_ABORT_CONTROLLER, + REMOVE_ABORT_CONTROLLERS, + REQUEST_TYPE_DEFAULT, } from '../actions/constants'; /** @@ -56,6 +60,13 @@ const connectionData = ( state = {}, action ) => { }; } + case SET_RECONNECTING_ACCOUNT: { + return { + ...state, + reconnectingAccount: action.reconnectingAccount, + }; + } + case UPDATE_CONNECTION: return { ...state, @@ -84,6 +95,33 @@ const connectionData = ( state = {}, action ) => { }; } + case ADD_ABORT_CONTROLLER: { + const requestType = action.requestType || REQUEST_TYPE_DEFAULT; + + return { + ...state, + abortControllers: { + ...state.abortControllers, + [ requestType ]: [ + ...( state.abortControllers?.[ requestType ] || [] ), + action.abortController, + ], + }, + }; + } + + case REMOVE_ABORT_CONTROLLERS: { + const requestType = action.requestType || REQUEST_TYPE_DEFAULT; + + return { + ...state, + abortControllers: { + ...state.abortControllers, + [ requestType ]: [], + }, + }; + } + case SET_KEYRING_RESULT: return { ...state, diff --git a/projects/js-packages/publicize-components/src/social-store/selectors/connection-data.js b/projects/js-packages/publicize-components/src/social-store/selectors/connection-data.js index c27e5e0355734..a9427c8333c78 100644 --- a/projects/js-packages/publicize-components/src/social-store/selectors/connection-data.js +++ b/projects/js-packages/publicize-components/src/social-store/selectors/connection-data.js @@ -1,3 +1,5 @@ +import { REQUEST_TYPE_DEFAULT } from '../actions/constants'; + /** * Returns the connections list from the store. * @@ -151,6 +153,28 @@ export function getUpdatingConnections( state ) { return state.connectionData?.updatingConnections ?? []; } +/** + * Get the account being reconnected + * + * @param {import("../types").SocialStoreState} state - State object. + * @returns {import("../types").ConnectionData['reconnectingAccount']} The account being reconnected. + */ +export function getReconnectingAccount( state ) { + return state.connectionData?.reconnectingAccount ?? ''; +} + +/** + * Get the abort controllers for a specific request type. + * + * @param {import("../types").SocialStoreState} state - State object. + * @param {string} requestType - The request type. + * + * @returns {Array} The abort controllers. + */ +export function getAbortControllers( state, requestType = REQUEST_TYPE_DEFAULT ) { + return state.connectionData?.abortControllers?.[ requestType ] ?? []; +} + /** * Whether a mastodon account is already connected. * diff --git a/projects/js-packages/publicize-components/src/social-store/types.ts b/projects/js-packages/publicize-components/src/social-store/types.ts index 231050799521a..88344bb91c09b 100644 --- a/projects/js-packages/publicize-components/src/social-store/types.ts +++ b/projects/js-packages/publicize-components/src/social-store/types.ts @@ -43,6 +43,7 @@ export type ConnectionData = { connections: Connection[]; deletingConnections?: Array< number | string >; updatingConnections?: Array< number | string >; + reconnectingAccount?: string; keyringResult?: KeyringResult; }; diff --git a/projects/js-packages/shared-extension-utils/CHANGELOG.md b/projects/js-packages/shared-extension-utils/CHANGELOG.md index 9e65688c739ff..f61bff08b5004 100644 --- a/projects/js-packages/shared-extension-utils/CHANGELOG.md +++ b/projects/js-packages/shared-extension-utils/CHANGELOG.md @@ -5,6 +5,22 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.14.20] - 2024-07-23 +### Fixed +- Updated package dependencies. [#38464] + +## [0.14.19] - 2024-07-22 +### Changed +- Update dependencies. [#37356] + +## [0.14.18] - 2024-07-18 +### Changed +- Update dependencies. [#37356] + +## [0.14.17] - 2024-07-03 +### Changed +- Updated package dependencies. [#38132] + ## [0.14.16] - 2024-06-21 ### Changed - Update dependencies. [#37356] @@ -396,6 +412,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Core: prepare utility for release +[0.14.20]: https://github.com/Automattic/jetpack-shared-extension-utils/compare/0.14.19...0.14.20 +[0.14.19]: https://github.com/Automattic/jetpack-shared-extension-utils/compare/0.14.18...0.14.19 +[0.14.18]: https://github.com/Automattic/jetpack-shared-extension-utils/compare/0.14.17...0.14.18 +[0.14.17]: https://github.com/Automattic/jetpack-shared-extension-utils/compare/0.14.16...0.14.17 [0.14.16]: https://github.com/Automattic/jetpack-shared-extension-utils/compare/0.14.15...0.14.16 [0.14.15]: https://github.com/Automattic/jetpack-shared-extension-utils/compare/0.14.14...0.14.15 [0.14.14]: https://github.com/Automattic/jetpack-shared-extension-utils/compare/0.14.13...0.14.14 diff --git a/projects/js-packages/shared-extension-utils/package.json b/projects/js-packages/shared-extension-utils/package.json index 2394f4c03fe8b..6eddb169f3ef1 100644 --- a/projects/js-packages/shared-extension-utils/package.json +++ b/projects/js-packages/shared-extension-utils/package.json @@ -1,6 +1,6 @@ { "name": "@automattic/jetpack-shared-extension-utils", - "version": "0.14.17-alpha", + "version": "0.14.20", "description": "Utility functions used by the block editor extensions", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/shared-extension-utils/#readme", "bugs": { diff --git a/projects/js-packages/boost-score-api/changelog/renovate-wordpress-monorepo b/projects/js-packages/storybook/changelog/renovate-playwright-monorepo#3 similarity index 100% rename from projects/js-packages/boost-score-api/changelog/renovate-wordpress-monorepo rename to projects/js-packages/storybook/changelog/renovate-playwright-monorepo#3 diff --git a/projects/js-packages/storybook/package.json b/projects/js-packages/storybook/package.json index 33a213da65a46..f2e1e49c06b80 100644 --- a/projects/js-packages/storybook/package.json +++ b/projects/js-packages/storybook/package.json @@ -25,7 +25,7 @@ "@babel/plugin-syntax-jsx": "7.24.7", "@babel/preset-react": "7.24.7", "@babel/runtime": "7.24.7", - "@playwright/test": "1.44.1", + "@playwright/test": "1.45.1", "@storybook/addon-a11y": "8.1.6", "@storybook/addon-docs": "8.1.6", "@storybook/addon-essentials": "8.1.6", diff --git a/projects/js-packages/components/changelog/renovate-wordpress-monorepo b/projects/js-packages/svelte-data-sync-client/changelog/renovate-svelte-4.x similarity index 100% rename from projects/js-packages/components/changelog/renovate-wordpress-monorepo rename to projects/js-packages/svelte-data-sync-client/changelog/renovate-svelte-4.x diff --git a/projects/js-packages/svelte-data-sync-client/package.json b/projects/js-packages/svelte-data-sync-client/package.json index e8679d28cd08f..2aed4f1ebb874 100644 --- a/projects/js-packages/svelte-data-sync-client/package.json +++ b/projects/js-packages/svelte-data-sync-client/package.json @@ -28,7 +28,7 @@ "@typescript-eslint/parser": "6.21.0", "eslint": "8.57.0", "jest": "29.7.0", - "svelte": "3.58.0", + "svelte": "4.2.18", "tslib": "2.5.0", "typescript": "5.0.4", "webpack": "5.76.0", diff --git a/projects/js-packages/webpack-config/CHANGELOG.md b/projects/js-packages/webpack-config/CHANGELOG.md index 44f1af03bf6a2..74a905137f5b8 100644 --- a/projects/js-packages/webpack-config/CHANGELOG.md +++ b/projects/js-packages/webpack-config/CHANGELOG.md @@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 3.2.11 - 2024-07-24 +### Added +- Pass default Babel options to I18nCheckWebpackPlugin if none are supplied, as we already do for TranspileRule. [#38482] + +## 3.2.10 - 2024-07-03 +### Changed +- Updated package dependencies. [#38132] + ## 3.2.9 - 2024-06-13 ### Changed - Updated package dependencies. [#37822] diff --git a/projects/js-packages/webpack-config/README.md b/projects/js-packages/webpack-config/README.md index e79a55911ea14..16ed22829993b 100644 --- a/projects/js-packages/webpack-config/README.md +++ b/projects/js-packages/webpack-config/README.md @@ -184,6 +184,8 @@ This provides an instance of [@wordpress/i18n-check-webpack-plugin](https://www. The default configuration sets a filter that excludes `node_modules` other than `@automattic/*`. This may be accessed as `I18nCheckPlugin.defaultFilter`. +The default configuration also sets `extractorOptions.babelOptions`: If `path.resolve( 'babel.config.js' )` exists, `configFile` will default to that. Otherwise, `presets` will default to set some appropriate defaults (which will require the peer dependencies on [@babel/core](https://www.npmjs.com/package/@babel/core) and [@babel/runtime](https://www.npmjs.com/package/@babel/runtime)). + ##### `I18nLoaderPlugin( options )` This provides an instance of [@automattic/i18n-loader-webpack-plugin](https://www.npmjs.com/package/@automattic/i18n-loader-webpack-plugin). The `options` are passed to the plugin. diff --git a/projects/js-packages/webpack-config/package.json b/projects/js-packages/webpack-config/package.json index d4a6904434702..a2dda6f8efa4f 100644 --- a/projects/js-packages/webpack-config/package.json +++ b/projects/js-packages/webpack-config/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@automattic/jetpack-webpack-config", - "version": "3.2.10-alpha", + "version": "3.2.11", "description": "Library of pieces for webpack config in Jetpack projects.", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/webpack-config/#readme", "bugs": { diff --git a/projects/js-packages/webpack-config/src/webpack.js b/projects/js-packages/webpack-config/src/webpack.js index caa64d98f278f..90f9993d6e594 100644 --- a/projects/js-packages/webpack-config/src/webpack.js +++ b/projects/js-packages/webpack-config/src/webpack.js @@ -128,9 +128,26 @@ const ForkTSCheckerPlugin = options => [ const I18nCheckPlugin = options => { const opts = { filter: i18nFilterFunction, ...options }; + + // Default text domain. if ( typeof opts.expectDomain === 'undefined' ) { opts.expectDomain = loadTextDomainFromComposerJson(); } + + // Default Babel options for extractor. + if ( typeof opts.extractorOptions?.babelOptions === 'undefined' ) { + const babelOptions = { babelrc: false }; + const configFile = path.resolve( 'babel.config.js' ); + if ( fs.existsSync( configFile ) ) { + babelOptions.configFile = configFile; + } else { + babelOptions.presets = [ require.resolve( './babel-preset.js' ) ]; + } + + opts.extractorOptions ??= {}; + opts.extractorOptions.babelOptions = babelOptions; + } + return [ new I18nCheckWebpackPlugin( opts ) ]; }; I18nCheckPlugin.defaultFilter = i18nFilterFunction; diff --git a/projects/packages/README.md b/projects/packages/README.md index da02590751acd..d5cb63d4742ab 100644 --- a/projects/packages/README.md +++ b/projects/packages/README.md @@ -41,7 +41,7 @@ and that will install the required Composer packages. ### Using packages -Before using a package within another project, you will need to require the package in the project's `composer.json` file. Then, run `tools/fixup-project-versions.sh` to update the `composer.lock` file in the proect, and reinstall the project with the `jetpack install` command. +Before using a package within another project, you will need to require the package in the project's `composer.json` file. Then, run `tools/fixup-project-versions.sh` to update the `composer.lock` file in the project, and reinstall the project with the `jetpack install` command. As an example, if you want to require the Logo package in the Jetpack plugin, add `"automattic/jetpack-logo": "@dev",` underneath `require` in the Jetpack plugin's `composer.json` file to require it. After updating the project versions run `jetpack install plugins/jetpack` to pull the new package. diff --git a/projects/packages/assets/CHANGELOG.md b/projects/packages/assets/CHANGELOG.md index 1ae8fa1944160..1257c9a7d2f25 100644 --- a/projects/packages/assets/CHANGELOG.md +++ b/projects/packages/assets/CHANGELOG.md @@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2.2.0] - 2024-07-23 +### Added +- Assets: Add JSX runtime polyfill `react-jsx-runtime` for WordPress < 6.6. [#38428] + +## [2.1.13] - 2024-07-03 +### Changed +- Updated package dependencies. [#38132] + ## [2.1.12] - 2024-06-05 ### Changed - Updated package dependencies. [#37669] @@ -454,6 +462,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Statically access asset tools +[2.2.0]: https://github.com/Automattic/jetpack-assets/compare/v2.1.13...v2.2.0 +[2.1.13]: https://github.com/Automattic/jetpack-assets/compare/v2.1.12...v2.1.13 [2.1.12]: https://github.com/Automattic/jetpack-assets/compare/v2.1.11...v2.1.12 [2.1.11]: https://github.com/Automattic/jetpack-assets/compare/v2.1.10...v2.1.11 [2.1.10]: https://github.com/Automattic/jetpack-assets/compare/v2.1.9...v2.1.10 diff --git a/projects/packages/assets/composer.json b/projects/packages/assets/composer.json index eb0547bfe1cd3..57987fec7a97a 100644 --- a/projects/packages/assets/composer.json +++ b/projects/packages/assets/composer.json @@ -60,7 +60,7 @@ "link-template": "https://github.com/Automattic/jetpack-assets/compare/v${old}...v${new}" }, "branch-alias": { - "dev-trunk": "2.1.x-dev" + "dev-trunk": "2.2.x-dev" } } } diff --git a/projects/packages/assets/package.json b/projects/packages/assets/package.json index 71fc967d70e21..54c5a27706d89 100644 --- a/projects/packages/assets/package.json +++ b/projects/packages/assets/package.json @@ -10,6 +10,9 @@ "test": "jest tests", "validate": "pnpm exec validate-es build/" }, + "dependencies": { + "react": "18.3.1" + }, "devDependencies": { "@automattic/jetpack-webpack-config": "workspace:*", "@wordpress/browserslist-config": "6.2.0", diff --git a/projects/packages/assets/src/class-assets.php b/projects/packages/assets/src/class-assets.php index dab45354ee26e..d06fa6a144816 100644 --- a/projects/packages/assets/src/class-assets.php +++ b/projects/packages/assets/src/class-assets.php @@ -529,6 +529,11 @@ public static function wp_default_scripts_hook( $wp_scripts ) { $wp_scripts->add( 'wp-jp-i18n-state', false, array( 'wp-deprecated', 'wp-jp-i18n-loader' ) ); $wp_scripts->add_inline_script( 'wp-jp-i18n-state', 'wp.deprecated( "wp-jp-i18n-state", { alternative: "wp-jp-i18n-loader" } );' ); $wp_scripts->add_inline_script( 'wp-jp-i18n-state', 'wp.jpI18nState = wp.jpI18nLoader.state;' ); + + // Register the React JSX runtime script - used as a polyfill until we can update JSX transforms. See https://github.com/Automattic/jetpack/issues/38424. + // @todo Remove this when we drop support for WordPress 6.5, as well as the script inclusion in test_wp_default_scripts_hook. + $jsx_url = self::normalize_path( plugins_url( '../build/react-jsx-runtime.js', __FILE__ ) ); + $wp_scripts->add( 'react-jsx-runtime', $jsx_url, array( 'react' ), '18.3.1', true ); } // endregion . diff --git a/projects/packages/assets/tests/php/test-assets.php b/projects/packages/assets/tests/php/test-assets.php index 09255c174a3ab..c3a53b0660fa0 100644 --- a/projects/packages/assets/tests/php/test-assets.php +++ b/projects/packages/assets/tests/php/test-assets.php @@ -757,7 +757,8 @@ function ( $value ) use ( $value_sets, $i ) { return $funcs; }; - $mock->expects( $this->exactly( 2 ) )->method( 'add' ) + // @todo: Remove `react-jsx-runtime` from the list of dependencies once we drop support for WordPress 6.5 and remove the dependency from wp_default_scripts_hook. + $mock->expects( $this->exactly( 3 ) )->method( 'add' ) ->with( ...$with_consecutive( array( @@ -768,7 +769,12 @@ function ( $value ) use ( $value_sets, $i ) { ), array( 'wp-i18n' ), ), - array( 'wp-jp-i18n-state', false, array( 'wp-deprecated', 'wp-jp-i18n-loader' ) ) + array( 'wp-jp-i18n-state', false, array( 'wp-deprecated', 'wp-jp-i18n-loader' ) ), + array( + 'react-jsx-runtime', + 'http://www.example.com/wp-content/plugins/jetpack/packages/assets/build/react-jsx-runtime.js', + array( 'react' ), + ) ) ); $mock->expects( $this->exactly( 3 ) )->method( 'add_inline_script' ) diff --git a/projects/packages/assets/webpack.config.js b/projects/packages/assets/webpack.config.js index df39af1d2683b..541c5afc44a06 100644 --- a/projects/packages/assets/webpack.config.js +++ b/projects/packages/assets/webpack.config.js @@ -34,4 +34,23 @@ module.exports = [ ], }, }, + { + entry: { + 'react-jsx-runtime': { + import: 'react/jsx-runtime', + }, + }, + output: { + ...jetpackWebpackConfig.output, + path: path.resolve( './build' ), + filename: 'react-jsx-runtime.js', + library: { + name: 'ReactJSXRuntime', + type: 'window', + }, + }, + externals: { + react: 'React', + }, + }, ]; diff --git a/projects/packages/autoloader/CHANGELOG.md b/projects/packages/autoloader/CHANGELOG.md index 3d579e6761679..b253cfe24e48a 100644 --- a/projects/packages/autoloader/CHANGELOG.md +++ b/projects/packages/autoloader/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [3.0.9] - 2024-07-10 +### Fixed +- Avoid a deprecation notice in `Autoloader_Locator::find_latest_autoloader()`. [#38245] + ## [3.0.8] - 2024-05-29 ### Fixed - `AutoloadGenerator::__construct` no longer pretends `$io` is nullable. That never worked. [#37608] @@ -364,6 +368,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add Custom Autoloader +[3.0.9]: https://github.com/Automattic/jetpack-autoloader/compare/v3.0.8...v3.0.9 [3.0.8]: https://github.com/Automattic/jetpack-autoloader/compare/v3.0.7...v3.0.8 [3.0.7]: https://github.com/Automattic/jetpack-autoloader/compare/v3.0.6...v3.0.7 [3.0.6]: https://github.com/Automattic/jetpack-autoloader/compare/v3.0.5...v3.0.6 diff --git a/projects/packages/autoloader/src/AutoloadGenerator.php b/projects/packages/autoloader/src/AutoloadGenerator.php index 594ebb59faec4..2e9a311cb3163 100644 --- a/projects/packages/autoloader/src/AutoloadGenerator.php +++ b/projects/packages/autoloader/src/AutoloadGenerator.php @@ -21,7 +21,7 @@ */ class AutoloadGenerator { - const VERSION = '3.0.8'; + const VERSION = '3.0.9'; /** * IO object. diff --git a/projects/packages/autoloader/src/class-autoloader-locator.php b/projects/packages/autoloader/src/class-autoloader-locator.php index 7c05e88a693ef..908222601c81d 100644 --- a/projects/packages/autoloader/src/class-autoloader-locator.php +++ b/projects/packages/autoloader/src/class-autoloader-locator.php @@ -37,7 +37,7 @@ public function find_latest_autoloader( $plugin_paths, &$latest_version ) { foreach ( $plugin_paths as $plugin_path ) { $version = $this->get_autoloader_version( $plugin_path ); - if ( ! $this->version_selector->is_version_update_required( $latest_version, $version ) ) { + if ( ! $version || ! $this->version_selector->is_version_update_required( $latest_version, $version ) ) { continue; } diff --git a/projects/packages/autoloader/tests/php/tests/unit/AutoloaderLocatorTest.php b/projects/packages/autoloader/tests/php/tests/unit/AutoloaderLocatorTest.php index 823e603f01154..841d7326c799d 100644 --- a/projects/packages/autoloader/tests/php/tests/unit/AutoloaderLocatorTest.php +++ b/projects/packages/autoloader/tests/php/tests/unit/AutoloaderLocatorTest.php @@ -63,19 +63,29 @@ public function test_finds_latest_autoloader() { $this->assertNull( $latest ); $this->assertNull( $latest_version ); - $latest = $this->autoloader_locator->find_latest_autoloader( + $latest_version = null; + $latest = $this->autoloader_locator->find_latest_autoloader( array( self::$older_plugin_dir ), $latest_version ); $this->assertEquals( self::$older_plugin_dir, $latest ); $this->assertEquals( self::OLDER_VERSION, $latest_version ); - $latest = $this->autoloader_locator->find_latest_autoloader( + $latest_version = null; + $latest = $this->autoloader_locator->find_latest_autoloader( array( TEST_PLUGIN_DIR, self::$older_plugin_dir ), $latest_version ); $this->assertEquals( TEST_PLUGIN_DIR, $latest ); $this->assertEquals( Test_Plugin_Factory::VERSION_CURRENT, $latest_version ); + + $latest_version = null; + $latest = $this->autoloader_locator->find_latest_autoloader( + array( TEST_PLUGIN_DIR, WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . 'this-does-not-exist' ), + $latest_version + ); + $this->assertEquals( TEST_PLUGIN_DIR, $latest ); + $this->assertEquals( Test_Plugin_Factory::VERSION_CURRENT, $latest_version ); } /** diff --git a/projects/packages/backup/CHANGELOG.md b/projects/packages/backup/CHANGELOG.md index 5f4181c0b0db0..8329af3a4654c 100644 --- a/projects/packages/backup/CHANGELOG.md +++ b/projects/packages/backup/CHANGELOG.md @@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [3.4.2] - 2024-07-22 +### Changed +- Update dependencies. [#38402] + +## [3.4.1] - 2024-07-08 +### Changed +- Updated package dependencies. [#38132] + ## [3.4.0] - 2024-06-27 ### Added - Add on-demand backups feature in the backup package [#37998] @@ -652,6 +660,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add API endpoints and Jetpack Backup package for managing Help… +[3.4.2]: https://github.com/Automattic/jetpack-backup/compare/v3.4.1...v3.4.2 +[3.4.1]: https://github.com/Automattic/jetpack-backup/compare/v3.4.0...v3.4.1 [3.4.0]: https://github.com/Automattic/jetpack-backup/compare/v3.3.17...v3.4.0 [3.3.17]: https://github.com/Automattic/jetpack-backup/compare/v3.3.16...v3.3.17 [3.3.16]: https://github.com/Automattic/jetpack-backup/compare/v3.3.15...v3.3.16 diff --git a/projects/packages/backup/src/class-package-version.php b/projects/packages/backup/src/class-package-version.php index b4eda6ebef08b..0f7bc2a6cca40 100644 --- a/projects/packages/backup/src/class-package-version.php +++ b/projects/packages/backup/src/class-package-version.php @@ -16,7 +16,7 @@ */ class Package_Version { - const PACKAGE_VERSION = '3.4.1-alpha'; + const PACKAGE_VERSION = '3.4.2'; const PACKAGE_SLUG = 'backup'; diff --git a/projects/packages/blaze/CHANGELOG.md b/projects/packages/blaze/CHANGELOG.md index f981e823ebeb5..056a09a5cc001 100644 --- a/projects/packages/blaze/CHANGELOG.md +++ b/projects/packages/blaze/CHANGELOG.md @@ -5,6 +5,22 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.22.2] - 2024-07-23 +### Fixed +- Updated package dependencies. [#38464] + +## [0.22.1] - 2024-07-22 +### Changed +- Update dependencies. [#37356] + +## [0.22.0] - 2024-07-08 +### Changed +- As we've launched untangling & nav redesign, the wpcom_is_nav_redesign_enabled() function name is not relevant anymore and can be confusing for future developers, so we replace it with the equivalent get_option call. [#38197] +- Updated package dependencies. [#38132] + +### Fixed +- Fixes a bug in the Blaze endpoint blaze/posts that happens when we get a non-200 from WPCOM [#38070] + ## [0.21.10] - 2024-06-28 ### Changed - Eligibility checks: when a request to the WordPress.com API fails, store the response for an hour to avoid spamming the API. [#38066] @@ -396,6 +412,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Updated package dependencies. [#27906] +[0.22.2]: https://github.com/automattic/jetpack-blaze/compare/v0.22.1...v0.22.2 +[0.22.1]: https://github.com/automattic/jetpack-blaze/compare/v0.22.0...v0.22.1 +[0.22.0]: https://github.com/automattic/jetpack-blaze/compare/v0.21.10...v0.22.0 [0.21.10]: https://github.com/automattic/jetpack-blaze/compare/v0.21.9...v0.21.10 [0.21.9]: https://github.com/automattic/jetpack-blaze/compare/v0.21.8...v0.21.9 [0.21.8]: https://github.com/automattic/jetpack-blaze/compare/v0.21.7...v0.21.8 diff --git a/projects/packages/blaze/changelog/fix-blaze-posts-error-handling b/projects/packages/blaze/changelog/fix-blaze-posts-error-handling deleted file mode 100644 index ed180f8173fa4..0000000000000 --- a/projects/packages/blaze/changelog/fix-blaze-posts-error-handling +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fixed - -Fixes a bug in the Blaze endpoint blaze/posts that happens when we get a non-200 from WPCOM diff --git a/projects/packages/blaze/composer.json b/projects/packages/blaze/composer.json index 281ecab1ecc07..7701f03b155e4 100644 --- a/projects/packages/blaze/composer.json +++ b/projects/packages/blaze/composer.json @@ -64,7 +64,7 @@ "link-template": "https://github.com/automattic/jetpack-blaze/compare/v${old}...v${new}" }, "branch-alias": { - "dev-trunk": "0.21.x-dev" + "dev-trunk": "0.22.x-dev" }, "textdomain": "jetpack-blaze", "version-constants": { diff --git a/projects/packages/blaze/package.json b/projects/packages/blaze/package.json index 31f0cda2a2e3f..c6892980b3a3f 100644 --- a/projects/packages/blaze/package.json +++ b/projects/packages/blaze/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@automattic/jetpack-blaze", - "version": "0.21.11-alpha", + "version": "0.22.2", "description": "Attract high-quality traffic to your site using Blaze. Using this service, you can advertise a post or page on some of the millions of pages across WordPress.com and Tumblr from just $5 per day.", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/packages/blaze/#readme", "bugs": { diff --git a/projects/packages/blaze/src/class-blaze.php b/projects/packages/blaze/src/class-blaze.php index d19dc84492a9a..c81836fd62e09 100644 --- a/projects/packages/blaze/src/class-blaze.php +++ b/projects/packages/blaze/src/class-blaze.php @@ -74,11 +74,10 @@ public static function add_post_links_actions() { * @return bool */ public static function is_dashboard_enabled() { - $is_dashboard_enabled = true; - $wpcom_is_nav_redesign_enabled = function_exists( 'wpcom_is_nav_redesign_enabled' ) && wpcom_is_nav_redesign_enabled(); + $is_dashboard_enabled = true; // On WordPress.com sites, the dashboard is not needed if the nav redesign is not enabled. - if ( ! $wpcom_is_nav_redesign_enabled && ( new Host() )->is_wpcom_platform() ) { + if ( get_option( 'wpcom_admin_interface' ) !== 'wp-admin' && ( new Host() )->is_wpcom_platform() ) { $is_dashboard_enabled = false; } diff --git a/projects/packages/blaze/src/class-dashboard.php b/projects/packages/blaze/src/class-dashboard.php index cf716ae9893a7..f9918e43297ad 100644 --- a/projects/packages/blaze/src/class-dashboard.php +++ b/projects/packages/blaze/src/class-dashboard.php @@ -21,7 +21,7 @@ class Dashboard { * * @var string */ - const PACKAGE_VERSION = '0.21.11-alpha'; + const PACKAGE_VERSION = '0.22.2'; /** * List of dependencies needed to render the dashboard in wp-admin. diff --git a/projects/packages/calypsoify/CHANGELOG.md b/projects/packages/calypsoify/CHANGELOG.md index b9a77dc082689..069ec60176425 100644 --- a/projects/packages/calypsoify/CHANGELOG.md +++ b/projects/packages/calypsoify/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.1.2] - 2024-07-08 +### Changed +- Updated package dependencies. [#38132] + ## [0.1.1] - 2024-06-06 ### Added - Calypsoify: Deprecating functions no longer in use [#37453] @@ -21,4 +25,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Calypsoify: Load feature from the Calypsoify package. [#37375] - Updated package dependencies. [#37379] +[0.1.2]: https://github.com/Automattic/jetpack-calypsoify/compare/v0.1.1...v0.1.2 [0.1.1]: https://github.com/Automattic/jetpack-calypsoify/compare/v0.1.0...v0.1.1 diff --git a/projects/packages/calypsoify/package.json b/projects/packages/calypsoify/package.json index 270a36ee27d55..6e2f05b7c75bf 100644 --- a/projects/packages/calypsoify/package.json +++ b/projects/packages/calypsoify/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@automattic/jetpack-calypsoify", - "version": "0.1.2-alpha", + "version": "0.1.2", "description": "Calypsoify is designed to make sure specific wp-admin pages include navigation that prioritizes the Calypso navigation experience.", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/packages/calypsoify/#readme", "bugs": { diff --git a/projects/packages/calypsoify/src/class-jetpack-calypsoify.php b/projects/packages/calypsoify/src/class-jetpack-calypsoify.php index 56b0af17043a6..6434492769927 100644 --- a/projects/packages/calypsoify/src/class-jetpack-calypsoify.php +++ b/projects/packages/calypsoify/src/class-jetpack-calypsoify.php @@ -15,7 +15,7 @@ */ class Jetpack_Calypsoify { - const PACKAGE_VERSION = '0.1.2-alpha'; + const PACKAGE_VERSION = '0.1.2'; /** * Singleton instance of `Jetpack_Calypsoify`. diff --git a/projects/packages/classic-theme-helper/.phan/baseline.php b/projects/packages/classic-theme-helper/.phan/baseline.php index 889311320576f..0ddcfaf434b6c 100644 --- a/projects/packages/classic-theme-helper/.phan/baseline.php +++ b/projects/packages/classic-theme-helper/.phan/baseline.php @@ -10,19 +10,17 @@ return [ // # Issue statistics: // PhanTypeMismatchArgumentInternal : 10+ occurrences - // PhanUndeclaredClassMethod : 5 occurrences // PhanTypeInvalidDimOffset : 2 occurrences // PhanTypeMismatchArgument : 2 occurrences - // PhanNonClassMethodCall : 1 occurrence // PhanTypeComparisonToArray : 1 occurrence // PhanTypeMismatchArgumentProbablyReal : 1 occurrence // PhanTypeMismatchProperty : 1 occurrence - // PhanTypeMismatchPropertyProbablyReal : 1 occurrence // PhanTypePossiblyInvalidDimOffset : 1 occurrence + // PhanUndeclaredFunction : 1 occurrence // Currently, file_suppressions and directory_suppressions are the only supported suppressions 'file_suppressions' => [ - '_inc/lib/tonesque.php' => ['PhanNonClassMethodCall', 'PhanTypeMismatchArgument', 'PhanTypeMismatchArgumentInternal', 'PhanTypeMismatchArgumentProbablyReal', 'PhanTypeMismatchPropertyProbablyReal', 'PhanUndeclaredClassMethod'], + '_inc/lib/tonesque.php' => ['PhanTypeMismatchArgument', 'PhanTypeMismatchArgumentInternal', 'PhanTypeMismatchArgumentProbablyReal'], 'src/class-featured-content.php' => ['PhanTypeComparisonToArray', 'PhanTypeInvalidDimOffset', 'PhanTypeMismatchArgument', 'PhanTypeMismatchProperty', 'PhanTypePossiblyInvalidDimOffset'], ], // 'directory_suppressions' => ['src/directory_name' => ['PhanIssueName1', 'PhanIssueName2']] can be manually added if needed. diff --git a/projects/packages/classic-theme-helper/CHANGELOG.md b/projects/packages/classic-theme-helper/CHANGELOG.md index 6e0dd1b0f4d48..479b9022b0a51 100644 --- a/projects/packages/classic-theme-helper/CHANGELOG.md +++ b/projects/packages/classic-theme-helper/CHANGELOG.md @@ -5,6 +5,23 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.4.2] - 2024-07-22 +### Added +- Added Jetpack_Color class. [#38357] + +## [0.4.1] - 2024-07-15 +### Added +- Theme Tools: Adding Social Menu to Classic Theme Helper package [#38243] + +## [0.4.0] - 2024-07-08 +### Changed +- Classic Theme Helper - Featured Content: Moved check for plugins page to init since setup is used now externally [#38215] +- Classic Theme Helper - Requiring Responsive Videos and Featured Content files. [#37969] +- Updated package dependencies. [#38132] + +### Removed +- Classic Theme Helper: Remove wpcom only code for featured content [#38154] + ## [0.3.1] - 2024-06-13 ### Changed - Updated package dependencies. [#37796] @@ -33,6 +50,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Add wordpress folder on gitignore. [#37177] +[0.4.2]: https://github.com/Automattic/jetpack-classic-theme-helper/compare/v0.4.1...v0.4.2 +[0.4.1]: https://github.com/Automattic/jetpack-classic-theme-helper/compare/v0.4.0...v0.4.1 +[0.4.0]: https://github.com/Automattic/jetpack-classic-theme-helper/compare/v0.3.1...v0.4.0 [0.3.1]: https://github.com/Automattic/jetpack-classic-theme-helper/compare/v0.3.0...v0.3.1 [0.3.0]: https://github.com/Automattic/jetpack-classic-theme-helper/compare/v0.2.1...v0.3.0 [0.2.1]: https://github.com/Automattic/jetpack-classic-theme-helper/compare/v0.2.0...v0.2.1 diff --git a/projects/packages/classic-theme-helper/_inc/lib/class.color.php b/projects/packages/classic-theme-helper/_inc/lib/class.color.php new file mode 100644 index 0000000000000..7f162c609cecf --- /dev/null +++ b/projects/packages/classic-theme-helper/_inc/lib/class.color.php @@ -0,0 +1,911 @@ + + * @author Matt Wiebe + * @license https://www.opensource.org/licenses/MIT + * + * @package automattic/jetpack + */ + +// phpcs:disable WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid + +if ( ! class_exists( 'Jetpack_Color' ) ) { + /** + * Color utilities + */ + class Jetpack_Color { + /** + * Color code (later array or string, depending on type) + * + * @var int|array|string + */ + protected $color = 0; + + /** + * Initialize object + * + * @param string|array $color A color of the type $type. + * @param string $type The type of color we will construct from. + * One of hex (default), rgb, hsl, int. + */ + public function __construct( $color = null, $type = 'hex' ) { + if ( $color ) { + switch ( $type ) { + case 'hex': + $this->fromHex( $color ); + break; + case 'rgb': + if ( is_array( $color ) && count( $color ) === 3 ) { + list( $r, $g, $b ) = array_values( $color ); + $this->fromRgbInt( $r, $g, $b ); + } + break; + case 'hsl': + if ( is_array( $color ) && count( $color ) === 3 ) { + list( $h, $s, $l ) = array_values( $color ); + $this->fromHsl( $h, $s, $l ); + } + break; + case 'int': + // @phan-suppress-next-line PhanTypeMismatchArgument + $this->fromInt( $color ); + break; + default: + // there is no default. + break; + } + } + } + + /** + * Init color from hex value + * + * @param string $hex_value Color hex value. + * + * @return $this + * @throws RangeException Invalid color code range error. + */ + public function fromHex( $hex_value ) { + $hex_value = str_replace( '#', '', $hex_value ); + // handle short hex codes like #fff. + if ( 3 === strlen( $hex_value ) ) { + $hex_value = $hex_value[0] . $hex_value[0] . $hex_value[1] . $hex_value[1] . $hex_value[2] . $hex_value[2]; + } + return $this->fromInt( hexdec( $hex_value ) ); + } + + /** + * Init color from integer RGB values + * + * @param int $red Red color code. + * @param int $green Green color code. + * @param int $blue Blue color code. + * + * @return $this + * @throws RangeException Invalid color code range error. + */ + public function fromRgbInt( $red, $green, $blue ) { + if ( $red < 0 || $red > 255 ) { + throw new RangeException( 'Red value ' . $red . ' out of valid color code range' ); + } + + if ( $green < 0 || $green > 255 ) { + throw new RangeException( 'Green value ' . $green . ' out of valid color code range' ); + } + + if ( $blue < 0 || $blue > 255 ) { + throw new RangeException( 'Blue value ' . $blue . ' out of valid color code range' ); + } + + $this->color = ( intval( $red ) << 16 ) + ( intval( $green ) << 8 ) + intval( $blue ); + + return $this; + } + + /** + * Init color from hex RGB values + * + * @param string $red Red color code. + * @param string $green Green color code. + * @param string $blue Blue color code. + * + * @return $this + */ + public function fromRgbHex( $red, $green, $blue ) { + return $this->fromRgbInt( hexdec( $red ), hexdec( $green ), hexdec( $blue ) ); + } + + /** + * Converts an HSL color value to RGB. Conversion formula + * adapted from https://en.wikipedia.org/wiki/HSL_color_space. + * + * @param int $h Hue. [0-360]. + * @param int $s Saturation [0, 100]. + * @param int $l Lightness [0, 100]. + */ + public function fromHsl( $h, $s, $l ) { + $h /= 360; + $s /= 100; + $l /= 100; + + if ( 0 === $s ) { + // achromatic. + $r = $l; + $g = $l; + $b = $l; + } else { + $q = $l < 0.5 ? $l * ( 1 + $s ) : $l + $s - $l * $s; + $p = 2 * $l - $q; + $r = $this->hue2rgb( $p, $q, $h + 1 / 3 ); + $g = $this->hue2rgb( $p, $q, $h ); + $b = $this->hue2rgb( $p, $q, $h - 1 / 3 ); + } + + return $this->fromRgbInt( $r * 255, $g * 255, $b * 255 ); + } + + /** + * Helper function for Jetpack_Color::fromHsl() + * + * @param float $p Minimum of R/G/B [0, 1]. + * @param float $q Maximum of R/G/B [0, 1]. + * @param float $t Adjusted hue [0, 1]. + */ + private function hue2rgb( $p, $q, $t ) { + if ( $t < 0 ) { + ++$t; + } + if ( $t > 1 ) { + --$t; + } + if ( $t < 1 / 6 ) { + return $p + ( $q - $p ) * 6 * $t; + } + if ( $t < 1 / 2 ) { + return $q; + } + if ( $t < 2 / 3 ) { + return $p + ( $q - $p ) * ( 2 / 3 - $t ) * 6; + } + return $p; + } + + /** + * Init color from integer value + * + * @param int $int_value Color code. + * + * @return $this + * @throws RangeException Invalid color code range error. + */ + public function fromInt( $int_value ) { + if ( $int_value < 0 || $int_value > 16777215 ) { + throw new RangeException( $int_value . ' out of valid color code range' ); + } + + $this->color = $int_value; + + return $this; + } + + /** + * Convert color to hex + * + * @return string + */ + public function toHex() { + return sprintf( '%06x', $this->color ); + } + + /** + * Convert color to RGB array (integer values) + * + * @return array + */ + public function toRgbInt() { + return array( + 'red' => (int) ( 255 & ( $this->color >> 16 ) ), + 'green' => (int) ( 255 & ( $this->color >> 8 ) ), + 'blue' => (int) ( 255 & ( $this->color ) ), + ); + } + + /** + * Convert color to RGB array (hex values) + * + * @return array + */ + public function toRgbHex() { + $r = array(); + foreach ( $this->toRgbInt() as $item ) { + $r[] = dechex( $item ); + } + return $r; + } + + /** + * Get Hue/Saturation/Value for the current color + * (float values, slow but accurate) + * + * @return array + */ + public function toHsvFloat() { + $rgb = $this->toRgbInt(); + + $rgb_min = min( $rgb ); + $rgb_max = max( $rgb ); + + $hsv = array( + 'hue' => 0, + 'sat' => 0, + 'val' => $rgb_max, + ); + + // If v is 0, color is black. + if ( 0 === $hsv['val'] ) { + return $hsv; + } + + // Normalize RGB values to 1. + $rgb['red'] /= $hsv['val']; + $rgb['green'] /= $hsv['val']; + $rgb['blue'] /= $hsv['val']; + $rgb_min = min( $rgb ); + $rgb_max = max( $rgb ); + + // Calculate saturation. + $hsv['sat'] = $rgb_max - $rgb_min; + if ( 0 === $hsv['sat'] ) { + $hsv['hue'] = 0; + return $hsv; + } + + // Normalize saturation to 1. + $rgb['red'] = ( $rgb['red'] - $rgb_min ) / ( $rgb_max - $rgb_min ); + $rgb['green'] = ( $rgb['green'] - $rgb_min ) / ( $rgb_max - $rgb_min ); + $rgb['blue'] = ( $rgb['blue'] - $rgb_min ) / ( $rgb_max - $rgb_min ); + $rgb_min = min( $rgb ); + $rgb_max = max( $rgb ); + + // Calculate hue. + if ( $rgb_max === $rgb['red'] ) { + $hsv['hue'] = 0.0 + 60 * ( $rgb['green'] - $rgb['blue'] ); + if ( $hsv['hue'] < 0 ) { + $hsv['hue'] += 360; + } + } elseif ( $rgb_max === $rgb['green'] ) { + $hsv['hue'] = 120 + ( 60 * ( $rgb['blue'] - $rgb['red'] ) ); + } else { + $hsv['hue'] = 240 + ( 60 * ( $rgb['red'] - $rgb['green'] ) ); + } + + return $hsv; + } + + /** + * Get HSV values for color + * (integer values from 0-255, fast but less accurate) + * + * @return array + */ + public function toHsvInt() { + $rgb = $this->toRgbInt(); + + $rgb_min = min( $rgb ); + $rgb_max = max( $rgb ); + + $hsv = array( + 'hue' => 0, + 'sat' => 0, + 'val' => $rgb_max, + ); + + // If value is 0, color is black. + if ( 0 === $hsv['val'] ) { + return $hsv; + } + + // Calculate saturation. + $hsv['sat'] = round( 255 * ( $rgb_max - $rgb_min ) / $hsv['val'] ); + // @phan-suppress-next-line PhanImpossibleTypeComparison + if ( 0 === $hsv['sat'] ) { + $hsv['hue'] = 0; + return $hsv; + } + + // Calculate hue. + if ( $rgb_max === $rgb['red'] ) { + $hsv['hue'] = round( 0 + 43 * ( $rgb['green'] - $rgb['blue'] ) / ( $rgb_max - $rgb_min ) ); + } elseif ( $rgb_max === $rgb['green'] ) { + $hsv['hue'] = round( 85 + 43 * ( $rgb['blue'] - $rgb['red'] ) / ( $rgb_max - $rgb_min ) ); + } else { + $hsv['hue'] = round( 171 + 43 * ( $rgb['red'] - $rgb['green'] ) / ( $rgb_max - $rgb_min ) ); + } + if ( $hsv['hue'] < 0 ) { + $hsv['hue'] += 255; + } + + return $hsv; + } + + /** + * Converts an RGB color value to HSL. Conversion formula + * adapted from https://en.wikipedia.org/wiki/HSL_color_space. + * Assumes r, g, and b are contained in the set [0, 255] and + * returns h in [0, 360], s in [0, 100], l in [0, 100] + * + * @return Array The HSL representation + */ + public function toHsl() { + list( $r, $g, $b ) = array_values( $this->toRgbInt() ); + $r /= 255; + $g /= 255; + $b /= 255; + $max = max( $r, $g, $b ); + $min = min( $r, $g, $b ); + $l = ( $max + $min ) / 2; + + if ( $max === $min ) { + // achromatic. + $s = 0; + $h = 0; + } else { + $d = $max - $min; + $s = $l > 0.5 ? $d / ( 2 - $max - $min ) : $d / ( $max + $min ); + switch ( $max ) { + case $r: + $h = ( $g - $b ) / $d + ( $g < $b ? 6 : 0 ); + break; + case $g: + $h = ( $b - $r ) / $d + 2; + break; + case $b: + $h = ( $r - $g ) / $d + 4; + break; + } + $h /= 6; + } + $h = (int) round( $h * 360 ); + $s = (int) round( $s * 100 ); + $l = (int) round( $l * 100 ); + return compact( 'h', 's', 'l' ); + } + + /** + * From a color code to a string to be used in CSS declaration. + * + * @param string $type Color code type. + * @param int $alpha Transparency. + * + * @return string + */ + public function toCSS( $type = 'hex', $alpha = 1 ) { + switch ( $type ) { + case 'hex': + return $this->toString(); + case 'rgb': + case 'rgba': + list( $r, $g, $b ) = array_values( $this->toRgbInt() ); + if ( is_numeric( $alpha ) && $alpha < 1 ) { + return "rgba( {$r}, {$g}, {$b}, $alpha )"; + } else { + return "rgb( {$r}, {$g}, {$b} )"; + } + case 'hsl': + case 'hsla': + list( $h, $s, $l ) = array_values( $this->toHsl() ); + if ( is_numeric( $alpha ) && $alpha < 1 ) { + return "hsla( {$h}, {$s}, {$l}, $alpha )"; + } else { + return "hsl( {$h}, {$s}, {$l} )"; + } + default: + return $this->toString(); + } + } + + /** + * Get current color in XYZ format + * + * @return array + */ + public function toXyz() { + $rgb = $this->toRgbInt(); + + // Normalize RGB values to 1. + $rgb_new = array(); + foreach ( $rgb as $item ) { + $rgb_new[] = $item / 255; + } + $rgb = $rgb_new; + + $rgb_new = array(); + foreach ( $rgb as $item ) { + if ( $item > 0.04045 ) { + $item = pow( ( ( $item + 0.055 ) / 1.055 ), 2.4 ); + } else { + $item /= 12.92; + } + $rgb_new[] = $item * 100; + } + $rgb = $rgb_new; + + // Observer. = 2°, Illuminant = D65. + $xyz = array( + // @phan-suppress-next-line PhanTypeInvalidDimOffset,PhanTypeInvalidLeftOperandOfNumericOp + 'x' => ( $rgb['red'] * 0.4124 ) + ( $rgb['green'] * 0.3576 ) + ( $rgb['blue'] * 0.1805 ), + // @phan-suppress-next-line PhanTypeInvalidDimOffset,PhanTypeInvalidLeftOperandOfNumericOp + 'y' => ( $rgb['red'] * 0.2126 ) + ( $rgb['green'] * 0.7152 ) + ( $rgb['blue'] * 0.0722 ), + // @phan-suppress-next-line PhanTypeInvalidDimOffset,PhanTypeInvalidLeftOperandOfNumericOp + 'z' => ( $rgb['red'] * 0.0193 ) + ( $rgb['green'] * 0.1192 ) + ( $rgb['blue'] * 0.9505 ), + ); + + return $xyz; + } + + /** + * Get color CIE-Lab values + * + * @return array + */ + public function toLabCie() { + $xyz = $this->toXyz(); + + // Ovserver = 2*, Iluminant=D65. + $xyz['x'] /= 95.047; + $xyz['y'] /= 100; + $xyz['z'] /= 108.883; + + $xyz_new = array(); + foreach ( $xyz as $item ) { + if ( $item > 0.008856 ) { + $xyz_new[] = pow( $item, 1 / 3 ); + } else { + $xyz_new[] = ( 7.787 * $item ) + ( 16 / 116 ); + } + } + $xyz = $xyz_new; + + $lab = array( + // @phan-suppress-next-line PhanTypeInvalidDimOffset,PhanTypeInvalidRightOperandOfNumericOp + 'l' => ( 116 * $xyz['y'] ) - 16, + // @phan-suppress-next-line PhanTypeInvalidDimOffset,PhanTypeInvalidLeftOperandOfNumericOp,PhanTypeInvalidRightOperandOfNumericOp + 'a' => 500 * ( $xyz['x'] - $xyz['y'] ), + // @phan-suppress-next-line PhanTypeInvalidDimOffset,PhanTypeInvalidLeftOperandOfNumericOp,PhanTypeInvalidRightOperandOfNumericOp + 'b' => 200 * ( $xyz['y'] - $xyz['z'] ), + ); + + return $lab; + } + + /** + * Convert color to integer + * + * @return int + */ + public function toInt() { + return $this->color; + } + + /** + * Alias of toString() + * + * @return string + */ + public function __toString() { + return $this->toString(); + } + + /** + * Get color as string + * + * @return string + */ + public function toString() { + $str = $this->toHex(); + return strtoupper( "#{$str}" ); + } + + /** + * Get the distance between this color and the given color + * + * @param Jetpack_Color $color Color code. + * + * @return int + */ + public function getDistanceRgbFrom( Jetpack_Color $color ) { + $rgb1 = $this->toRgbInt(); + $rgb2 = $color->toRgbInt(); + + $r_diff = abs( $rgb1['red'] - $rgb2['red'] ); + $g_diff = abs( $rgb1['green'] - $rgb2['green'] ); + $b_diff = abs( $rgb1['blue'] - $rgb2['blue'] ); + + // Sum of RGB differences. + $diff = $r_diff + $g_diff + $b_diff; + return $diff; + } + + /** + * Get distance from the given color using the Delta E method + * + * @param Jetpack_Color $color Color code. + * + * @return float + */ + public function getDistanceLabFrom( Jetpack_Color $color ) { + $lab1 = $this->toLabCie(); + $lab2 = $color->toLabCie(); + + $l_diff = abs( $lab2['l'] - $lab1['l'] ); + $a_diff = abs( $lab2['a'] - $lab1['a'] ); + $b_diff = abs( $lab2['b'] - $lab1['b'] ); + + $delta = sqrt( $l_diff + $a_diff + $b_diff ); + + return $delta; + } + + /** + * Calculate luminosity. + * + * @return float + */ + public function toLuminosity() { + $lum = array(); + foreach ( $this->toRgbInt() as $slot => $value ) { + $chan = $value / 255; + $lum[ $slot ] = ( $chan <= 0.03928 ) ? $chan / 12.92 : pow( ( ( $chan + 0.055 ) / 1.055 ), 2.4 ); + } + return 0.2126 * $lum['red'] + 0.7152 * $lum['green'] + 0.0722 * $lum['blue']; + } + + /** + * Get distance between colors using luminance. + * Should be more than 5 for readable contrast + * + * @param Jetpack_Color $color Another color. + * @return float + */ + public function getDistanceLuminosityFrom( Jetpack_Color $color ) { + $l1 = $this->toLuminosity(); + $l2 = $color->toLuminosity(); + if ( $l1 > $l2 ) { + return ( $l1 + 0.05 ) / ( $l2 + 0.05 ); + } else { + return ( $l2 + 0.05 ) / ( $l1 + 0.05 ); + } + } + + /** + * Get maximum contrast color. + * + * @return $this + */ + public function getMaxContrastColor() { + $with_black = $this->getDistanceLuminosityFrom( new Jetpack_Color( '#000' ) ); + $with_white = $this->getDistanceLuminosityFrom( new Jetpack_Color( '#fff' ) ); + $color = new Jetpack_Color(); + $hex = ( $with_black >= $with_white ) ? '#000000' : '#ffffff'; + return $color->fromHex( $hex ); + } + + /** + * Get grayscale contrasting color. + * + * @param bool|int $contrast Contrast. + * + * @return $this + */ + public function getGrayscaleContrastingColor( $contrast = false ) { + if ( ! $contrast ) { + return $this->getMaxContrastColor(); + } + // don't allow less than 5. + $target_contrast = ( $contrast < 5 ) ? 5 : $contrast; + $color = $this->getMaxContrastColor(); + $contrast = $color->getDistanceLuminosityFrom( $this ); + + // if current max contrast is less than the target contrast, we had wishful thinking. + if ( $contrast <= $target_contrast ) { + return $color; + } + + $incr = ( '#000000' === $color->toString() ) ? 1 : -1; + while ( $contrast > $target_contrast ) { + $color = $color->incrementLightness( $incr ); + $contrast = $color->getDistanceLuminosityFrom( $this ); + } + + return $color; + } + + /** + * Gets a readable contrasting color. $this is assumed to be the text and $color the background color. + * + * @param object $bg_color A Color object that will be compared against $this. + * @param integer $min_contrast The minimum contrast to achieve, if possible. + * @return object A Color object, an increased contrast $this compared against $bg_color + */ + public function getReadableContrastingColor( $bg_color = null, $min_contrast = 5 ) { + if ( ! $bg_color || ! is_a( $bg_color, 'Jetpack_Color' ) ) { + return $this; + } + // you shouldn't use less than 5, but you might want to. + $target_contrast = $min_contrast; + // working things. + $contrast = $bg_color->getDistanceLuminosityFrom( $this ); + $max_contrast_color = $bg_color->getMaxContrastColor(); + $max_contrast = $max_contrast_color->getDistanceLuminosityFrom( $bg_color ); + + // if current max contrast is less than the target contrast, we had wishful thinking. + // still, go max. + if ( $max_contrast <= $target_contrast ) { + return $max_contrast_color; + } + // or, we might already have sufficient contrast. + if ( $contrast >= $target_contrast ) { + return $this; + } + + $incr = ( 0 === $max_contrast_color->toInt() ) ? -1 : 1; + while ( $contrast < $target_contrast ) { + $this->incrementLightness( $incr ); + $contrast = $bg_color->getDistanceLuminosityFrom( $this ); + // infininite loop prevention: you never know. + if ( 0 === $this->color || 16777215 === $this->color ) { + break; + } + } + + return $this; + } + + /** + * Detect if color is grayscale + * + * @param int $threshold Max difference between colors. + * + * @return bool + */ + public function isGrayscale( $threshold = 16 ) { + $rgb = $this->toRgbInt(); + + // Get min and max rgb values, then difference between them. + $rgb_min = min( $rgb ); + $rgb_max = max( $rgb ); + $diff = $rgb_max - $rgb_min; + + return $diff < $threshold; + } + + /** + * Get the closest matching color from the given array of colors + * + * @param array $colors array of integers or Jetpack_Color objects. + * + * @return mixed the array key of the matched color + */ + public function getClosestMatch( array $colors ) { + $match_dist = 10000; + $match_key = null; + foreach ( $colors as $key => $color ) { + if ( ! ( $color instanceof Jetpack_Color ) ) { + $c = new Jetpack_Color( $color ); + } + // @phan-suppress-next-line PhanPossiblyUndeclaredVariable,PhanTypeMismatchArgumentNullable + $dist = $this->getDistanceLabFrom( $c ); + if ( $dist < $match_dist ) { + $match_dist = $dist; + $match_key = $key; + } + } + + return $match_key; + } + + /* TRANSFORMS */ + + /** + * Transform -- Darken color. + * + * @param int $amount Amount. Default to 5. + * + * @return $this + */ + public function darken( $amount = 5 ) { + return $this->incrementLightness( - $amount ); + } + + /** + * Transform -- Lighten color. + * + * @param int $amount Amount. Default to 5. + * + * @return $this + */ + public function lighten( $amount = 5 ) { + return $this->incrementLightness( $amount ); + } + + /** + * Transform -- Increment lightness. + * + * @param int $amount Amount. + * + * @return $this + */ + public function incrementLightness( $amount ) { + $hsl = $this->toHsl(); + + $h = $hsl['h'] ?? 0; + $s = $hsl['s'] ?? 0; + $l = $hsl['l'] ?? 0; + + $l += $amount; + if ( $l < 0 ) { + $l = 0; + } + if ( $l > 100 ) { + $l = 100; + } + return $this->fromHsl( $h, $s, $l ); + } + + /** + * Transform -- Saturate color. + * + * @param int $amount Amount. Default to 15. + * + * @return $this + */ + public function saturate( $amount = 15 ) { + return $this->incrementSaturation( $amount ); + } + + /** + * Transform -- Desaturate color. + * + * @param int $amount Amount. Default to 15. + * + * @return $this + */ + public function desaturate( $amount = 15 ) { + return $this->incrementSaturation( - $amount ); + } + + /** + * Transform -- Increment saturation. + * + * @param int $amount Amount. + * + * @return $this + */ + public function incrementSaturation( $amount ) { + $hsl = $this->toHsl(); + + $h = $hsl['h'] ?? 0; + $s = $hsl['s'] ?? 0; + $l = $hsl['l'] ?? 0; + + $s += $amount; + if ( $s < 0 ) { + $s = 0; + } + if ( $s > 100 ) { + $s = 100; + } + return $this->fromHsl( $h, $s, $l ); + } + + /** + * Transform -- To grayscale. + * + * @return $this + */ + public function toGrayscale() { + $hsl = $this->toHsl(); + + $h = $hsl['h'] ?? 0; + $s = 0; + $l = $hsl['l'] ?? 0; + + return $this->fromHsl( $h, $s, $l ); + } + + /** + * Transform -- To the complementary color. + * + * The complement is the color on the opposite side of the color wheel, 180° away. + * + * @return $this + */ + public function getComplement() { + return $this->incrementHue( 180 ); + } + + /** + * Transform -- To an analogous color of the complement. + * + * @param int $step Pass `1` or `-1` to choose which direction around the color wheel. + * + * @return $this + */ + public function getSplitComplement( $step = 1 ) { + $incr = 180 + ( $step * 30 ); + return $this->incrementHue( $incr ); + } + + /** + * Transform -- To an analogous color. + * + * Analogous colors are those adjacent on the color wheel, separated by 30°. + * + * @param int $step Pass `1` or `-1` to choose which direction around the color wheel. + * + * @return $this + */ + public function getAnalog( $step = 1 ) { + $incr = $step * 30; + return $this->incrementHue( $incr ); + } + + /** + * Transform -- To a tetradic (rectangular) color. + * + * A rectangular color scheme uses a color, its complement, and the colors 60° from each. + * This transforms the color to its 60° "tetrad". + * + * @param int $step Pass `1` or `-1` to choose which direction around the color wheel. + * + * @return $this + */ + public function getTetrad( $step = 1 ) { + $incr = $step * 60; + return $this->incrementHue( $incr ); + } + + /** + * Transform -- To a triadic color. + * + * A triadic color scheme uses three colors evenly spaced (120°) around the color wheel. + * This transforms the color to one of its triadic colors. + * + * @param int $step Pass `1` or `-1` to choose which direction around the color wheel. + * + * @return $this + */ + public function getTriad( $step = 1 ) { + $incr = $step * 120; + return $this->incrementHue( $incr ); + } + + /** + * Transform -- Increment hue. + * + * @param int $amount Amount. + * + * @return $this + */ + public function incrementHue( $amount ) { + $hsl = $this->toHsl(); + + $h = $hsl['h'] ?? 0; + $s = $hsl['s'] ?? 0; + $l = $hsl['l'] ?? 0; + + $h = ( $h + $amount ) % 360; + if ( $h < 0 ) { + $h += 360; + } + return $this->fromHsl( $h, $s, $l ); + } + } +} diff --git a/projects/packages/classic-theme-helper/_inc/lib/tonesque.php b/projects/packages/classic-theme-helper/_inc/lib/tonesque.php index b16480b914fae..e90d8a13305bb 100644 --- a/projects/packages/classic-theme-helper/_inc/lib/tonesque.php +++ b/projects/packages/classic-theme-helper/_inc/lib/tonesque.php @@ -8,271 +8,273 @@ * @package automattic/jetpack */ -/** - * Color representation class. - */ -class Tonesque { - /** - * Image URL. - * - * @var string - */ - private $image_url = ''; - /** - * Image identifier representing the image. - * - * @var null|object - */ - private $image_obj = null; +if ( ! class_exists( 'Tonesque' ) ) { /** - * Color code. - * - * @var string + * Color representation class. */ - private $color = ''; + class Tonesque { + /** + * Image URL. + * + * @var string + */ + private $image_url = ''; + /** + * Image identifier representing the image. + * + * @var null|object + */ + private $image_obj = null; + /** + * Color code. + * + * @var string + */ + private $color = ''; - /** - * Constructor. - * - * @param string $image_url Image URL. - */ - public function __construct( $image_url ) { - if ( ! class_exists( 'Jetpack_Color' ) && defined( 'JETPACK__PLUGIN_DIR' ) ) { - require_once JETPACK__PLUGIN_DIR . '/_inc/lib/class.color.php'; + /** + * Constructor. + * + * @param string $image_url Image URL. + */ + public function __construct( $image_url ) { + if ( ! class_exists( 'Jetpack_Color' ) ) { + require_once __DIR__ . '/class.color.php'; + } + + $this->image_url = esc_url_raw( $image_url ); + $this->image_url = trim( $this->image_url ); + /** + * Allows any image URL to be passed in for $this->image_url. + * + * @module theme-tools + * + * @since 2.5.0 + * + * @param string $image_url The URL to any image + */ + $this->image_url = apply_filters( 'tonesque_image_url', $this->image_url ); + + $this->image_obj = self::imagecreatefromurl( $this->image_url ); } - $this->image_url = esc_url_raw( $image_url ); - $this->image_url = trim( $this->image_url ); /** - * Allows any image URL to be passed in for $this->image_url. - * - * @module theme-tools + * Get an image object from a URL. * - * @since 2.5.0 + * @param string $image_url Image URL. * - * @param string $image_url The URL to any image + * @return object|bool Image object or false if the image could not be loaded. */ - $this->image_url = apply_filters( 'tonesque_image_url', $this->image_url ); + public static function imagecreatefromurl( $image_url ) { + $data = null; - $this->image_obj = self::imagecreatefromurl( $this->image_url ); - } + // If it's a URL. + if ( preg_match( '#^https?://#i', $image_url ) ) { + // If it's a url pointing to a local media library url. + $content_url = content_url(); + $_image_url = set_url_scheme( $image_url ); + if ( str_starts_with( $_image_url, $content_url ) ) { + $_image_path = str_replace( $content_url, WP_CONTENT_DIR, $_image_url ); + if ( file_exists( $_image_path ) ) { + $filetype = wp_check_filetype( $_image_path ); + $type = $filetype['type']; - /** - * Get an image object from a URL. - * - * @param string $image_url Image URL. - * - * @return object|bool Image object or false if the image could not be loaded. - */ - public static function imagecreatefromurl( $image_url ) { - $data = null; - - // If it's a URL. - if ( preg_match( '#^https?://#i', $image_url ) ) { - // If it's a url pointing to a local media library url. - $content_url = content_url(); - $_image_url = set_url_scheme( $image_url ); - if ( str_starts_with( $_image_url, $content_url ) ) { - $_image_path = str_replace( $content_url, WP_CONTENT_DIR, $_image_url ); - if ( file_exists( $_image_path ) ) { - $filetype = wp_check_filetype( $_image_path ); - $type = $filetype['type']; + if ( str_starts_with( $type, 'image/' ) ) { + $data = file_get_contents( $_image_path ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents + } + } + } - if ( str_starts_with( $type, 'image/' ) ) { - $data = file_get_contents( $_image_path ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents + if ( empty( $data ) ) { + $response = wp_safe_remote_get( $image_url ); + $response_code = wp_remote_retrieve_response_code( $response ); + if ( + is_wp_error( $response ) + || ! $response_code + || $response_code < 200 + || $response_code >= 300 + ) { + return false; } + $data = wp_remote_retrieve_body( $response ); } } - if ( empty( $data ) ) { - $response = wp_safe_remote_get( $image_url ); - $response_code = wp_remote_retrieve_response_code( $response ); - if ( - is_wp_error( $response ) - || ! $response_code - || $response_code < 200 - || $response_code >= 300 - ) { - return false; + // If it's a local path in our WordPress install. + if ( file_exists( $image_url ) ) { + $filetype = wp_check_filetype( $image_url ); + $type = $filetype['type']; + + if ( str_starts_with( $type, 'image/' ) ) { + $data = file_get_contents( $image_url ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents } - $data = wp_remote_retrieve_body( $response ); } - } - // If it's a local path in our WordPress install. - if ( file_exists( $image_url ) ) { - $filetype = wp_check_filetype( $image_url ); - $type = $filetype['type']; - - if ( str_starts_with( $type, 'image/' ) ) { - $data = file_get_contents( $image_url ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents + if ( null === $data ) { + return false; } - } - if ( null === $data ) { - return false; + // Now turn it into an image and return it. + return imagecreatefromstring( $data ); } - // Now turn it into an image and return it. - return imagecreatefromstring( $data ); - } + /** + * Construct object from image. + * + * @param string $type Type (hex, rgb, hsv) (optional). + * + * @return string|bool color as a string formatted as $type or false if the image could not be loaded. + */ + public function color( $type = 'hex' ) { + // Bail if there is no image to work with. + if ( ! $this->image_obj ) { + return false; + } - /** - * Construct object from image. - * - * @param string $type Type (hex, rgb, hsv) (optional). - * - * @return string|bool color as a string formatted as $type or false if the image could not be loaded. - */ - public function color( $type = 'hex' ) { - // Bail if there is no image to work with. - if ( ! $this->image_obj ) { - return false; + // Finds dominant color. + $color = self::grab_color(); + // Passes value to Color class. + return self::get_color( $color, $type ); } - // Finds dominant color. - $color = self::grab_color(); - // Passes value to Color class. - return self::get_color( $color, $type ); - } - - /** - * Grabs the color index for each of five sample points of the image - * - * @param string $type can be 'index' or 'hex'. - * - * @return array|false color indices or false if the image could not be loaded. - */ - public function grab_points( $type = 'index' ) { - $img = $this->image_obj; - if ( ! $img ) { - return false; - } + /** + * Grabs the color index for each of five sample points of the image + * + * @param string $type can be 'index' or 'hex'. + * + * @return array|false color indices or false if the image could not be loaded. + */ + public function grab_points( $type = 'index' ) { + $img = $this->image_obj; + if ( ! $img ) { + return false; + } - $height = imagesy( $img ); - $width = imagesx( $img ); + $height = imagesy( $img ); + $width = imagesx( $img ); - /* - * Sample five points in the image - * based on rule of thirds and center. - */ - $topy = round( $height / 3 ); - $bottomy = round( ( $height / 3 ) * 2 ); - $leftx = round( $width / 3 ); - $rightx = round( ( $width / 3 ) * 2 ); - $centery = round( $height / 2 ); - $centerx = round( $width / 2 ); + /* + * Sample five points in the image + * based on rule of thirds and center. + */ + $topy = round( $height / 3 ); + $bottomy = round( ( $height / 3 ) * 2 ); + $leftx = round( $width / 3 ); + $rightx = round( ( $width / 3 ) * 2 ); + $centery = round( $height / 2 ); + $centerx = round( $width / 2 ); - // Cast those colors into an array. - $points = array( - imagecolorat( $img, $leftx, $topy ), - imagecolorat( $img, $rightx, $topy ), - imagecolorat( $img, $leftx, $bottomy ), - imagecolorat( $img, $rightx, $bottomy ), - imagecolorat( $img, $centerx, $centery ), - ); + // Cast those colors into an array. + $points = array( + imagecolorat( $img, $leftx, $topy ), + imagecolorat( $img, $rightx, $topy ), + imagecolorat( $img, $leftx, $bottomy ), + imagecolorat( $img, $rightx, $bottomy ), + imagecolorat( $img, $centerx, $centery ), + ); - if ( 'hex' === $type ) { - foreach ( $points as $i => $p ) { - $c = imagecolorsforindex( $img, $p ); - $points[ $i ] = self::get_color( - array( - 'r' => $c['red'], - 'g' => $c['green'], - 'b' => $c['blue'], - ), - 'hex' - ); + if ( 'hex' === $type ) { + foreach ( $points as $i => $p ) { + $c = imagecolorsforindex( $img, $p ); + $points[ $i ] = self::get_color( + array( + 'r' => $c['red'], + 'g' => $c['green'], + 'b' => $c['blue'], + ), + 'hex' + ); + } } + + return $points; } - return $points; - } + /** + * Finds the average color of the image based on five sample points + * + * @return array|bool array with rgb color or false if the image could not be loaded. + */ + public function grab_color() { + $img = $this->image_obj; + if ( ! $img ) { + return false; + } - /** - * Finds the average color of the image based on five sample points - * - * @return array|bool array with rgb color or false if the image could not be loaded. - */ - public function grab_color() { - $img = $this->image_obj; - if ( ! $img ) { - return false; - } + $rgb = self::grab_points(); - $rgb = self::grab_points(); + $r = array(); + $g = array(); + $b = array(); - $r = array(); - $g = array(); - $b = array(); + /* + * Process the color points + * Find the average representation + */ + foreach ( $rgb as $color ) { + $index = imagecolorsforindex( $img, $color ); + $r[] = $index['red']; + $g[] = $index['green']; + $b[] = $index['blue']; + } + $red = round( array_sum( $r ) / 5 ); + $green = round( array_sum( $g ) / 5 ); + $blue = round( array_sum( $b ) / 5 ); - /* - * Process the color points - * Find the average representation - */ - foreach ( $rgb as $color ) { - $index = imagecolorsforindex( $img, $color ); - $r[] = $index['red']; - $g[] = $index['green']; - $b[] = $index['blue']; - } - $red = round( array_sum( $r ) / 5 ); - $green = round( array_sum( $g ) / 5 ); - $blue = round( array_sum( $b ) / 5 ); + // The average color of the image as rgb array. + $color = array( + 'r' => $red, + 'g' => $green, + 'b' => $blue, + ); - // The average color of the image as rgb array. - $color = array( - 'r' => $red, - 'g' => $green, - 'b' => $blue, - ); + return $color; + } - return $color; - } + /** + * Get a Color object using /lib class.color + * Convert to appropriate type + * + * @param string $color Color code. + * @param string $type Color type (rgb, hex, hsv). + * + * @return string + */ + public function get_color( $color, $type ) { + $c = new Jetpack_Color( $color, 'rgb' ); + $this->color = $c; - /** - * Get a Color object using /lib class.color - * Convert to appropriate type - * - * @param string $color Color code. - * @param string $type Color type (rgb, hex, hsv). - * - * @return string - */ - public function get_color( $color, $type ) { - $c = new Jetpack_Color( $color, 'rgb' ); - $this->color = $c; + switch ( $type ) { + case 'rgb': + $color = implode( ',', $c->toRgbInt() ); + break; + case 'hex': + $color = $c->toHex(); + break; + case 'hsv': + $color = implode( ',', $c->toHsvInt() ); + break; + default: + return $c->toHex(); + } - switch ( $type ) { - case 'rgb': - $color = implode( ',', $c->toRgbInt() ); - break; - case 'hex': - $color = $c->toHex(); - break; - case 'hsv': - $color = implode( ',', $c->toHsvInt() ); - break; - default: - return $c->toHex(); + return $color; } - return $color; - } + /** + * + * Checks contrast against main color + * Gives either black or white for using with opacity + * + * @return string|bool Returns black or white or false if the image could not be loaded. + */ + public function contrast() { + if ( ! $this->color ) { + return false; + } - /** - * - * Checks contrast against main color - * Gives either black or white for using with opacity - * - * @return string|bool Returns black or white or false if the image could not be loaded. - */ - public function contrast() { - if ( ! $this->color ) { - return false; + $c = $this->color->getMaxContrastColor(); + return implode( ',', $c->toRgbInt() ); } - - $c = $this->color->getMaxContrastColor(); - return implode( ',', $c->toRgbInt() ); } } diff --git a/projects/packages/classic-theme-helper/changelog/add-require-social-menu-classic-theme-helper-package b/projects/packages/classic-theme-helper/changelog/add-require-social-menu-classic-theme-helper-package new file mode 100644 index 0000000000000..a74567d00dae5 --- /dev/null +++ b/projects/packages/classic-theme-helper/changelog/add-require-social-menu-classic-theme-helper-package @@ -0,0 +1,4 @@ +Significance: patch +Type: changed + +Social Menus: Requiring the feature from the Classic Theme Helper package. diff --git a/projects/packages/classic-theme-helper/changelog/update-featured-content-remove-wpcom-only-code b/projects/packages/classic-theme-helper/changelog/update-featured-content-remove-wpcom-only-code deleted file mode 100644 index 6014eff375663..0000000000000 --- a/projects/packages/classic-theme-helper/changelog/update-featured-content-remove-wpcom-only-code +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: removed - -Classic Theme Helper: Remove wpcom only code for featured content diff --git a/projects/packages/classic-theme-helper/package.json b/projects/packages/classic-theme-helper/package.json index 81e026c6ea9bd..9c33543cb466a 100644 --- a/projects/packages/classic-theme-helper/package.json +++ b/projects/packages/classic-theme-helper/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@automattic/jetpack-classic-theme-helper", - "version": "0.4.0-alpha", + "version": "0.4.3-alpha", "description": "Features used with classic themes", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/packages/classic-theme-helper/#readme", "bugs": { diff --git a/projects/packages/classic-theme-helper/src/class-featured-content.php b/projects/packages/classic-theme-helper/src/class-featured-content.php index 56447d3de8c8c..520ce31a32c29 100644 --- a/projects/packages/classic-theme-helper/src/class-featured-content.php +++ b/projects/packages/classic-theme-helper/src/class-featured-content.php @@ -10,7 +10,7 @@ use Automattic\Jetpack\Assets; use WP_Customize_Manager; use WP_Query; -if ( ! class_exists( __NAMESPACE__ . '\Featured_Content' ) && isset( $GLOBALS['pagenow'] ) && 'plugins.php' !== $GLOBALS['pagenow'] ) { +if ( ! class_exists( __NAMESPACE__ . '\Featured_Content' ) ) { /** * Featured Content. @@ -88,6 +88,9 @@ public static function setup() { */ public static function init() { + if ( isset( $GLOBALS['pagenow'] ) && 'plugins.php' === $GLOBALS['pagenow'] ) { + return; + } /** * Array variable to store theme support. * @@ -757,6 +760,4 @@ public static function jetpack_update_featured_content_for_split_terms( $old_ter } } } - - Featured_Content::setup(); } diff --git a/projects/packages/classic-theme-helper/src/class-main.php b/projects/packages/classic-theme-helper/src/class-main.php index c38ef4f5a2a65..af2f0b15b8611 100644 --- a/projects/packages/classic-theme-helper/src/class-main.php +++ b/projects/packages/classic-theme-helper/src/class-main.php @@ -14,7 +14,7 @@ */ class Main { - const PACKAGE_VERSION = '0.4.0-alpha'; + const PACKAGE_VERSION = '0.4.3-alpha'; /** * Modules to include. @@ -22,8 +22,9 @@ class Main { * @var array */ public $modules = array( - 'class-featured-content.php', - // 'responsive-videos.php', + 'responsive-videos.php', + 'social-menu.php', + 'jetpack-color.php', ); /** Holds the singleton instance of the Loader @@ -38,9 +39,10 @@ class Main { public static function init() { if ( ! self::$instance ) { self::$instance = new Main(); - add_action( 'plugins_loaded', array( self::$instance, 'load_modules' ) ); + self::$instance->load_modules(); // TODO Commenting below since we still load them from theme-tools module - // add_action( 'init', array( __CLASS__, 'jetpack_load_theme_tools' ), 30 ); + add_action( 'init', array( __CLASS__, 'jetpack_load_theme_tools' ), 30 ); + // phpcs:ignore Squiz.PHP.CommentedOutCode.Found // add_action( 'after_setup_theme', array( __CLASS__, 'jetpack_load_theme_compat' ), -1 ); } @@ -66,7 +68,7 @@ public function load_modules() { */ public static function jetpack_load_theme_tools() { if ( current_theme_supports( 'tonesque' ) ) { - require_once __DIR__ . '../_inc/lib/tonesque.php'; + require_once __DIR__ . '/../_inc/lib/tonesque.php'; } } @@ -124,4 +126,5 @@ private static function jetpack_require_compat_file( $key, $files ) { } } } + Main::init(); diff --git a/projects/packages/classic-theme-helper/src/jetpack-color.php b/projects/packages/classic-theme-helper/src/jetpack-color.php new file mode 100644 index 0000000000000..5d3b347f249d4 --- /dev/null +++ b/projects/packages/classic-theme-helper/src/jetpack-color.php @@ -0,0 +1,8 @@ + esc_html( $social_menu_description ), + ) + ); + + // Enqueue CSS + add_action( 'wp_enqueue_scripts', 'jetpack_social_menu_style' ); + + // Load SVG icons related functions and filters + if ( 'svg' === jetpack_social_menu_get_type() ) { + require __DIR__ . '/social-menu/icon-functions.php'; + } + } + add_action( 'after_setup_theme', 'jetpack_social_menu_init', 99 ); + add_action( 'restapi_theme_init', 'jetpack_social_menu_init' ); +} + +if ( ! function_exists( 'jetpack_social_menu_get_type' ) ) { + /** + * Return the type of menu the theme is using. + * + * @uses get_theme_support() + * @return null|string $menu_type + */ + function jetpack_social_menu_get_type() { + $options = get_theme_support( 'jetpack-social-menu' ); + + if ( ! $options ) { + $menu_type = null; + } else { + $menu_type = 'genericons'; + if ( is_array( $options ) && isset( $options[0] ) ) { + $menu_type = ( in_array( $options[0], array( 'genericons', 'svg' ), true ) ) ? $options[0] : 'genericons'; + } + } + + return $menu_type; + } +} + +if ( ! function_exists( 'jetpack_social_menu_style' ) ) { + /** + * Function to enqueue the CSS. + */ + function jetpack_social_menu_style() { + $menu_type = jetpack_social_menu_get_type(); + + if ( ! $menu_type ) { + return; + } + + $deps = ( 'genericons' === $menu_type ) ? array( 'genericons' ) : null; + + if ( has_nav_menu( 'jetpack-social-menu' ) ) { + wp_enqueue_style( 'jetpack-social-menu', plugins_url( 'social-menu/social-menu.css', __FILE__ ), $deps, '1.0' ); + } + } +} + +if ( ! function_exists( 'jetpack_social_menu' ) ) { + /** + * Create the function for the menu. + */ + function jetpack_social_menu() { + if ( has_nav_menu( 'jetpack-social-menu' ) ) : + $menu_type = (string) jetpack_social_menu_get_type(); + $link_after = ''; + + if ( 'svg' === $menu_type ) { + $link_after .= jetpack_social_menu_get_svg( array( 'icon' => 'chain' ) ); + } ?> + + '', + 'fallback' => false, + ); + + // Parse args. + $args = wp_parse_args( $args, $defaults ); + + // Set aria hidden. + $aria_hidden = ' aria-hidden="true"'; + + // Begin SVG markup. + $svg = ''; + + /* + * Display the icon. + * + * The whitespace around `` is intentional - it is a work around to a keyboard navigation bug in Safari 10. + * + * See https://core.trac.wordpress.org/ticket/38387. + */ + $svg .= ' '; + + // Add some markup to use as a fallback for browsers that do not support SVGs. + if ( $args['fallback'] ) { + $svg .= ''; + } + + $svg .= ''; + + return $svg; + } +} + +if ( ! function_exists( 'jetpack_social_menu_nav_menu_social_icons' ) ) { + /** + * Display SVG icons in social links menu. + * + * @param string $item_output The menu item output. + * @param WP_Post $item Menu item object. + * @param int $depth Depth of the menu. + * @param object $args wp_nav_menu() arguments. + * @return string $item_output The menu item output with social icon. + */ + function jetpack_social_menu_nav_menu_social_icons( $item_output, $item, $depth, $args ) { + // Get supported social icons. + $social_icons = jetpack_social_menu_social_links_icons(); + + // Change SVG icon inside social links menu if there is supported URL. + if ( 'jetpack-social-menu' === $args->theme_location ) { + foreach ( $social_icons as $attr => $value ) { + /* + * attr can be a URL host, or a regex, starting with #. + * Let's check for both scenarios. + */ + if ( + // First Regex. + ( + str_starts_with( $attr, '#' ) && str_ends_with( $attr, '#' ) + && preg_match( $attr, $item_output ) + ) + // Then, regular host name. + || str_contains( $item_output, $attr ) + ) { + $item_output = str_replace( + $args->link_after, + '' . jetpack_social_menu_get_svg( array( 'icon' => esc_attr( $value ) ) ), + $item_output + ); + } + } + } + + return $item_output; + } + add_filter( 'walker_nav_menu_start_el', 'jetpack_social_menu_nav_menu_social_icons', 10, 4 ); +} + +if ( ! function_exists( 'jetpack_social_menu_social_links_icons' ) ) { + /** + * Returns an array of supported social links (URL / regex and icon name). + * For regex, use the # delimiter. + * + * @return array $social_links_icons + */ + function jetpack_social_menu_social_links_icons() { + // Supported social links icons. + $social_links_icons = array( + '#https?:\/\/(www\.)?amazon\.(com|cn|in|fr|de|it|nl|es|co|ca)\/#' => 'amazon', + '500px.com' => '500px', + 'apple.com' => 'apple', + 'itunes.com' => 'apple', + 'bandcamp.com' => 'bandcamp', + 'behance.net' => 'behance', + 'blogger.com' => 'blogger', + 'blogspot.com' => 'blogger', + 'bsky.app' => 'bluesky', + 'codepen.io' => 'codepen', + 'deviantart.com' => 'deviantart', + 'discord.gg' => 'discord', + 'discordapp.com' => 'discord', + 'digg.com' => 'digg', + 'dribbble.com' => 'dribbble', + 'dropbox.com' => 'dropbox', + 'etsy.com' => 'etsy', + 'eventbrite.com' => 'eventbrite', + 'facebook.com' => 'facebook', + '/feed/' => 'feed', + 'flickr.com' => 'flickr', + 'foursquare.com' => 'foursquare', + 'ghost.org' => 'ghost', + 'goodreads.com' => 'goodreads', + 'google.com' => 'google', + 'github.com' => 'github', + 'instagram.com' => 'instagram', + 'linkedin.com' => 'linkedin', + 'mailto:' => 'mail', + 'meetup.com' => 'meetup', + 'medium.com' => 'medium', + 'nextdoor.com' => 'nextdoor', + 'patreon.com' => 'patreon', + 'pinterest.' => 'pinterest', + 'getpocket.com' => 'pocket', + 'ravelry.com' => 'ravelry', + 'reddit.com' => 'reddit', + 'skype.com' => 'skype', + 'skype:' => 'skype', + 'slideshare.net' => 'slideshare', + 'sms:' => 'sms', + 'snapchat.com' => 'snapchat', + 'soundcloud.com' => 'soundcloud', + 'spotify.com' => 'spotify', + 'stackoverflow.com' => 'stackoverflow', + 'strava.com' => 'strava', + 'stumbleupon.com' => 'stumbleupon', + 'telegram.me' => 'telegram', + 'threads.net' => 'threads', + 'tiktok.com' => 'tiktok', + 'tumblr.com' => 'tumblr', + 'twitch.tv' => 'twitch', + 'twitter.com' => 'twitter', + 'vimeo.com' => 'vimeo', + 'vk.com' => 'vk', + 'whatsapp.com' => 'whatsapp', + 'woocommerce.com' => 'woocommerce', + 'wordpress.org' => 'wordpress', + 'wordpress.com' => 'wordpress', + 'yelp.com' => 'yelp', + 'x.com' => 'x', + 'xanga.com' => 'xanga', + 'youtube.com' => 'youtube', + ); + + /* + * Add Mastodon instances to this array. + */ + require_once __DIR__ . '../../shared-functions.php'; + $mastodon_instance_list = jetpack_mastodon_get_instance_list(); + foreach ( $mastodon_instance_list as $instance ) { + $social_links_icons[ $instance ] = 'mastodon'; + } + + return $social_links_icons; + } +} diff --git a/projects/packages/classic-theme-helper/src/social-menu/social-menu.css b/projects/packages/classic-theme-helper/src/social-menu/social-menu.css new file mode 100644 index 0000000000000..aad2f077364d3 --- /dev/null +++ b/projects/packages/classic-theme-helper/src/social-menu/social-menu.css @@ -0,0 +1,222 @@ +/*-------------------------------------------------------------- +Global +--------------------------------------------------------------*/ +.jetpack-social-navigation ul { + display: block; + margin: 0 0 1.5em; + padding: 0; +} + +.jetpack-social-navigation li { + display: inline-block; + margin: 0; + line-height: 1; +} + +.jetpack-social-navigation a { + border: 0; + height: 1em; + text-decoration: none; + width: 1em; +} + +/*-------------------------------------------------------------- +SVG +--------------------------------------------------------------*/ +.jetpack-social-navigation-svg .icon { + color: inherit; + fill: currentColor; + height: 1em; + vertical-align: middle; + width: 1em; +} + +/*-------------------------------------------------------------- +Genericons +--------------------------------------------------------------*/ +.jetpack-social-navigation-genericons a:before { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + display: inline-block; + font-family: Genericons; + font-size: 1em; + font-style: normal; + font-weight: normal; + height: 1em; + line-height: 1; + speak: none; + text-decoration: inherit; + vertical-align: top; + width: 1em; +} + +/* Default */ +.jetpack-social-navigation-genericons a:before { + content: "\f415"; +} + +/* Bluesky */ +.jetpack-social-navigation-genericons a[href*="bsky.app"]:before { + content: "\f10f"; +} + +/* CodePen */ +.jetpack-social-navigation-genericons a[href*="codepen.io"]:before { + content: "\f216"; +} + +/* Digg */ +.jetpack-social-navigation-genericons a[href*="digg.com"]:before { + content: "\f221"; +} + +/* Dribbble */ +.jetpack-social-navigation-genericons a[href*="dribbble.com"]:before { + content: "\f201"; +} + +/* Dropbox */ +.jetpack-social-navigation-genericons a[href*="dropbox.com"]:before { + content: "\f225"; +} + +/* Email */ +.jetpack-social-navigation-genericons a[href*="mailto:"]:before { + content: "\f410"; +} + +/* Facebook */ +.jetpack-social-navigation-genericons a[href*="facebook.com"]:before { + content: "\f203"; +} + +/* Flickr */ +.jetpack-social-navigation-genericons a[href*="flickr.com"]:before { + content: "\f211"; +} + +/* Foursquare */ +.jetpack-social-navigation-genericons a[href*="foursquare.com"]:before { + content: "\f226"; +} + +/* GitHub */ +.jetpack-social-navigation-genericons a[href*="github.com"]:before { + content: "\f200"; +} + +/* Google Plus */ +.jetpack-social-navigation-genericons a[href*="plus.google.com"]:before { + content: "\f206"; +} + +/* Instagram */ +.jetpack-social-navigation-genericons a[href*="instagram.com"]:before { + content: "\f215"; +} + +/* LinkedIn */ +.jetpack-social-navigation-genericons a[href*="linkedin.com"]:before { + content: "\f208"; +} + +/* Nextdoor */ +.jetpack-social-navigation-genericons a[href*="nextdoor.com"]:before { + content: "\f10c"; +} + +/* Path */ +.jetpack-social-navigation-genericons a[href*="path.com"]:before { + content: "\f219"; +} + +/* Pinterest */ +.jetpack-social-navigation-genericons a[href*="pinterest."]:before { + content: "\f210"; +} + +/* Pocket */ +.jetpack-social-navigation-genericons a[href*="getpocket.com"]:before { + content: "\f224"; +} + +/* Polldaddy */ +.jetpack-social-navigation-genericons a[href*="polldaddy.com"]:before { + content: "\f217"; +} + +/* Reddit */ +.jetpack-social-navigation-genericons a[href*="reddit.com"]:before { + content: "\f222"; +} + +/* RSS Feed */ +.jetpack-social-navigation-genericons a[href$="/feed/"]:before { + content: "\f413"; +} + +/* Skype */ +.jetpack-social-navigation-genericons a[href*="skype:"]:before { + content: "\f220"; +} + +/* Skype */ +.jetpack-social-navigation-genericons a[href*="sms:"]:before { + content: "\f110"; +} + +/* Spotify */ +.jetpack-social-navigation-genericons a[href*="spotify.com"]:before { + content: "\f515"; +} + +/* StumbleUpon */ +.jetpack-social-navigation-genericons a[href*="stumbleupon.com"]:before { + content: "\f223"; +} + +/* Threads */ +.jetpack-social-navigation-genericons a[href*="threads.net"]:before { + content: "\f10d"; +} + +/* Tumblr */ +.jetpack-social-navigation-genericons a[href*="tumblr.com"]:before { + content: "\f214"; +} + +/* Twitch */ +.jetpack-social-navigation-genericons a[href*="twitch.tv"]:before { + content: "\f516"; +} + +/* Twitter */ +.jetpack-social-navigation-genericons a[href*="twitter.com"]:before { + content: "\f202"; +} + +/* Vimeo */ +.jetpack-social-navigation-genericons a[href*="vimeo.com"]:before { + content: "\f212"; +} + +/* Vine */ +.jetpack-social-navigation-genericons a[href*="vine.co"]:before { + content: "\f517"; +} + +/* WordPress */ +.jetpack-social-navigation-genericons a[href*="wordpress.com"]:before, +.jetpack-social-navigation-genericons a[href*="wordpress.org"]:before { + content: "\f205"; +} + +/* X */ +.jetpack-social-navigation-genericons a[href*="x.com"]:before { + content: "\f10e"; +} + +/* YouTube */ +.jetpack-social-navigation-genericons a[href*="youtube.com"]:before { + content: "\f213"; +} diff --git a/projects/packages/classic-theme-helper/src/social-menu/social-menu.svg b/projects/packages/classic-theme-helper/src/social-menu/social-menu.svg new file mode 100644 index 0000000000000..6d68732322fd8 --- /dev/null +++ b/projects/packages/classic-theme-helper/src/social-menu/social-menu.svg @@ -0,0 +1,192 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/projects/packages/connection/.phpcs.dir.xml b/projects/packages/connection/.phpcs.dir.xml index e329e71fb8709..2bbec72e2e87d 100644 --- a/projects/packages/connection/.phpcs.dir.xml +++ b/projects/packages/connection/.phpcs.dir.xml @@ -5,14 +5,12 @@ - - diff --git a/projects/packages/connection/CHANGELOG.md b/projects/packages/connection/CHANGELOG.md index a48820a8beb43..bc52731f97123 100644 --- a/projects/packages/connection/CHANGELOG.md +++ b/projects/packages/connection/CHANGELOG.md @@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2.11.2] - 2024-07-22 +### Fixed +- Fixed textdomain on i18n messages imported from the IDC package. [#38412] + +## [2.11.1] - 2024-07-03 +### Changed +- Updated package dependencies. [#38132] + ## [2.11.0] - 2024-06-26 ### Added - Add blog_id to tracks data [#37902] @@ -1123,6 +1131,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Separate the connection library into its own package. +[2.11.2]: https://github.com/Automattic/jetpack-connection/compare/v2.11.1...v2.11.2 +[2.11.1]: https://github.com/Automattic/jetpack-connection/compare/v2.11.0...v2.11.1 [2.11.0]: https://github.com/Automattic/jetpack-connection/compare/v2.10.2...v2.11.0 [2.10.2]: https://github.com/Automattic/jetpack-connection/compare/v2.10.1...v2.10.2 [2.10.1]: https://github.com/Automattic/jetpack-connection/compare/v2.10.0...v2.10.1 diff --git a/projects/packages/connection/src/class-package-version.php b/projects/packages/connection/src/class-package-version.php index 3af7d9eb0cd1e..9662160145401 100644 --- a/projects/packages/connection/src/class-package-version.php +++ b/projects/packages/connection/src/class-package-version.php @@ -12,7 +12,7 @@ */ class Package_Version { - const PACKAGE_VERSION = '2.11.1-alpha'; + const PACKAGE_VERSION = '2.11.2'; const PACKAGE_SLUG = 'connection'; diff --git a/projects/packages/connection/src/identity-crisis/class-identity-crisis.php b/projects/packages/connection/src/identity-crisis/class-identity-crisis.php index 989e4264954d3..e19a57314c923 100644 --- a/projects/packages/connection/src/identity-crisis/class-identity-crisis.php +++ b/projects/packages/connection/src/identity-crisis/class-identity-crisis.php @@ -241,7 +241,7 @@ public function display_admin_bar_button() { $consumer_data = UI::get_consumer_data(); $label = isset( $consumer_data['customContent']['adminBarSafeModeLabel'] ) ? esc_html( $consumer_data['customContent']['adminBarSafeModeLabel'] ) - : esc_html__( 'Jetpack Safe Mode', 'jetpack-idc' ); + : esc_html__( 'Jetpack Safe Mode', 'jetpack-connection' ); $title = sprintf( '', @@ -448,7 +448,7 @@ public static function normalize_url_protocol_agnostic( $url ) { 'cannot_parse_url', sprintf( /* translators: %s: URL to parse. */ - esc_html__( 'Cannot parse URL %s', 'jetpack-idc' ), + esc_html__( 'Cannot parse URL %s', 'jetpack-connection' ), $url ) ); diff --git a/projects/packages/connection/src/identity-crisis/class-rest-endpoints.php b/projects/packages/connection/src/identity-crisis/class-rest-endpoints.php index 0f3842dbf72a4..b35478119b3f1 100644 --- a/projects/packages/connection/src/identity-crisis/class-rest-endpoints.php +++ b/projects/packages/connection/src/identity-crisis/class-rest-endpoints.php @@ -59,7 +59,7 @@ public static function initialize_rest_api() { 'permission_callback' => __CLASS__ . '::identity_crisis_mitigation_permission_check', 'args' => array( 'redirect_uri' => array( - 'description' => __( 'URI of the admin page where the user should be redirected after connection flow', 'jetpack-idc' ), + 'description' => __( 'URI of the admin page where the user should be redirected after connection flow', 'jetpack-connection' ), 'type' => 'string', ), ), @@ -98,7 +98,7 @@ public static function initialize_rest_api() { 'permission_callback' => array( static::class, 'compare_url_secret_permission_check' ), 'args' => array( 'secret' => array( - 'description' => __( 'URL secret to compare to the ones stored in the database.', 'jetpack-idc' ), + 'description' => __( 'URL secret to compare to the ones stored in the database.', 'jetpack-connection' ), 'type' => 'string', 'required' => true, ), @@ -127,7 +127,7 @@ public static function confirm_safe_mode() { return new WP_Error( 'error_setting_jetpack_safe_mode', - esc_html__( 'Could not confirm safe mode.', 'jetpack-idc' ), + esc_html__( 'Could not confirm safe mode.', 'jetpack-connection' ), array( 'status' => 500 ) ); } @@ -144,7 +144,7 @@ public static function migrate_stats_and_subscribers() { if ( Jetpack_Options::get_option( 'sync_error_idc' ) && ! Jetpack_Options::delete_option( 'sync_error_idc' ) ) { return new WP_Error( 'error_deleting_sync_error_idc', - esc_html__( 'Could not delete sync error option.', 'jetpack-idc' ), + esc_html__( 'Could not delete sync error option.', 'jetpack-connection' ), array( 'status' => 500 ) ); } @@ -158,7 +158,7 @@ public static function migrate_stats_and_subscribers() { } return new WP_Error( 'error_setting_jetpack_migrate', - esc_html__( 'Could not confirm migration.', 'jetpack-idc' ), + esc_html__( 'Could not confirm migration.', 'jetpack-connection' ), array( 'status' => 500 ) ); } @@ -221,7 +221,7 @@ public static function identity_crisis_mitigation_permission_check() { $error_msg = esc_html__( 'You do not have the correct user permissions to perform this action. Please contact your site admin if you think this is a mistake.', - 'jetpack-idc' + 'jetpack-connection' ); return new WP_Error( 'invalid_user_permission_identity_crisis', $error_msg, array( 'status' => rest_authorization_required_code() ) ); @@ -250,7 +250,7 @@ public static function fetch_url_secret() { $secret = new URL_Secret(); if ( ! $secret->exists() ) { - return new WP_Error( 'missing_url_secret', esc_html__( 'URL secret does not exist.', 'jetpack-idc' ) ); + return new WP_Error( 'missing_url_secret', esc_html__( 'URL secret does not exist.', 'jetpack-connection' ) ); } return rest_ensure_response( @@ -299,7 +299,7 @@ public static function url_secret_permission_check() { ? true : new WP_Error( 'invalid_user_permission_identity_crisis', - esc_html__( 'You do not have the correct user permissions to perform this action.', 'jetpack-idc' ), + esc_html__( 'You do not have the correct user permissions to perform this action.', 'jetpack-connection' ), array( 'status' => rest_authorization_required_code() ) ); } @@ -314,7 +314,7 @@ public static function compare_url_secret_permission_check() { return ( new Connection_Manager() )->is_connected() ? new WP_Error( 'invalid_connection_status', - esc_html__( 'The endpoint is not available on connected sites.', 'jetpack-idc' ), + esc_html__( 'The endpoint is not available on connected sites.', 'jetpack-connection' ), array( 'status' => 403 ) ) : true; diff --git a/projects/packages/connection/src/identity-crisis/class-ui.php b/projects/packages/connection/src/identity-crisis/class-ui.php index 98b3a52f38cbe..ee4c5acb4e880 100644 --- a/projects/packages/connection/src/identity-crisis/class-ui.php +++ b/projects/packages/connection/src/identity-crisis/class-ui.php @@ -64,7 +64,7 @@ public static function enqueue_scripts() { __FILE__, array( 'in_footer' => true, - 'textdomain' => 'jetpack-idc', + 'textdomain' => 'jetpack-connection', ) ); Assets::enqueue_script( 'jp_identity_crisis_banner' ); diff --git a/projects/packages/connection/src/identity-crisis/class-url-secret.php b/projects/packages/connection/src/identity-crisis/class-url-secret.php index c90be194073ef..e2ff3f8b47a12 100644 --- a/projects/packages/connection/src/identity-crisis/class-url-secret.php +++ b/projects/packages/connection/src/identity-crisis/class-url-secret.php @@ -89,7 +89,7 @@ public function create() { $result = Jetpack_Options::update_option( static::OPTION_KEY, $secret_data ); if ( ! $result ) { - throw new Exception( esc_html__( 'Unable to save new URL secret', 'jetpack-idc' ) ); + throw new Exception( esc_html__( 'Unable to save new URL secret', 'jetpack-connection' ) ); } $this->secret = $secret_data['secret']; diff --git a/projects/packages/explat/changelog/add-explat-authenticated-component b/projects/packages/explat/changelog/add-explat-authenticated-component new file mode 100644 index 0000000000000..216793470f6a6 --- /dev/null +++ b/projects/packages/explat/changelog/add-explat-authenticated-component @@ -0,0 +1,4 @@ +Significance: minor +Type: added + +Adds a new component to fetch experiments specifically for authenticated users diff --git a/projects/packages/explat/changelog/update-explat-experiment-fetching b/projects/packages/explat/changelog/update-explat-experiment-fetching new file mode 100644 index 0000000000000..98856b11cf2b2 --- /dev/null +++ b/projects/packages/explat/changelog/update-explat-experiment-fetching @@ -0,0 +1,4 @@ +Significance: patch +Type: changed + +ExPlat: add condition to prevent fetching the experiment assignment if there's not anon id (meaning that Tracks is likely disabled) diff --git a/projects/packages/explat/src/class-rest-controller.php b/projects/packages/explat/src/class-rest-controller.php index 2c32c9e67025a..9ee70dfaccc07 100644 --- a/projects/packages/explat/src/class-rest-controller.php +++ b/projects/packages/explat/src/class-rest-controller.php @@ -84,11 +84,8 @@ public function get_assignments( $request ) { if ( $request['as_connected_user'] && $is_user_connected ) { $response = Client::wpcom_json_api_request_as_user( - $request_path, - 'v2', - $args, - null, - 'wpcom' + add_query_arg( $args, $request_path ), + 'v2' ); } else { $response = wp_remote_get( diff --git a/projects/packages/explat/src/client/assignment.ts b/projects/packages/explat/src/client/assignment.ts index a4498e480e17f..80bd7d8f80b1c 100644 --- a/projects/packages/explat/src/client/assignment.ts +++ b/projects/packages/explat/src/client/assignment.ts @@ -1,19 +1,29 @@ import apiFetch from '@wordpress/api-fetch'; import { addQueryArgs } from '@wordpress/url'; -export const fetchExperimentAssignment = async ( { - experimentName, - anonId, -}: { - experimentName: string; - anonId: string | null; -} ): Promise< unknown > => { - const params = { - experiment_name: experimentName, - anon_id: anonId ?? undefined, - as_connected_user: false, +const fetchExperimentAssignment = + ( asConnectedUser = false ) => + async ( { + experimentName, + anonId, + }: { + experimentName: string; + anonId: string | null; + } ): Promise< unknown > => { + if ( ! anonId ) { + throw new Error( `Tracking is disabled, can't fetch experimentAssignment` ); + } + + const params = { + experiment_name: experimentName, + anon_id: anonId ?? undefined, + as_connected_user: asConnectedUser, + }; + + const assignmentsRequestUrl = addQueryArgs( 'jetpack/v4/explat/assignments', params ); + + return await apiFetch( { path: assignmentsRequestUrl } ); }; - const assignmentsRequestUrl = addQueryArgs( 'jetpack/v4/explat/assignments', params ); - return await apiFetch( { path: assignmentsRequestUrl } ); -}; +export const fetchExperimentAssignmentAnonymously = fetchExperimentAssignment( false ); +export const fetchExperimentAssignmentWithAuth = fetchExperimentAssignment( true ); diff --git a/projects/packages/explat/src/client/index.ts b/projects/packages/explat/src/client/index.ts index d71f799d41c2e..b21c993f02861 100644 --- a/projects/packages/explat/src/client/index.ts +++ b/projects/packages/explat/src/client/index.ts @@ -7,7 +7,10 @@ import createExPlatClientReactHelpers from '@automattic/explat-client-react-help * Internal dependencies */ import { getAnonId, initializeAnonId } from './anon'; -import { fetchExperimentAssignment } from './assignment'; +import { + fetchExperimentAssignmentAnonymously, + fetchExperimentAssignmentWithAuth, +} from './assignment'; import { logError } from './error'; import { isDevelopmentMode } from './utils'; @@ -18,7 +21,7 @@ export const initializeExPlat = (): void => { initializeExPlat(); const exPlatClient = createExPlatClient( { - fetchExperimentAssignment, + fetchExperimentAssignment: fetchExperimentAssignmentAnonymously, getAnonId, logError, isDevelopmentMode, @@ -28,3 +31,21 @@ export const { loadExperimentAssignment, dangerouslyGetExperimentAssignment } = export const { useExperiment, Experiment, ProvideExperimentData } = createExPlatClientReactHelpers( exPlatClient ); + +const exPlatClientWithAuth = createExPlatClient( { + fetchExperimentAssignment: fetchExperimentAssignmentWithAuth, + getAnonId, + logError, + isDevelopmentMode, +} ); + +export const { + loadExperimentAssignment: loadExperimentAssignmentWithAuth, + dangerouslyGetExperimentAssignment: dangerouslyGetExperimentAssignmentWithAuth, +} = exPlatClientWithAuth; + +export const { + useExperiment: useExperimentWithAuth, + Experiment: ExperimentWithAuth, + ProvideExperimentData: ProvideExperimentDataWithAuth, +} = createExPlatClientReactHelpers( exPlatClientWithAuth ); diff --git a/projects/packages/forms/CHANGELOG.md b/projects/packages/forms/CHANGELOG.md index 690cbd926da64..aedae914d2645 100644 --- a/projects/packages/forms/CHANGELOG.md +++ b/projects/packages/forms/CHANGELOG.md @@ -5,6 +5,19 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.32.5] - 2024-07-22 +### Fixed +- Block Picker: Fixed display of the picker in the block editor following changes in WordPress 6.6. [#38406] +- Form Submissions Table: Ensured the IP address is displayed for each submission, when available. [#38352] + +## [0.32.4] - 2024-07-15 +### Changed +- Internal updates. + +## [0.32.3] - 2024-07-08 +### Changed +- Updated package dependencies. [#38132] + ## [0.32.2] - 2024-06-24 ### Changed - Update dependencies. [#37979] @@ -598,6 +611,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added a new jetpack/forms package [#28409] - Added a public load_contact_form method for initializing the contact form module. [#28416] +[0.32.5]: https://github.com/automattic/jetpack-forms/compare/v0.32.4...v0.32.5 +[0.32.4]: https://github.com/automattic/jetpack-forms/compare/v0.32.3...v0.32.4 +[0.32.3]: https://github.com/automattic/jetpack-forms/compare/v0.32.2...v0.32.3 [0.32.2]: https://github.com/automattic/jetpack-forms/compare/v0.32.1...v0.32.2 [0.32.1]: https://github.com/automattic/jetpack-forms/compare/v0.32.0...v0.32.1 [0.32.0]: https://github.com/automattic/jetpack-forms/compare/v0.31.4...v0.32.0 diff --git a/projects/packages/forms/package.json b/projects/packages/forms/package.json index c3789e0e60336..ae8bc945f71b2 100644 --- a/projects/packages/forms/package.json +++ b/projects/packages/forms/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@automattic/jetpack-forms", - "version": "0.32.3-alpha", + "version": "0.32.5", "description": "Jetpack Forms", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/packages/forms/#readme", "bugs": { diff --git a/projects/packages/forms/src/blocks/.eslintrc.js b/projects/packages/forms/src/blocks/.eslintrc.js index 02e6db853d18e..840d017a63a3e 100644 --- a/projects/packages/forms/src/blocks/.eslintrc.js +++ b/projects/packages/forms/src/blocks/.eslintrc.js @@ -23,7 +23,6 @@ module.exports = { 'react/react-in-jsx-scope': 0, 'space-unary-ops': 0, 'space-before-function-paren': 0, - 'wpcalypso/jsx-classname-namespace': 0, // eslint 6.x migration 'react-hooks/rules-of-hooks': 1, diff --git a/projects/packages/forms/src/blocks/contact-form/child-blocks.js b/projects/packages/forms/src/blocks/contact-form/child-blocks.js index 440e2b2313214..d7b89470fbb7a 100644 --- a/projects/packages/forms/src/blocks/contact-form/child-blocks.js +++ b/projects/packages/forms/src/blocks/contact-form/child-blocks.js @@ -243,6 +243,7 @@ const OptionFieldDefaults = { supports: { reusable: false, html: false, + splitting: true, }, }; diff --git a/projects/packages/forms/src/blocks/contact-form/components/jetpack-field-multiple.js b/projects/packages/forms/src/blocks/contact-form/components/jetpack-field-multiple.js index 8087358fda37e..5b9d77179c705 100644 --- a/projects/packages/forms/src/blocks/contact-form/components/jetpack-field-multiple.js +++ b/projects/packages/forms/src/blocks/contact-form/components/jetpack-field-multiple.js @@ -62,7 +62,7 @@ function JetpackFieldMultiple( props ) {
diff --git a/projects/packages/forms/src/blocks/contact-form/components/jetpack-field-option.js b/projects/packages/forms/src/blocks/contact-form/components/jetpack-field-option.js index 0ab579d9de533..48b08ff3c8fad 100644 --- a/projects/packages/forms/src/blocks/contact-form/components/jetpack-field-option.js +++ b/projects/packages/forms/src/blocks/contact-form/components/jetpack-field-option.js @@ -1,18 +1,26 @@ -import { RichText } from '@wordpress/block-editor'; +import { RichText, useBlockProps } from '@wordpress/block-editor'; import { createBlock } from '@wordpress/blocks'; import { useDispatch, useSelect } from '@wordpress/data'; +import { useRef } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import clsx from 'clsx'; import { first } from 'lodash'; +import { useEffect } from 'react'; +import { supportsParagraphSplitting } from '../util/block-support'; import { useParentAttributes } from '../util/use-parent-attributes'; import { useJetpackFieldStyles } from './use-jetpack-field-styles'; -export const JetpackFieldOptionEdit = props => { - const { attributes, clientId, name, onReplace, setAttributes } = props; +export const JetpackFieldOptionEdit = ( { + isSelected, + attributes, + clientId, + name, + onReplace, + setAttributes, +} ) => { const { removeBlock } = useDispatch( 'core/block-editor' ); const parentAttributes = useParentAttributes( clientId ); const { optionStyle } = useJetpackFieldStyles( parentAttributes ); - const siblingsCount = useSelect( select => { const blockEditor = select( 'core/block-editor' ); @@ -21,17 +29,16 @@ export const JetpackFieldOptionEdit = props => { }, [ clientId ] ); + const blockProps = useBlockProps(); + const labelRef = useRef( null ); - const type = name.replace( 'jetpack/field-option-', '' ); - - const classes = clsx( 'jetpack-field-option', `field-option-${ type }` ); - - const handleSplit = label => - createBlock( name, { + const handleSplit = label => { + return createBlock( name, { ...attributes, clientId: label && attributes.label.indexOf( label ) === 0 ? attributes.clientId : undefined, label, } ); + }; const handleDelete = () => { if ( siblingsCount <= 1 ) { @@ -41,21 +48,46 @@ export const JetpackFieldOptionEdit = props => { removeBlock( clientId ); }; + const onFocus = () => { + // TODO: move cursor to end + }; + + useEffect( () => { + const element = labelRef.current; + + element?.addEventListener( 'focus', onFocus ); + + if ( isSelected ) { + // Timeout is necessary for the focus to be effective + setTimeout( () => element?.focus(), 0 ); + } + + return () => { + element?.removeEventListener( 'focus', onFocus ); + }; + }, [ isSelected, labelRef ] ); + + const supportsSplitting = supportsParagraphSplitting(); + const type = name.replace( 'jetpack/field-option-', '' ); + const classes = clsx( 'jetpack-field-option', `field-option-${ type }` ); + return ( -
+
{ - setAttributes( { label: value } ); - } } - onRemove={ handleDelete } - onSplit={ handleSplit } + onChange={ val => setAttributes( { label: val } ) } onReplace={ onReplace } - placeholder={ __( 'Add option…', 'jetpack-forms' ) } + onRemove={ handleDelete } preserveWhiteSpace={ false } withoutInteractiveFormatting - value={ attributes.label } + ref={ labelRef } + { ...( supportsSplitting ? {} : { onSplit: handleSplit } ) } />
); diff --git a/projects/packages/forms/src/blocks/contact-form/edit.js b/projects/packages/forms/src/blocks/contact-form/edit.js index afb53ddcb9070..e055c3bf60d1c 100644 --- a/projects/packages/forms/src/blocks/contact-form/edit.js +++ b/projects/packages/forms/src/blocks/contact-form/edit.js @@ -9,8 +9,8 @@ import { InnerBlocks, InspectorControls, URLInput, - __experimentalBlockVariationPicker as BlockVariationPicker, // eslint-disable-line wpcalypso/no-unsafe-wp-apis - __experimentalBlockPatternSetup as BlockPatternSetup, // eslint-disable-line wpcalypso/no-unsafe-wp-apis + __experimentalBlockVariationPicker as BlockVariationPicker, // eslint-disable-line @wordpress/no-unsafe-wp-apis + __experimentalBlockPatternSetup as BlockPatternSetup, // eslint-disable-line @wordpress/no-unsafe-wp-apis } from '@wordpress/block-editor'; import { createBlock, registerBlockVariation } from '@wordpress/blocks'; import { diff --git a/projects/packages/forms/src/blocks/contact-form/editor.scss b/projects/packages/forms/src/blocks/contact-form/editor.scss index 303db487ad3a6..55e98b23939b2 100644 --- a/projects/packages/forms/src/blocks/contact-form/editor.scss +++ b/projects/packages/forms/src/blocks/contact-form/editor.scss @@ -19,6 +19,8 @@ max-width: none; margin: 0; flex: 0; + text-align: center; + padding: 0 2px; } .block-editor-block-variation-picker__variation { @@ -175,6 +177,7 @@ .form-placeholder__external-link { font-size: 13px; padding: 0; + font-weight: 400; &, &:hover, &:active, &:focus { background-color: unset; diff --git a/projects/packages/forms/src/blocks/contact-form/util/block-support.js b/projects/packages/forms/src/blocks/contact-form/util/block-support.js new file mode 100644 index 0000000000000..932e8e5ba3bc3 --- /dev/null +++ b/projects/packages/forms/src/blocks/contact-form/util/block-support.js @@ -0,0 +1,10 @@ +import { hasBlockSupport } from '@wordpress/blocks'; + +/** + * Check if Gutenberg supports splitting paragraphs. + * + * @returns {boolean} Whether Gutenberg supports splitting paragraphs. + */ +export function supportsParagraphSplitting() { + return hasBlockSupport( 'core/paragraph', 'splitting', false ); +} diff --git a/projects/packages/forms/src/class-jetpack-forms.php b/projects/packages/forms/src/class-jetpack-forms.php index af9843b48efea..d6ade4c4627a5 100644 --- a/projects/packages/forms/src/class-jetpack-forms.php +++ b/projects/packages/forms/src/class-jetpack-forms.php @@ -15,7 +15,7 @@ */ class Jetpack_Forms { - const PACKAGE_VERSION = '0.32.3-alpha'; + const PACKAGE_VERSION = '0.32.5'; /** * Load the contact form module. diff --git a/projects/packages/forms/src/contact-form/class-admin.php b/projects/packages/forms/src/contact-form/class-admin.php index 83a428d658b2e..5d37d93caac14 100644 --- a/projects/packages/forms/src/contact-form/class-admin.php +++ b/projects/packages/forms/src/contact-form/class-admin.php @@ -738,6 +738,18 @@ public function grunion_manage_post_column_response( $post ) { } } + // Extract IP address if we still do not have it at this point. + if ( + ! isset( $content_fields['_feedback_ip'] ) + && is_array( $chunks ) + && ! empty( $chunks[0] ) + ) { + preg_match( '/^IP: (.+)$/m', $chunks[0], $matches ); + if ( ! empty( $matches[1] ) ) { + $content_fields['_feedback_ip'] = $matches[1]; + } + } + $response_fields = array_diff_key( $response_fields, array_flip( $non_printable_keys ) ); echo ''; @@ -757,7 +769,7 @@ public function grunion_manage_post_column_response( $post ) { echo '
'; echo ' ); } @@ -371,26 +381,6 @@ class Search extends React.Component {
); }; - - closeButton = () => { - if ( ! this.props.hideClose && ( this.state.keyword || this.state.isOpen ) ) { - return ( -
- -
- ); - } - - return null; - }; } export default Search; diff --git a/projects/plugins/jetpack/_inc/client/components/search/style.scss b/projects/plugins/jetpack/_inc/client/components/search/style.scss index 9538ff8f16e65..fa387a1dc26e9 100644 --- a/projects/plugins/jetpack/_inc/client/components/search/style.scss +++ b/projects/plugins/jetpack/_inc/client/components/search/style.scss @@ -27,6 +27,31 @@ background-color: $white; border-radius: inherit; height: 100%; + + &:focus-visible { + outline-offset: -2px; + } + } + + .dops-search__clear-btn { + min-width: 48px; + padding: 0.25em; + + background: none; + border: solid 1px currentColor; + border-radius: 4px; + color: var(--jp-gray-50); + + &:focus-visible { + outline-offset: 2px; + } + } + + .dops-search__icon-navigation, + .dops-search__clear-btn { + &:focus-visible { + outline: solid 2px var(--jp-green-50); + } } .dops-search__open-icon, @@ -68,7 +93,9 @@ top: 0; right: 0; overflow: hidden; - + gap: 0.25rem; + background-color: var(--jp-white); + .dops-search__input-fade { position: relative; flex: 1 1 auto; diff --git a/projects/plugins/jetpack/_inc/client/components/settings-group/style.scss b/projects/plugins/jetpack/_inc/client/components/settings-group/style.scss index 03d50cd33de0b..95a95ca777313 100644 --- a/projects/plugins/jetpack/_inc/client/components/settings-group/style.scss +++ b/projects/plugins/jetpack/_inc/client/components/settings-group/style.scss @@ -57,7 +57,7 @@ } .dops-card { - padding-right: rem( 48px ); + padding-right: rem( 72px ); } .jp-support-info { diff --git a/projects/plugins/jetpack/_inc/client/main.jsx b/projects/plugins/jetpack/_inc/client/main.jsx index 7a4105d8b8f57..2f38880c19e80 100644 --- a/projects/plugins/jetpack/_inc/client/main.jsx +++ b/projects/plugins/jetpack/_inc/client/main.jsx @@ -121,13 +121,13 @@ const recommendationsRoutes = [ '/recommendations/welcome-search', '/recommendations/welcome-scan', '/recommendations/welcome-social-basic', - '/recommendations/welcome-social-advanced', + '/recommendations/welcome-social-v1', '/recommendations/welcome-social-image-generator', '/recommendations/welcome-golden-token', '/recommendations/backup-activated', '/recommendations/scan-activated', '/recommendations/unlimited-sharing-activated', - '/recommendations/social-advanced-activated', + '/recommendations/social-v1-activated', '/recommendations/antispam-activated', '/recommendations/videopress-activated', '/recommendations/search-activated', @@ -485,7 +485,6 @@ class Main extends React.Component { See: https://www.scottohara.me/blog/2019/01/12/lists-and-safari.html */ }
    -
  • { __( 'Measure your impact with Jetpack Stats', 'jetpack' ) }
  • { __( 'Speed up your site with optimized images', 'jetpack' ) }
  • { __( 'Protect your site against bot attacks', 'jetpack' ) }
  • { __( 'Get notifications if your site goes offline', 'jetpack' ) }
  • @@ -599,12 +598,12 @@ class Main extends React.Component { case '/recommendations/welcome-search': case '/recommendations/welcome-scan': case '/recommendations/welcome-social-basic': - case '/recommendations/welcome-social-advanced': + case '/recommendations/welcome-social-v1': case '/recommendations/welcome-golden-token': case '/recommendations/backup-activated': case '/recommendations/scan-activated': case '/recommendations/unlimited-sharing-activated': - case '/recommendations/social-advanced-activated': + case '/recommendations/social-v1-activated': case '/recommendations/welcome-social-image-generator': case '/recommendations/antispam-activated': case '/recommendations/videopress-activated': diff --git a/projects/plugins/jetpack/_inc/client/newsletter/newsletter.jsx b/projects/plugins/jetpack/_inc/client/newsletter/newsletter.jsx index 4a0b5ae04ba23..48159eda50034 100644 --- a/projects/plugins/jetpack/_inc/client/newsletter/newsletter.jsx +++ b/projects/plugins/jetpack/_inc/client/newsletter/newsletter.jsx @@ -85,7 +85,10 @@ function Newsletter( props ) { toggleModule={ toggleModuleNow } > - { __( 'Let visitors subscribe to this site', 'jetpack' ) } + { __( + 'Let visitors subscribe to this site and receive emails when you publish a post', + 'jetpack' + ) } diff --git a/projects/plugins/jetpack/_inc/client/performance/speed-up-site.jsx b/projects/plugins/jetpack/_inc/client/performance/speed-up-site.jsx index ac70ec5df9109..58397bdb52529 100644 --- a/projects/plugins/jetpack/_inc/client/performance/speed-up-site.jsx +++ b/projects/plugins/jetpack/_inc/client/performance/speed-up-site.jsx @@ -12,7 +12,7 @@ import { connect } from 'react-redux'; import { isOfflineMode } from 'state/connection'; import { getModule, getModuleOverride } from 'state/modules'; import { isModuleFound as _isModuleFound } from 'state/search'; -import { getGutenbergState, isAtomicSite } from '../state/initial-state'; +import { isAtomicSite } from '../state/initial-state'; import { isPluginActive } from '../state/site/plugins'; const SpeedUpSite = withModuleSettingsFormHelpers( @@ -315,7 +315,6 @@ export default connect( state => { isModuleFound: module_name => _isModuleFound( state, module_name ), isOfflineMode: isOfflineMode( state ), getModuleOverride: module_name => getModuleOverride( state, module_name ), - gutenbergInfo: getGutenbergState( state ), isAtomicSite: isAtomicSite( state ), isPageOptimizeActive: isPluginActive( state, 'page-optimize/page-optimize.php' ), }; diff --git a/projects/plugins/jetpack/_inc/client/product-descriptions/constants.js b/projects/plugins/jetpack/_inc/client/product-descriptions/constants.js index 670d7fbd415b6..9d59eb5dedf66 100644 --- a/projects/plugins/jetpack/_inc/client/product-descriptions/constants.js +++ b/projects/plugins/jetpack/_inc/client/product-descriptions/constants.js @@ -8,6 +8,7 @@ export const PRODUCT_DESCRIPTION_PRODUCTS = { JETPACK_SECURITY: 'security', JETPACK_SOCIAL: 'social', JETPACK_VIDEOPRESS: 'videopress', + JETPACK_AI: 'jetpack-ai', }; export const productDescriptionRoutes = [ @@ -26,6 +27,7 @@ export const myJetpackRoutes = [ `/add-${ PRODUCT_DESCRIPTION_PRODUCTS.JETPACK_SEARCH }`, `/add-${ PRODUCT_DESCRIPTION_PRODUCTS.JETPACK_SOCIAL }`, `/add-${ PRODUCT_DESCRIPTION_PRODUCTS.JETPACK_VIDEOPRESS }`, + `/add-${ PRODUCT_DESCRIPTION_PRODUCTS.JETPACK_AI }`, ]; export const productIllustrations = { diff --git a/projects/plugins/jetpack/_inc/client/recommendations/constants.js b/projects/plugins/jetpack/_inc/client/recommendations/constants.js index 4a162606521cb..baef7d5f2610a 100644 --- a/projects/plugins/jetpack/_inc/client/recommendations/constants.js +++ b/projects/plugins/jetpack/_inc/client/recommendations/constants.js @@ -9,8 +9,8 @@ import { JETPACK_SEARCH_PRODUCTS, JETPACK_SCAN_PRODUCTS, JETPACK_GOLDEN_TOKEN_BUNDLES, - JETPACK_SOCIAL_ADVANCED_PRODUCTS, JETPACK_SOCIAL_BASIC_PRODUCTS, + JETPACK_SOCIAL_V1_PRODUCTS, } from 'lib/plans/constants'; export const RECOMMENDATION_WIZARD_STEP = { @@ -41,12 +41,12 @@ export const RECOMMENDATION_WIZARD_STEP = { WELCOME__SEARCH: 'welcome__search', WELCOME__SCAN: 'welcome__scan', WELCOME__SOCIAL_BASIC: 'welcome__social_basic', - WELCOME__SOCIAL_ADVANCED: 'welcome__social_advanced', + WELCOME__SOCIAL_V1: 'welcome__social_v1', WELCOME__SOCIAL_IMAGE_GENERATOR: 'welcome__social_image_generator', WELCOME__GOLDEN_TOKEN: 'welcome__golden_token', BACKUP_ACTIVATED: 'backup-activated', SCAN_ACTIVATED: 'scan-activated', - SOCIAL_ADVANCED_ACTIVATED: 'social-advanced-activated', + SOCIAL_V1_ACTIVATED: 'social-v1-activated', UNLIMITED_SHARING_ACTIVATED: 'unlimited-sharing-activated', ANTISPAM_ACTIVATED: 'antispam-activated', VIDEOPRESS_ACTIVATED: 'videopress-activated', @@ -67,7 +67,7 @@ export const ONBOARDING_JETPACK_VIDEOPRESS = 'JETPACK_VIDEOPRESS'; export const ONBOARDING_JETPACK_SEARCH = 'JETPACK_SEARCH'; export const ONBOARDING_JETPACK_SCAN = 'JETPACK_SCAN'; export const ONBOARDING_JETPACK_SOCIAL_BASIC = 'JETPACK_SOCIAL_BASIC'; -export const ONBOARDING_JETPACK_SOCIAL_ADVANCED = 'JETPACK_SOCIAL_ADVANCED'; +export const ONBOARDING_JETPACK_SOCIAL_V1 = 'JETPACK_SOCIAL_V1'; export const ONBOARDING_JETPACK_GOLDEN_TOKEN = 'JETPACK_GOLDEN_TOKEN'; export const ONBOARDING_SUPPORT_START_TIMESTAMP = 1664323200000; // 2022-09-28 @@ -83,7 +83,7 @@ export const ONBOARDING_ORDER = [ ONBOARDING_JETPACK_SEARCH, ONBOARDING_JETPACK_SCAN, ONBOARDING_JETPACK_SOCIAL_BASIC, - ONBOARDING_JETPACK_SOCIAL_ADVANCED, + ONBOARDING_JETPACK_SOCIAL_V1, ]; export const ONBOARDING_NAME_BY_PRODUCT_SLUG = { @@ -97,7 +97,7 @@ export const ONBOARDING_NAME_BY_PRODUCT_SLUG = { [ ONBOARDING_JETPACK_SEARCH ]: JETPACK_SEARCH_PRODUCTS, [ ONBOARDING_JETPACK_SCAN ]: JETPACK_SCAN_PRODUCTS, [ ONBOARDING_JETPACK_SOCIAL_BASIC ]: JETPACK_SOCIAL_BASIC_PRODUCTS, - [ ONBOARDING_JETPACK_SOCIAL_ADVANCED ]: JETPACK_SOCIAL_ADVANCED_PRODUCTS, + [ ONBOARDING_JETPACK_SOCIAL_V1 ]: JETPACK_SOCIAL_V1_PRODUCTS, }; export const SUMMARY_SECTION_BY_ONBOARDING_NAME = { @@ -109,7 +109,7 @@ export const SUMMARY_SECTION_BY_ONBOARDING_NAME = { 'antispam-activated', 'videopress-activated', 'unlimited-sharing-activated', - 'social-advanced-activated', + 'social-v1-activated', 'search-activated', ], }, @@ -145,11 +145,11 @@ export const SUMMARY_SECTION_BY_ONBOARDING_NAME = { name: 'Social Basic', slugs: [ RECOMMENDATION_WIZARD_STEP.UNLIMITED_SHARING_ACTIVATED ], }, - [ ONBOARDING_JETPACK_SOCIAL_ADVANCED ]: { - name: 'Social Advanced', + [ ONBOARDING_JETPACK_SOCIAL_V1 ]: { + name: 'Social', slugs: [ RECOMMENDATION_WIZARD_STEP.UNLIMITED_SHARING_ACTIVATED, - RECOMMENDATION_WIZARD_STEP.SOCIAL_ADVANCED_ACTIVATED, + RECOMMENDATION_WIZARD_STEP.SOCIAL_V1_ACTIVATED, ], }, [ ONBOARDING_JETPACK_GOLDEN_TOKEN ]: { diff --git a/projects/plugins/jetpack/_inc/client/recommendations/feature-utils.js b/projects/plugins/jetpack/_inc/client/recommendations/feature-utils.js index 20905cc7131e3..bfdac2b6b923b 100644 --- a/projects/plugins/jetpack/_inc/client/recommendations/feature-utils.js +++ b/projects/plugins/jetpack/_inc/client/recommendations/feature-utils.js @@ -165,7 +165,7 @@ export const getSummaryPrimaryProps = ( state, primarySlug ) => { ctaLabel: __( 'Manage', 'jetpack' ), ctaLink: getSiteAdminUrl( state ) + 'admin.php?page=jetpack#/sharing', }; - case 'social-advanced-activated': + case 'social-v1-activated': return { displayName: __( 'Advanced Sharing Features', 'jetpack' ), ctaLabel: __( 'Manage', 'jetpack' ), @@ -554,17 +554,20 @@ export const getStepContent = ( state, stepSlug ) => { illustration: 'assistant-jetpack-social', skipText: __( 'Next', 'jetpack' ), }; - case 'welcome__social_advanced': + case 'welcome__social_v1': return { question: __( 'Welcome to Jetpack Social!', 'jetpack' ), description: __( - "With your new advanced plan you unlocked unlimited sharing, access to upload photos and videos with your posts, and usage of Social Image Generator.

    Let's start with connecting your social media accounts, if you haven't already.", + "With your new Social plan you unlocked priority support, access to upload photos and videos with your posts, and usage of Social Image Generator.

    Let's start with connecting your social media accounts, if you haven't already.", 'jetpack' ), ctaText: __( 'Manage Social Media Connections', 'jetpack' ), - ctaLink: getRedirectUrl( 'calypso-marketing-connections', { - site: getSiteRawUrl( state ), - } ), + ctaLink: getSocialInitiaState( state ).useAdminUiV1 + ? getSiteAdminUrl( state ) + 'admin.php?page=jetpack#/sharing' + : getRedirectUrl( 'calypso-marketing-connections', { + site: getSiteRawUrl( state ), + } ), + ctaForceExternal: true, illustration: 'assistant-jetpack-social', skipText: __( 'Next', 'jetpack' ), }; @@ -577,6 +580,7 @@ export const getStepContent = ( state, stepSlug ) => { ), ctaText: __( 'View Jetpack Social settings', 'jetpack' ), ctaLink: getSiteAdminUrl( state ) + 'admin.php?page=jetpack#/sharing', + ctaForceExternal: true, illustration: 'assistant-social-image-post', skipText: __( 'Next', 'jetpack' ), }; @@ -631,11 +635,11 @@ export const getStepContent = ( state, stepSlug ) => { illustration: 'assistant-jetpack-social', skipText: __( 'Next', 'jetpack' ), }; - case 'social-advanced-activated': + case 'social-v1-activated': return { question: __( 'Advanced Sharing features', 'jetpack' ), description: __( - 'Use your unlocked unlimited sharing, upload photos and videos with your posts, and create previews with Social Image Generator. To use these features, just head to the post editor and start creating your post!

    You can manage your connections, and tweak features like Social Image Generator from the Jetpack Social Settings.', + 'Use your Social plan to upload photos and videos with your posts, and create previews with Social Image Generator. To use these features, just head to the post editor and start creating your post!

    You can manage your connections, and tweak features like Social Image Generator from the Jetpack Social Settings.', 'jetpack' ), ctaText: __( 'View Jetpack Social settings', 'jetpack' ), diff --git a/projects/plugins/jetpack/_inc/client/recommendations/index.jsx b/projects/plugins/jetpack/_inc/client/recommendations/index.jsx index 17810cfb45839..ea476fb8dfad6 100644 --- a/projects/plugins/jetpack/_inc/client/recommendations/index.jsx +++ b/projects/plugins/jetpack/_inc/client/recommendations/index.jsx @@ -148,8 +148,8 @@ const RecommendationsComponent = props => { case RECOMMENDATION_WIZARD_STEP.WELCOME__SOCIAL_BASIC: redirectPath = '/welcome-social-basic'; break; - case RECOMMENDATION_WIZARD_STEP.WELCOME__SOCIAL_ADVANCED: - redirectPath = '/welcome-social-advanced'; + case RECOMMENDATION_WIZARD_STEP.WELCOME__SOCIAL_V1: + redirectPath = '/welcome-social-v1'; break; case RECOMMENDATION_WIZARD_STEP.WELCOME__SOCIAL_IMAGE_GENERATOR: redirectPath = 'welcome-social-image-generator'; @@ -169,8 +169,8 @@ const RecommendationsComponent = props => { case RECOMMENDATION_WIZARD_STEP.UNLIMITED_SHARING_ACTIVATED: redirectPath = '/unlimited-sharing-activated'; break; - case RECOMMENDATION_WIZARD_STEP.SOCIAL_ADVANCED_ACTIVATED: - redirectPath = '/social-advanced-activated'; + case RECOMMENDATION_WIZARD_STEP.SOCIAL_V1_ACTIVATED: + redirectPath = '/social-v1-activated'; break; case RECOMMENDATION_WIZARD_STEP.ANTISPAM_ACTIVATED: redirectPath = '/antispam-activated'; @@ -295,8 +295,8 @@ const RecommendationsComponent = props => { - - + + @@ -313,8 +313,8 @@ const RecommendationsComponent = props => { - - + + diff --git a/projects/plugins/jetpack/_inc/client/recommendations/prompts/resource-prompt/index.jsx b/projects/plugins/jetpack/_inc/client/recommendations/prompts/resource-prompt/index.jsx index bf139576a324d..37a82ecd15918 100644 --- a/projects/plugins/jetpack/_inc/client/recommendations/prompts/resource-prompt/index.jsx +++ b/projects/plugins/jetpack/_inc/client/recommendations/prompts/resource-prompt/index.jsx @@ -45,6 +45,7 @@ const ResourcePromptComponent = props => { illustration, ctaText, ctaLink, + ctaForceExternal, hasNoAction, skipText, stepSlug, @@ -119,7 +120,8 @@ const ResourcePromptComponent = props => { return null; }, [ stepProgressValue, progressValue ] ); - const ctaLinkIsExternal = ctaLink?.match( /^https:\/\/jetpack.com\/redirect/ ); + const ctaLinkIsExternal = + ctaLink?.match( /^https:\/\/jetpack.com\/redirect/ ) || ctaForceExternal; return ( { + // Sync the form values with the settings prop. + if ( this.props.settings !== prevProps.settings ) { + this.setState( { + ...this.state, + ipAllowListEnabled: this.props.settings?.ipAllowListEnabled, + ipAllowList: this.props.settings?.ipAllowList, + } ); + } + }; + + /** + * Handle settings updates. + * + * @returns {void} + */ + onSubmit = () => { + this.props.removeNotice( 'module-setting-update' ); + this.props.removeNotice( 'module-setting-update-success' ); + + this.props.createNotice( 'is-info', __( 'Updating settings…', 'jetpack' ), { + id: 'module-setting-update', + } ); + + this.props + .updateWafSettings( this.state ) + .then( () => { + this.props.removeNotice( 'module-setting-update' ); + this.props.createNotice( 'is-success', __( 'Updated Settings.', 'jetpack' ), { + id: 'module-setting-update-success', + } ); + } ) + .catch( error => { + this.props.removeNotice( 'module-setting-update' ); + this.props.createNotice( + 'is-error', + sprintf( + /* translators: placeholder is an error code or an error message. */ + __( 'Error updating settings. %s', 'jetpack' ), + error.message || error.code + ), + { + id: 'module-setting-update', + } + ); + } ); + }; + + /** + * Toggle IP allow list. + */ + toggleIpAllowList = () => { + this.setState( + { ...this.state, ipAllowListEnabled: ! this.state.ipAllowListEnabled }, + this.onSubmit + ); + }; + + /** + * Handle IP allow list change. + * + * @param {Event} event - = The event object. + */ + handleIpAllowListChange = event => { + this.setState( { ...this.state, ipAllowList: event?.target?.value } ); + }; + + currentIpIsSafelisted = () => { + // get current IP allow list in textarea from this.state.ipAllowList; + return !! includes( this.state.ipAllowList, this.props.currentIp ); + }; + + addToSafelist = () => { + const newAllowList = + this.state.ipAllowList + + ( 0 >= this.state.ipAllowList.length ? '' : '\n' ) + + this.props.currentIp; + + // Update the allow list + this.setState( { ...this.state, ipAllowList: newAllowList } ); + }; + + render() { + const isWafActive = this.props.getOptionValue( 'waf' ); + const isProtectActive = this.props.getOptionValue( 'protect' ); + const wafUnavailableInOfflineMode = this.props.isUnavailableInOfflineMode( 'waf' ); + const protectUnavailableInOfflineMode = this.props.isUnavailableInOfflineMode( 'protect' ); + const baseInputDisabledCase = + ( ! isWafActive && ! isProtectActive ) || + ( wafUnavailableInOfflineMode && protectUnavailableInOfflineMode ) || + this.props.isFetchingWafSettings || + this.props.isSavingAnyOption( [ 'waf' ] ); + + const moduleHeader = ( +
    + { _x( 'Always allowed IP addresses', 'Settings header', 'jetpack' ) } +
    + ); + + return ( + + { ( isWafActive || isProtectActive ) && } + + +
    + + { __( + "Prevent Jetpack's security features from blocking specific IP addresses", + 'jetpack' + ) } + + } + /> +
    +