diff --git a/.github/ISSUE_TEMPLATE/release-template.md b/.github/ISSUE_TEMPLATE/release-template.md index b300f7dc1b..151e2379f9 100644 --- a/.github/ISSUE_TEMPLATE/release-template.md +++ b/.github/ISSUE_TEMPLATE/release-template.md @@ -5,9 +5,9 @@ title: Release wp-parsely x.y.z labels: "wp-parsely, Maintenance" --- -This is an issue for tracking the next `wp-parsely` release. This ticket is to be opened the week before the actual release, so we have enough time to complete all the related tasks. +This is an issue for tracking the next `wp-parsely` release (milestone: [x.y.z](https://github.com/Parsely/wp-parsely/milestone/milestone-id?closed=1)). -The actual release of the plugin should be done on Mondays so we can catch the Tuesday WordPress VIP release window. +This ticket is to be opened the week before the actual release, so we have enough time to complete all the related tasks. The actual release of the plugin should be done on Mondays so we can catch the Tuesday WordPress VIP release window. ## Before releasing diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index ff390794bd..97dc284efd 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -29,7 +29,7 @@ jobs: # - coverage: Whether to run the tests with code coverage. # - experimental: Whether the build is "allowed to fail". matrix: - php: [ '7.2', '7.3', '7.4', '8.0', '8.1', '8.2'] + php: ['7.4', '8.0', '8.1', '8.2'] wp: ['latest'] coverage: [none] experimental: [false] @@ -81,10 +81,6 @@ jobs: - name: Start MySQL Service run: sudo systemctl start mysql.service - - name: Setting mysql_native_password for PHP <= 7.3 - if: ${{ matrix.php <= 7.3 }} - run: mysql -u root -proot -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'root';" - - name: Prepare environment for integration tests run: composer prepare-ci --no-interaction diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index ebaac433c1..cf9c796f58 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -30,7 +30,7 @@ jobs: # - coverage: Whether to run the tests with code coverage. # - experimental: Whether the build is "allowed to fail". matrix: - php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2'] + php: ['7.4', '8.0', '8.1', '8.2'] coverage: [none] experimental: [false] include: diff --git a/.nvmrc b/.nvmrc index 744ca17ec0..5d621bb2fe 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -22.14 +22.15 diff --git a/.phpcs.xml.dist b/.phpcs.xml.dist index 7aa088a3bf..f6cd7902cf 100644 --- a/.phpcs.xml.dist +++ b/.phpcs.xml.dist @@ -32,7 +32,7 @@ - + diff --git a/.wordpress-org/screenshot-3.png b/.wordpress-org/screenshot-3.png index 37566882a1..bd0ccaffba 100644 Binary files a/.wordpress-org/screenshot-3.png and b/.wordpress-org/screenshot-3.png differ diff --git a/.wp-env.json b/.wp-env.json index dafab87e0a..35b338edaf 100644 --- a/.wp-env.json +++ b/.wp-env.json @@ -1,5 +1,10 @@ { "plugins": [ "." - ] + ], + "env": { + "development": { + "phpmyadminPort": 9000 + } + } } diff --git a/CHANGELOG.md b/CHANGELOG.md index 8091b69630..02a32e3265 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,17 @@ 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.20.0](https://github.com/Parsely/wp-parsely/compare/3.19.3...3.20.0) - 2025-06-18 + +### Changed + +- Add floating Traffic Boost actions ([#3445](https://github.com/Parsely/wp-parsely/pull/3445)) +- Upgrade minimum PHP version to 7.4 ([#3419](https://github.com/Parsely/wp-parsely/pull/3419)) + +### Dependency Updates + +- The list of all dependency updates for this release is available [here](https://github.com/Parsely/wp-parsely/pulls?q=is%3Apr+is%3Amerged+milestone%3A3.20.0+label%3ADeps). + ## [3.19.3](https://github.com/Parsely/wp-parsely/compare/3.19.2...3.19.3) - 2025-06-03 ### Dependency Updates diff --git a/README.md b/README.md index c7a632be8d..c48b47ff0a 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # Parse.ly -Stable tag: 3.19.3 +Stable tag: 3.20.0 Requires at least: 6.0 Tested up to: 6.8 -Requires PHP: 7.2 +Requires PHP: 7.4 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html Tags: analytics, statistics, stats, content marketing, parsely, parsley, parse.ly diff --git a/build/content-helper/dashboard-page-rtl.css b/build/content-helper/dashboard-page-rtl.css index 296e193495..77e1a305bf 100644 --- a/build/content-helper/dashboard-page-rtl.css +++ b/build/content-helper/dashboard-page-rtl.css @@ -1,6 +1,6 @@ #wp-parsely-dashboard-widget,.components-button[aria-controls="wp-parsely-block-editor-sidebar:wp-parsely-content-helper"],.editor-post-excerpt,.parsely-dashboard-container,.settings_page_parsely,.wp-parsely-block-overlay,.wp-parsely-content-helper,.wp-parsely-excerpt-generator,.wp-parsely-panel,.wp-parsely-popover,.wp-parsely-preview-editor,.wp-parsely-smart-linking-close-dialog,.wp-parsely-smart-linking-review-modal,.wp-parsely-suggested-title-modal,.wp-parsely-traffic-boost-add-new-link-popover,.wp-parsely-traffic-boost-preview-settings-popover{--base-font:"source-sans-pro",arial,sans-serif;--numeric-font:"ff-din-round-web",sans-serif;--parsely-green-components:107,42%,46%;--parsely-green:hsl(var(--parsely-green-components));--parsely-green-10:#c7ecb1;--parsely-green-65:#2a691b;--gray-200:#f7f8f9;--gray-300:#edeeef;--gray-400:#d7dbdf;--gray-500:#959da5;--gray-600:#586069;--gray-700:#444d56;--gray-900:#24292e;--blue-500:#44a8e5;--blue-550:#2596db;--green-500:#7bc01b;--green-900:#3d6307;--ref-direct:205,13%,52%;--ref-internal:161,91%,41%;--ref-social:210,72%,41%;--ref-search:42,100%,50%;--ref-other:3,76%,58%;--base-text:var(--gray-900);--base-text-2:var(--gray-600);--base-3:var(--gray-400);--border:var(--gray-400);--data:var(--green-500);--control:var(--blue-500);--grid-unit-5:0.25rem;--grid-unit-10:0.5rem;--grid-unit-15:0.75rem;--grid-unit-20:1rem;--grid-unit-25:1.25rem;--grid-unit-30:1.5rem;--grid-unit-40:2rem;--grid-unit-50:2.5rem;--grid-unit-60:3rem;--font-size--smaller:0.688rem;--font-size--small:0.75rem;--font-size--medium:0.875rem;--font-size--large:1rem;--font-size--extra-large:1.2rem;--font-size--bigger:2rem;--dashboard-width:64rem;--black:#000;--dashboard-accent:#b9f16b;--gray-350:#e1e3e5;--sidebar-black:#1e1e1e;--sidebar-white:#f0f0f0;--traffic-boost-header-background:rgba(56,88,233,.08);--white:#fff}.parsely-menu-page-dashboard .parsely-dashboard-page-body,.parsely-menu-page-dashboard .parsely-dashboard-page-header{padding:var(--grid-unit-40) var(--grid-unit-40) var(--grid-unit-60) var(--grid-unit-40)}.parsely-menu-page-dashboard .dashboard-header-background{background:linear-gradient(-303deg,#000 30.51%,#487006 133.77%)}.parsely-menu-page-dashboard .dashboard-header{align-items:flex-start;display:flex;flex-wrap:wrap;gap:var(--grid-unit-25);justify-content:space-between}.parsely-menu-page-dashboard .dashboard-header .dashboard-header-summary{align-items:flex-start;color:var(--sidebar-white);display:flex;flex-direction:column;gap:var(--grid-unit-30);width:18.75rem}.parsely-menu-page-dashboard .dashboard-header .dashboard-header-summary .summary-info{align-items:flex-start;align-self:stretch;display:flex;flex-direction:column;gap:var(--grid-unit-10)}.parsely-menu-page-dashboard .dashboard-header .dashboard-header-summary .summary-info .summary-title{align-self:stretch;font-size:var(--font-size--bigger);font-style:normal;font-weight:600;line-height:2.8125rem}.parsely-menu-page-dashboard .dashboard-header .dashboard-header-summary .summary-info .summary-text{align-self:stretch;font-size:var(--font-size--small);font-style:normal;font-weight:400;line-height:var(--font-size--large)}.parsely-menu-page-dashboard .dashboard-header .dashboard-header-summary .summary-button button{background:transparent;border:1px solid var(--sidebar-white);box-shadow:none;color:var(--sidebar-white);cursor:pointer;margin-top:var(--grid-unit-20);padding:var(--grid-unit-10) var(--grid-unit-20)}.parsely-menu-page-dashboard .dashboard-header .dashboard-header-summary .summary-button button:hover{background:var(--sidebar-white);box-shadow:none;color:var(--sidebar-black)}.parsely-menu-page-dashboard .dashboard-header .dashboard-header-stats{align-content:flex-start;align-items:flex-start;align-self:stretch;display:flex;flex-wrap:wrap;gap:var(--grid-unit-30);justify-content:flex-end;width:31.25rem}.parsely-menu-page-dashboard .dashboard-header .dashboard-header-stats .header-stat-card{align-items:flex-start;color:var(--sidebar-white);display:flex;flex-direction:column;gap:var(--grid-unit-05);height:var(--grid-unit-60);justify-content:center;width:9.375rem}.parsely-menu-page-dashboard .dashboard-header .dashboard-header-stats .header-stat-card .card-title{align-items:center;display:flex;font-size:var(--font-size--small);font-style:normal;font-weight:400;gap:var(--grid-unit-05);line-height:var(--font-size--large)}.parsely-menu-page-dashboard .dashboard-header .dashboard-header-stats .header-stat-card .card-title svg{fill:var(--sidebar-white)}.parsely-menu-page-dashboard .dashboard-header .dashboard-header-stats .header-stat-card .card-value{align-items:center;display:flex;font-size:1.5rem;font-style:normal;font-weight:600;gap:var(--grid-unit-05);letter-spacing:-.48px;line-height:2rem}.parsely-menu-page-dashboard .dashboard-header .dashboard-header-stats .header-stat-card .card-value .card-change{font-size:var(--font-size--medium);font-style:normal;font-weight:500;line-height:var(--font-size--large)}.parsely-menu-page-dashboard .dashboard-header .dashboard-header-stats .stats-top{align-items:flex-start;border-bottom:1px solid var(--sidebar-white);display:flex;gap:var(--grid-unit-30);padding-bottom:var(--grid-unit-30)}.parsely-menu-page-dashboard .dashboard-header .dashboard-header-stats .stats-top .card-title{color:var(--dashboard-accent)}.parsely-menu-page-dashboard .dashboard-header .dashboard-header-stats .stats-top .card-title svg{fill:var(--dashboard-accent)}.parsely-menu-page-dashboard .dashboard-header .dashboard-header-stats .stats-top .card-value{color:var(--dashboard-accent)}.parsely-menu-page-dashboard .dashboard-header .dashboard-header-stats .stats-top .stat-intro{justify-content:flex-start}.parsely-menu-page-dashboard .dashboard-header .dashboard-header-stats .stats-top .stat-intro .card-title{color:var(--sidebar-white);font-size:var(--font-size--medium);font-style:normal;font-weight:500;line-height:var(--font-size--extra-large)} #wp-parsely-dashboard-widget,.components-button[aria-controls="wp-parsely-block-editor-sidebar:wp-parsely-content-helper"],.editor-post-excerpt,.parsely-dashboard-container,.settings_page_parsely,.wp-parsely-block-overlay,.wp-parsely-content-helper,.wp-parsely-excerpt-generator,.wp-parsely-panel,.wp-parsely-popover,.wp-parsely-preview-editor,.wp-parsely-smart-linking-close-dialog,.wp-parsely-smart-linking-review-modal,.wp-parsely-suggested-title-modal,.wp-parsely-traffic-boost-add-new-link-popover,.wp-parsely-traffic-boost-preview-settings-popover{--base-font:"source-sans-pro",arial,sans-serif;--numeric-font:"ff-din-round-web",sans-serif;--parsely-green-components:107,42%,46%;--parsely-green:hsl(var(--parsely-green-components));--parsely-green-10:#c7ecb1;--parsely-green-65:#2a691b;--gray-200:#f7f8f9;--gray-300:#edeeef;--gray-400:#d7dbdf;--gray-500:#959da5;--gray-600:#586069;--gray-700:#444d56;--gray-900:#24292e;--blue-500:#44a8e5;--blue-550:#2596db;--green-500:#7bc01b;--green-900:#3d6307;--ref-direct:205,13%,52%;--ref-internal:161,91%,41%;--ref-social:210,72%,41%;--ref-search:42,100%,50%;--ref-other:3,76%,58%;--base-text:var(--gray-900);--base-text-2:var(--gray-600);--base-3:var(--gray-400);--border:var(--gray-400);--data:var(--green-500);--control:var(--blue-500);--grid-unit-5:0.25rem;--grid-unit-10:0.5rem;--grid-unit-15:0.75rem;--grid-unit-20:1rem;--grid-unit-25:1.25rem;--grid-unit-30:1.5rem;--grid-unit-40:2rem;--grid-unit-50:2.5rem;--grid-unit-60:3rem;--font-size--smaller:0.688rem;--font-size--small:0.75rem;--font-size--medium:0.875rem;--font-size--large:1rem;--font-size--extra-large:1.2rem;--font-size--bigger:2rem;--dashboard-width:64rem;--black:#000;--dashboard-accent:#b9f16b;--gray-350:#e1e3e5;--sidebar-black:#1e1e1e;--sidebar-white:#f0f0f0;--traffic-boost-header-background:rgba(56,88,233,.08);--white:#fff}.parsely-menu-page-traffic-boost .parsely-dashboard-page-header{padding:var(--grid-unit-30)}.parsely-menu-page-traffic-boost .traffic-boost-search-container{border-radius:2px;margin-bottom:0;padding:var(--grid-unit-15) 0}.parsely-menu-page-traffic-boost-single-post{align-items:flex-start;align-self:stretch;display:flex;flex:1 0 0;gap:var(--grid-unit-20);padding:var(--grid-unit-20) 0 var(--grid-unit-20) var(--grid-unit-20);position:relative}.wp-parsely-snackbar-notices.traffic-boost-snackbar-notices{padding-right:30rem} -#wp-parsely-dashboard-widget,.components-button[aria-controls="wp-parsely-block-editor-sidebar:wp-parsely-content-helper"],.editor-post-excerpt,.parsely-dashboard-container,.settings_page_parsely,.wp-parsely-block-overlay,.wp-parsely-content-helper,.wp-parsely-excerpt-generator,.wp-parsely-panel,.wp-parsely-popover,.wp-parsely-preview-editor,.wp-parsely-smart-linking-close-dialog,.wp-parsely-smart-linking-review-modal,.wp-parsely-suggested-title-modal,.wp-parsely-traffic-boost-add-new-link-popover,.wp-parsely-traffic-boost-preview-settings-popover{--base-font:"source-sans-pro",arial,sans-serif;--numeric-font:"ff-din-round-web",sans-serif;--parsely-green-components:107,42%,46%;--parsely-green:hsl(var(--parsely-green-components));--parsely-green-10:#c7ecb1;--parsely-green-65:#2a691b;--gray-200:#f7f8f9;--gray-300:#edeeef;--gray-400:#d7dbdf;--gray-500:#959da5;--gray-600:#586069;--gray-700:#444d56;--gray-900:#24292e;--blue-500:#44a8e5;--blue-550:#2596db;--green-500:#7bc01b;--green-900:#3d6307;--ref-direct:205,13%,52%;--ref-internal:161,91%,41%;--ref-social:210,72%,41%;--ref-search:42,100%,50%;--ref-other:3,76%,58%;--base-text:var(--gray-900);--base-text-2:var(--gray-600);--base-3:var(--gray-400);--border:var(--gray-400);--data:var(--green-500);--control:var(--blue-500);--grid-unit-5:0.25rem;--grid-unit-10:0.5rem;--grid-unit-15:0.75rem;--grid-unit-20:1rem;--grid-unit-25:1.25rem;--grid-unit-30:1.5rem;--grid-unit-40:2rem;--grid-unit-50:2.5rem;--grid-unit-60:3rem;--font-size--smaller:0.688rem;--font-size--small:0.75rem;--font-size--medium:0.875rem;--font-size--large:1rem;--font-size--extra-large:1.2rem;--font-size--bigger:2rem;--dashboard-width:64rem;--black:#000;--dashboard-accent:#b9f16b;--gray-350:#e1e3e5;--sidebar-black:#1e1e1e;--sidebar-white:#f0f0f0;--traffic-boost-header-background:rgba(56,88,233,.08);--white:#fff}.traffic-boost-preview{align-self:stretch;background-color:var(--sidebar-white);border-radius:.5rem;display:flex;flex:1 0 0;flex-direction:column;min-width:18.75rem;overflow:hidden;padding:0}.traffic-boost-preview .wp-parsely-preview{background:var(--sidebar-white);display:flex;flex:1;height:100%;overflow:hidden}.traffic-boost-preview .wp-parsely-preview .preview-iframe-wrapper{height:100%;overflow:hidden;position:relative;width:100%}.traffic-boost-preview .wp-parsely-preview .wp-parsely-preview-iframe{background:var(--sidebar-white);border:none;height:100%;right:0;overflow-x:hidden;overflow-y:auto;position:absolute;top:0;transition:all .5s ease;width:100%;z-index:0}.traffic-boost-preview .wp-parsely-preview .wp-parsely-preview-iframe.is-loading{filter:blur(4px);opacity:.7}.traffic-boost-preview .wp-parsely-preview .wp-parsely-preview-loading{align-items:center;background-color:var(--sidebar-white);display:flex;inset:0;justify-content:center;opacity:0;pointer-events:none;position:absolute;transition:opacity .1s ease-in-out,visibility .1s ease-in-out;visibility:hidden;z-index:3}.traffic-boost-preview .wp-parsely-preview .wp-parsely-preview-loading.is-loading{align-items:center;display:flex;flex-direction:column;font-size:var(--font-size--extra-large);font-weight:500;gap:var(--grid-unit-30);justify-content:center;line-height:var(--font-size--extra-large);opacity:1;visibility:visible}.traffic-boost-preview .wp-parsely-preview .wp-parsely-preview-loading.is-loading .wp-parsely-loading{width:90%}.traffic-boost-preview .wp-parsely-preview .wp-parsely-preview-loading.is-loading .wp-parsely-loading .wp-parsely-loading-message{max-width:37.5rem;text-align:center}.traffic-boost-preview .wp-parsely-preview .wp-parsely-preview-loading .components-spinner{height:var(--grid-unit-40);margin:0;width:var(--grid-unit-40)}.traffic-boost-preview .wp-parsely-preview .wp-parsely-preview-generating-placement{align-items:center;display:flex;inset:0;justify-content:center;pointer-events:none;position:absolute;z-index:3}.traffic-boost-preview-header{align-items:center;background:var(--white);border-bottom:1px solid var(--gray-400);display:flex;justify-content:space-between;padding:var(--grid-unit-15) var(--grid-unit-20);z-index:5}.traffic-boost-preview-header .traffic-boost-preview-actions{display:flex;gap:var(--grid-unit-10)}.traffic-boost-preview-header .traffic-boost-preview-actions button{border-radius:.25rem;height:1.875rem;min-width:1.875rem;padding:var(--grid-unit-5)}.traffic-boost-preview-header .traffic-boost-preview-actions button:hover{color:var(--wp-admin-theme-color)}.traffic-boost-preview-header .traffic-boost-preview-actions .components-button.is-pressed:focus:not(:disabled){box-shadow:none}.traffic-boost-preview-header .traffic-boost-preview-info{align-items:flex-start;display:flex;flex:1 0 0;flex-direction:column;gap:var(--grid-unit-5);min-width:0}.traffic-boost-preview-header .traffic-boost-preview-info .traffic-boost-preview-info-title{color:var(--gray-900);font-size:var(--font-size--large);font-weight:500;line-height:var(--font-size--extra-large);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;width:100%}.traffic-boost-preview-header .traffic-boost-preview-info .traffic-boost-preview-info-links{align-items:center;align-self:stretch;display:flex;font-size:var(--font-size--medium);font-weight:400;gap:var(--grid-unit-10);line-height:var(--font-size--extra-large);min-height:var(--grid-unit-30)}.traffic-boost-preview-header .traffic-boost-preview-info .traffic-boost-preview-info-links .traffic-boost-preview-info-links-counter{align-items:center;align-self:stretch;display:flex;gap:var(--grid-unit-10)}.traffic-boost-preview-header .traffic-boost-preview-info .traffic-boost-preview-info-links .traffic-boost-preview-info-links-counter button{height:1.375rem;padding:var(--grid-unit-5) var(--grid-unit-10)}.traffic-boost-preview-header .traffic-boost-preview-info .traffic-boost-preview-info-links .traffic-boost-preview-info-links-counter button.is-pressed,.traffic-boost-preview-header .traffic-boost-preview-info .traffic-boost-preview-info-links .traffic-boost-preview-info-links-counter button:focus{box-shadow:0 0 0 1px var(--black);outline:none}.traffic-boost-preview-header .traffic-boost-preview-info .traffic-boost-preview-info-links .traffic-boost-preview-info-links-counter button:not(.is-pressed):focus{box-shadow:inset 0 0 0 1px var(--wp-admin-theme-color);color:var(--wp-admin-theme-color)}.wp-parsely-traffic-boost-preview-settings-popover{margin-top:var(--grid-unit-20);width:17.1875rem}.wp-parsely-traffic-boost-preview-settings-popover-content{min-width:13.75rem;padding:var(--grid-unit-20);width:17.1875rem}.traffic-boost-preview-footer{align-items:center;background:var(--white);border-top:1px solid var(--gray-400);display:flex;justify-content:space-between;padding:var(--grid-unit-10) var(--grid-unit-20)}.traffic-boost-preview-footer .traffic-boost-preview-footer-next,.traffic-boost-preview-footer .traffic-boost-preview-footer-previous{min-width:2.25rem}.traffic-boost-preview-footer .traffic-boost-preview-footer-actions{align-items:center;display:flex;flex-wrap:wrap;gap:var(--grid-unit-10);justify-content:center}.traffic-boost-preview-footer .traffic-boost-preview-footer-actions .components-button{height:2.25rem}.traffic-boost-preview-footer .traffic-boost-preview-footer-actions .traffic-boost-preview-footer-navigation{align-items:center;display:flex;font-size:var(--font-size--small);font-weight:500;gap:var(--grid-unit-5);line-height:var(--font-size--medium);text-transform:uppercase}.traffic-boost-preview-footer .traffic-boost-preview-footer-actions .traffic-boost-preview-footer-navigation select{border:none;font-size:var(--font-size--small);font-weight:500;line-height:var(--font-size--medium);margin-bottom:2px} +#wp-parsely-dashboard-widget,.components-button[aria-controls="wp-parsely-block-editor-sidebar:wp-parsely-content-helper"],.editor-post-excerpt,.parsely-dashboard-container,.settings_page_parsely,.wp-parsely-block-overlay,.wp-parsely-content-helper,.wp-parsely-excerpt-generator,.wp-parsely-panel,.wp-parsely-popover,.wp-parsely-preview-editor,.wp-parsely-smart-linking-close-dialog,.wp-parsely-smart-linking-review-modal,.wp-parsely-suggested-title-modal,.wp-parsely-traffic-boost-add-new-link-popover,.wp-parsely-traffic-boost-preview-settings-popover{--base-font:"source-sans-pro",arial,sans-serif;--numeric-font:"ff-din-round-web",sans-serif;--parsely-green-components:107,42%,46%;--parsely-green:hsl(var(--parsely-green-components));--parsely-green-10:#c7ecb1;--parsely-green-65:#2a691b;--gray-200:#f7f8f9;--gray-300:#edeeef;--gray-400:#d7dbdf;--gray-500:#959da5;--gray-600:#586069;--gray-700:#444d56;--gray-900:#24292e;--blue-500:#44a8e5;--blue-550:#2596db;--green-500:#7bc01b;--green-900:#3d6307;--ref-direct:205,13%,52%;--ref-internal:161,91%,41%;--ref-social:210,72%,41%;--ref-search:42,100%,50%;--ref-other:3,76%,58%;--base-text:var(--gray-900);--base-text-2:var(--gray-600);--base-3:var(--gray-400);--border:var(--gray-400);--data:var(--green-500);--control:var(--blue-500);--grid-unit-5:0.25rem;--grid-unit-10:0.5rem;--grid-unit-15:0.75rem;--grid-unit-20:1rem;--grid-unit-25:1.25rem;--grid-unit-30:1.5rem;--grid-unit-40:2rem;--grid-unit-50:2.5rem;--grid-unit-60:3rem;--font-size--smaller:0.688rem;--font-size--small:0.75rem;--font-size--medium:0.875rem;--font-size--large:1rem;--font-size--extra-large:1.2rem;--font-size--bigger:2rem;--dashboard-width:64rem;--black:#000;--dashboard-accent:#b9f16b;--gray-350:#e1e3e5;--sidebar-black:#1e1e1e;--sidebar-white:#f0f0f0;--traffic-boost-header-background:rgba(56,88,233,.08);--white:#fff}.traffic-boost-preview{align-self:stretch;background-color:var(--sidebar-white);border-radius:.5rem;display:flex;flex:1 0 0;flex-direction:column;min-width:18.75rem;overflow:hidden;padding:0}.traffic-boost-preview .wp-parsely-preview{background:var(--sidebar-white);display:flex;flex:1;height:100%;overflow:hidden}.traffic-boost-preview .wp-parsely-preview .preview-iframe-wrapper{height:100%;overflow:hidden;position:relative;width:100%}.traffic-boost-preview .wp-parsely-preview .wp-parsely-preview-iframe{background:var(--sidebar-white);border:none;height:100%;right:0;overflow-x:hidden;overflow-y:auto;position:absolute;top:0;transition:all .5s ease;width:100%;z-index:0}.traffic-boost-preview .wp-parsely-preview .wp-parsely-preview-iframe.is-loading{filter:blur(4px);opacity:.7}.traffic-boost-preview .wp-parsely-preview .wp-parsely-preview-loading{align-items:center;background-color:var(--sidebar-white);display:flex;inset:0;justify-content:center;opacity:0;pointer-events:none;position:absolute;transition:opacity .1s ease-in-out,visibility .1s ease-in-out;visibility:hidden;z-index:3}.traffic-boost-preview .wp-parsely-preview .wp-parsely-preview-loading.is-loading{align-items:center;display:flex;flex-direction:column;font-size:var(--font-size--extra-large);font-weight:500;gap:var(--grid-unit-30);justify-content:center;line-height:var(--font-size--extra-large);opacity:1;visibility:visible}.traffic-boost-preview .wp-parsely-preview .wp-parsely-preview-loading.is-loading .wp-parsely-loading{width:90%}.traffic-boost-preview .wp-parsely-preview .wp-parsely-preview-loading.is-loading .wp-parsely-loading .wp-parsely-loading-message{max-width:37.5rem;text-align:center}.traffic-boost-preview .wp-parsely-preview .wp-parsely-preview-loading .components-spinner{height:var(--grid-unit-40);margin:0;width:var(--grid-unit-40)}.traffic-boost-preview .wp-parsely-preview .wp-parsely-preview-generating-placement{align-items:center;display:flex;inset:0;justify-content:center;pointer-events:none;position:absolute;z-index:3}.traffic-boost-preview-header{align-items:center;background:var(--white);border-bottom:1px solid var(--gray-400);display:flex;justify-content:space-between;padding:var(--grid-unit-15) var(--grid-unit-20);z-index:5}.traffic-boost-preview-header .traffic-boost-preview-header-actions{display:flex;gap:var(--grid-unit-10)}.traffic-boost-preview-header .traffic-boost-preview-header-actions button{border-radius:.25rem;height:1.875rem;min-width:1.875rem;padding:var(--grid-unit-5)}.traffic-boost-preview-header .traffic-boost-preview-header-actions button:hover{color:var(--wp-admin-theme-color)}.traffic-boost-preview-header .traffic-boost-preview-header-actions .components-button.is-pressed:focus:not(:disabled){box-shadow:none}.traffic-boost-preview-header .traffic-boost-preview-info{align-items:flex-start;display:flex;flex:1 0 0;flex-direction:column;gap:var(--grid-unit-5);min-width:0}.traffic-boost-preview-header .traffic-boost-preview-info .traffic-boost-preview-info-title{color:var(--gray-900);font-size:var(--font-size--large);font-weight:500;line-height:var(--font-size--extra-large);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;width:100%}.traffic-boost-preview-header .traffic-boost-preview-info .traffic-boost-preview-info-links{align-items:center;align-self:stretch;display:flex;font-size:var(--font-size--medium);font-weight:400;gap:var(--grid-unit-10);line-height:var(--font-size--extra-large);min-height:var(--grid-unit-30)}.traffic-boost-preview-header .traffic-boost-preview-info .traffic-boost-preview-info-links .traffic-boost-preview-info-links-counter{align-items:center;align-self:stretch;display:flex;gap:var(--grid-unit-10)}.traffic-boost-preview-header .traffic-boost-preview-info .traffic-boost-preview-info-links .traffic-boost-preview-info-links-counter button{height:1.375rem;padding:var(--grid-unit-5) var(--grid-unit-10)}.traffic-boost-preview-header .traffic-boost-preview-info .traffic-boost-preview-info-links .traffic-boost-preview-info-links-counter button.is-pressed,.traffic-boost-preview-header .traffic-boost-preview-info .traffic-boost-preview-info-links .traffic-boost-preview-info-links-counter button:focus{box-shadow:0 0 0 1px var(--black);outline:none}.traffic-boost-preview-header .traffic-boost-preview-info .traffic-boost-preview-info-links .traffic-boost-preview-info-links-counter button:not(.is-pressed):focus{box-shadow:inset 0 0 0 1px var(--wp-admin-theme-color);color:var(--wp-admin-theme-color)}.wp-parsely-traffic-boost-preview-settings-popover{margin-top:var(--grid-unit-20);width:17.1875rem}.wp-parsely-traffic-boost-preview-settings-popover-content{min-width:13.75rem;padding:var(--grid-unit-20);width:17.1875rem}.traffic-boost-preview-footer{align-items:center;background:var(--white);border-top:1px solid var(--gray-400);display:flex;justify-content:space-between;padding:var(--grid-unit-10) var(--grid-unit-20)}.traffic-boost-preview-footer .traffic-boost-preview-footer-next,.traffic-boost-preview-footer .traffic-boost-preview-footer-previous{min-width:2.25rem}.traffic-boost-preview-footer .traffic-boost-preview-footer-actions{align-items:center;display:flex;flex-wrap:wrap;gap:var(--grid-unit-10);justify-content:center}.traffic-boost-preview-footer .traffic-boost-preview-footer-actions .components-button{height:2.25rem}.traffic-boost-preview-footer .traffic-boost-preview-footer-actions .traffic-boost-preview-footer-navigation{align-items:center;display:flex;font-size:var(--font-size--small);font-weight:500;gap:var(--grid-unit-5);line-height:var(--font-size--medium);text-transform:uppercase}.traffic-boost-preview-footer .traffic-boost-preview-footer-actions .traffic-boost-preview-footer-navigation select{border:none;font-size:var(--font-size--small);font-weight:500;line-height:var(--font-size--medium);margin-bottom:2px} .traffic-boost-links{display:flex;flex:1;flex-direction:column;height:100%;min-height:0}.traffic-boost-links .traffic-boost-links-list{display:flex;flex:1 1 auto;flex-direction:column;height:0;min-height:0;overflow-y:auto;overscroll-behavior:contain}.traffic-boost-links .traffic-boost-links-list .traffic-boost-single-link{align-items:center;border-bottom:1px solid var(--gray-350);cursor:pointer;display:flex;flex:0 0 auto;gap:var(--grid-unit-15);padding:var(--grid-unit-20) var(--grid-unit-30)}.traffic-boost-links .traffic-boost-links-list .traffic-boost-single-link.active{background:var(--Scales-Theme-theme-alpha-04,rgba(56,88,233,.04));border-bottom:1px solid var(--traffic-boost-header-background)}.traffic-boost-links .traffic-boost-links-list .traffic-boost-single-link:focus{box-shadow:none;outline:2px solid var(--wp-admin-theme-color);outline-offset:-2px}.traffic-boost-links .traffic-boost-links-list .traffic-boost-single-link .single-link-title{color:var(--sidebar-black);font-size:var(--font-size--medium);font-style:normal;font-weight:400;line-height:var(--grid-unit-30);overflow:hidden;text-overflow:ellipsis}.traffic-boost-links .links-pagination{align-items:center;color:var(--sidebar-black);display:flex;flex:0 0 auto;font-size:var(--font-size--smaller);font-style:normal;font-weight:500;justify-content:space-between;line-height:var(--grid-unit-20);margin-top:auto;padding:var(--grid-unit-15) var(--grid-unit-30);text-transform:uppercase}.traffic-boost-links .links-pagination .links-pagination-children{margin-left:auto}.traffic-boost-links .links-pagination .links-pagination-children .traffic-boost-add-suggestion{box-shadow:none}.traffic-boost-links .links-pagination .page-selector{align-items:center;display:flex;gap:var(--grid-unit-5)}.traffic-boost-links .links-pagination .page-selector select{background-color:transparent;border:none;cursor:pointer;font-size:var(--font-size--smaller);font-weight:500}.traffic-boost-links .links-pagination .page-selector select:focus{outline:2px solid var(--wp-admin-theme-color);outline-offset:1px}.traffic-boost-links .links-pagination .page-navigation{display:flex;gap:var(--grid-unit-5);margin-right:var(--grid-unit-30)}.traffic-boost-links .traffic-boost-links-list-loading{align-items:center;display:flex;gap:var(--grid-unit-10);height:100%;justify-content:center;padding:var(--grid-unit-20)} #wp-parsely-dashboard-widget,.components-button[aria-controls="wp-parsely-block-editor-sidebar:wp-parsely-content-helper"],.editor-post-excerpt,.parsely-dashboard-container,.settings_page_parsely,.wp-parsely-block-overlay,.wp-parsely-content-helper,.wp-parsely-excerpt-generator,.wp-parsely-panel,.wp-parsely-popover,.wp-parsely-preview-editor,.wp-parsely-smart-linking-close-dialog,.wp-parsely-smart-linking-review-modal,.wp-parsely-suggested-title-modal,.wp-parsely-traffic-boost-add-new-link-popover,.wp-parsely-traffic-boost-preview-settings-popover{--base-font:"source-sans-pro",arial,sans-serif;--numeric-font:"ff-din-round-web",sans-serif;--parsely-green-components:107,42%,46%;--parsely-green:hsl(var(--parsely-green-components));--parsely-green-10:#c7ecb1;--parsely-green-65:#2a691b;--gray-200:#f7f8f9;--gray-300:#edeeef;--gray-400:#d7dbdf;--gray-500:#959da5;--gray-600:#586069;--gray-700:#444d56;--gray-900:#24292e;--blue-500:#44a8e5;--blue-550:#2596db;--green-500:#7bc01b;--green-900:#3d6307;--ref-direct:205,13%,52%;--ref-internal:161,91%,41%;--ref-social:210,72%,41%;--ref-search:42,100%,50%;--ref-other:3,76%,58%;--base-text:var(--gray-900);--base-text-2:var(--gray-600);--base-3:var(--gray-400);--border:var(--gray-400);--data:var(--green-500);--control:var(--blue-500);--grid-unit-5:0.25rem;--grid-unit-10:0.5rem;--grid-unit-15:0.75rem;--grid-unit-20:1rem;--grid-unit-25:1.25rem;--grid-unit-30:1.5rem;--grid-unit-40:2rem;--grid-unit-50:2.5rem;--grid-unit-60:3rem;--font-size--smaller:0.688rem;--font-size--small:0.75rem;--font-size--medium:0.875rem;--font-size--large:1rem;--font-size--extra-large:1.2rem;--font-size--bigger:2rem;--dashboard-width:64rem;--black:#000;--dashboard-accent:#b9f16b;--gray-350:#e1e3e5;--sidebar-black:#1e1e1e;--sidebar-white:#f0f0f0;--traffic-boost-header-background:rgba(56,88,233,.08);--white:#fff}.traffic-boost-sidebar{align-items:flex-start;background:var(--white);border-bottom:1px solid var(--gray-200);border-radius:var(--grid-unit-10);box-shadow:0 1px 1px 0 rgba(0,0,0,.03),0 1px 2px 0 rgba(0,0,0,.02),0 .1875rem .1875rem 0 rgba(0,0,0,.02),0 .25rem .25rem 0 rgba(0,0,0,.01);display:flex;flex-direction:column;height:calc(100vh - 4.0625rem);max-width:28.125rem;min-width:20rem}.traffic-boost-sidebar .traffic-boost-sidebar-header{align-items:center;align-self:stretch;background:var(--traffic-boost-header-background);display:flex;flex-direction:column;gap:var(--grid-unit-20);min-height:var(--grid-unit-50);padding:var(--grid-unit-20) var(--grid-unit-30)}.traffic-boost-sidebar .traffic-boost-sidebar-header .traffic-boost-sidebar-header-nav{align-items:center;align-self:stretch;display:flex;justify-content:space-between}.traffic-boost-sidebar .traffic-boost-sidebar-header .traffic-boost-sidebar-header-nav .components-button{font-size:var(--font-size--medium);font-weight:400;line-height:var(--grid-unit-25);padding-right:0;padding-left:0}.traffic-boost-sidebar .traffic-boost-sidebar-header .traffic-boost-post-details{align-items:flex-start;align-self:stretch;display:flex;flex-direction:column;gap:var(--grid-unit-10);justify-content:flex-end}.traffic-boost-sidebar .traffic-boost-sidebar-header .traffic-boost-post-details .traffic-boost-post-details-label{color:var(--gray-900);font-size:var(--font-size--small);font-style:normal;font-weight:500;line-height:var(--grid-unit-20);overflow:hidden;text-transform:uppercase}.traffic-boost-sidebar .traffic-boost-sidebar-header .traffic-boost-post-details .traffic-boost-post-details-content{align-items:center;align-self:stretch;display:flex;gap:var(--grid-unit-10)}.traffic-boost-sidebar .traffic-boost-sidebar-header .traffic-boost-post-details .traffic-boost-post-details-content .post-title{color:var(--gray-900);font-size:var(--font-size--large);font-style:normal;font-weight:500;line-height:var(--grid-unit-25)}.traffic-boost-sidebar .traffic-boost-sidebar-header .traffic-boost-post-details .traffic-boost-post-details-divider{background:var(--gray-400);height:1px;margin:var(--grid-unit-10) 0;width:100%}.traffic-boost-sidebar .traffic-boost-sidebar-header .traffic-boost-post-details .traffic-boost-post-details-description{font-size:var(--font-size--medium);font-style:normal;font-weight:400;line-height:var(--grid-unit-20)}.traffic-boost-sidebar .traffic-boost-sidebar-content{display:flex;flex:1;flex-direction:column;gap:var(--grid-unit-20);min-height:0;width:100%}.traffic-boost-sidebar .traffic-boost-sidebar-content .traffic-boost-suggestions-settings .components-panel__body{border-bottom:0;border-top:0}.traffic-boost-sidebar .traffic-boost-sidebar-content .traffic-boost-suggestions-empty-state{align-items:center;display:flex;flex-direction:column;gap:var(--grid-unit-20);padding:var(--grid-unit-20)}.traffic-boost-sidebar .traffic-boost-sidebar-content .traffic-boost-suggestions-generating-footer{align-items:center;display:flex;gap:var(--grid-unit-10);justify-content:center;min-height:2.25rem;width:100%}.traffic-boost-sidebar .traffic-boost-sidebar-content .traffic-boost-suggestions-generating-footer span{margin-top:.3125rem}.traffic-boost-sidebar .traffic-boost-sidebar-tabs{display:flex;flex:1;flex-direction:column;width:100%}.traffic-boost-sidebar .traffic-boost-sidebar-tabs .components-tab-panel__tab-content{display:flex;flex:1;flex-direction:column;min-height:0;overflow:hidden}.traffic-boost-sidebar .traffic-boost-sidebar-tabs .components-tab-panel__tabs{border-bottom:1px solid var(--gray-400);border-top:1px solid var(--gray-400);display:flex;margin:0;padding:0;position:relative;width:100%}.traffic-boost-sidebar .traffic-boost-sidebar-tabs .components-tab-panel__tabs .components-tab-panel__tabs-item{flex:1;height:var(--grid-unit-60);justify-content:center;margin:0;padding:var(--grid-unit-15)}.traffic-boost-sidebar .traffic-boost-sidebar-tabs .components-tab-panel__tabs .components-tab-panel__tabs-item.is-active{box-shadow:inset 0 0 0 var(--wp-admin-border-width-focus) transparent,inset 0 -1.5px 0 0 var(--wp-admin-theme-color)}.traffic-boost-sidebar .traffic-boost-sidebar-tabs .components-tab-panel__tabs .components-tab-panel__tabs-item .tab-count{align-items:center;background:var(--gray-400);border-radius:2px;display:inline-flex;gap:var(--grid-unit-10);justify-content:center;margin-right:var(--grid-unit-20);padding:var(--grid-unit-5) var(--grid-unit-10)}.traffic-boost-sidebar .traffic-boost-sidebar-tabs .components-tab-panel__tabs .components-tab-panel__tabs-item.icon-only-tab{flex:0 0 var(--grid-unit-50);justify-content:center!important;min-width:var(--grid-unit-60)!important;padding:var(--grid-unit-10)!important;width:var(--grid-unit-60)!important}.traffic-boost-sidebar .traffic-boost-sidebar-tabs .components-tab-panel__tabs .components-tab-panel__tabs-item.icon-only-tab .icon{color:var(--gray-600);height:var(--grid-unit-30)!important;width:var(--grid-unit-30)!important}.traffic-boost-sidebar .traffic-boost-sidebar-tabs .components-tab-panel__tabs .components-tab-panel__tabs-item.icon-only-tab .icon svg{height:var(--grid-unit-30)!important;width:var(--grid-unit-30)!important}.traffic-boost-sidebar .traffic-boost-sidebar-tabs .components-tab-panel__tabs .components-tab-panel__tabs-item.icon-only-tab:hover .icon{color:var(--gray-900)}.traffic-boost-sidebar .traffic-boost-sidebar-tabs .components-tab-panel__tabs .components-tab-panel__tabs-item .suggestions-tab{order:1}.traffic-boost-sidebar .traffic-boost-sidebar-tabs .components-tab-panel__tabs .components-tab-panel__tabs-item .inbound-links-tab{order:2}.traffic-boost-sidebar .traffic-boost-suggestions-loading-generating{align-items:center;display:flex;flex-direction:column;gap:var(--grid-unit-15);height:100%;justify-content:center;padding:var(--grid-unit-20)}.wp-parsely-traffic-boost-add-new-link-popover{width:25rem}.wp-parsely-traffic-boost-add-new-link-popover .components-popover__content{display:flex;flex-direction:column;gap:var(--grid-unit-20);padding:var(--grid-unit-20);width:100%}.wp-parsely-traffic-boost-add-new-link-popover .traffic-boost-add-link-table.no-results{text-align:right}.wp-parsely-traffic-boost-add-new-link-popover .traffic-boost-add-link-table tr{cursor:pointer} .parsely-tone-selector-dropdown{align-items:center;align-self:stretch;background:var(--Gutenberg-White,#fff);border:1px solid var(--Gutenberg-Gray-600,#949494);border-radius:2px;height:2.25rem;overflow-wrap:break-word;width:100%;word-break:break-word}.parsely-tone-selector-dropdown button{height:2.0625rem}.parsely-tone-selector-dropdown .components-dropdown-menu__toggle.has-icon svg:first-child{display:none}.parsely-tone-selector-dropdown.is-disabled{opacity:.5;pointer-events:none}.parsely-tone-selector-dropdown .components-dropdown-menu__toggle{display:flex;gap:.625rem;width:100%}.parsely-tone-selector-dropdown .components-dropdown-menu__toggle svg:first-of-type path{transform:scale(1.4);transform-origin:center}.parsely-tone-selector-dropdown .parsely-tone-selector-label{flex-grow:2;padding:0 var(--grid-unit-10);text-align:right}.parsely-tone-selector-dropdown .parsely-tone-selector-label:first-letter{text-transform:uppercase}.parsely-persona-selector-custom{width:100%}.parsely-persona-selector-custom .components-base-control__field input{display:flex;height:2.5rem;padding:var(--grid-unit-15) var(--grid-unit-20)}.parsely-persona-selector-custom .components-base-control__field input,.parsely-persona-selector-dropdown{align-items:center;align-self:stretch;border:1px solid var(--Gutenberg-Gray-600,#949494);border-radius:2px}.parsely-persona-selector-dropdown{background:var(--Gutenberg-White,#fff);height:2.25rem;overflow-wrap:break-word;width:100%;word-break:break-word}.parsely-persona-selector-dropdown .components-dropdown-menu__toggle.has-icon svg:first-child{display:none}.parsely-persona-selector-dropdown button{height:2.0625rem}.parsely-persona-selector-dropdown.is-disabled{opacity:.5;pointer-events:none}.parsely-persona-selector-dropdown .components-dropdown-menu__toggle{display:flex;gap:.625rem;width:100%}.parsely-persona-selector-dropdown .parsely-persona-selector-label{flex-grow:2;padding:0 var(--grid-unit-10);text-align:right}.parsely-persona-selector-dropdown .parsely-persona-selector-label:first-letter{text-transform:uppercase}.parsely-tone-selector-custom{width:100%}.parsely-tone-selector-custom .components-base-control__field input{align-items:center;align-self:stretch;border:1px solid var(--Gutenberg-Gray-600,#949494);border-radius:2px;display:flex;height:2.5rem;padding:var(--grid-unit-15) var(--grid-unit-20)}.parsely-inputrange-control{align-items:flex-start;align-self:stretch;display:flex;flex-direction:column;gap:var(--grid-unit-10)}.parsely-inputrange-control .parsely-inputrange-control__label{margin:0}.parsely-inputrange-control .parsely-inputrange-control__controls{align-items:center;align-self:stretch;display:flex;gap:var(--grid-unit-20);height:2.5rem}.parsely-inputrange-control .parsely-inputrange-control__controls .components-input-control{display:flex;flex:1 0 0}.parsely-inputrange-control .parsely-inputrange-control__controls .components-input-control input[type=number]::-webkit-inner-spin-button,.parsely-inputrange-control .parsely-inputrange-control__controls .components-input-control input[type=number]::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.parsely-inputrange-control .parsely-inputrange-control__controls .components-input-control input[type=number]{-moz-appearance:textfield}.parsely-inputrange-control .parsely-inputrange-control__controls .components-input-control .components-base-control__field{flex-grow:1}.parsely-inputrange-control .parsely-inputrange-control__controls .components-input-control .components-input-control__suffix{color:var(--wp-components-color-accent,var(--wp-admin-theme-color,#3858e9))}.parsely-inputrange-control .parsely-inputrange-control__controls .components-range-control{flex:1 0 0;height:36px}.parsely-inputrange-control .parsely-inputrange-control__controls .components-range-control .components-range-control__root{height:2.5rem}#wp-parsely-dashboard-widget,.components-button[aria-controls="wp-parsely-block-editor-sidebar:wp-parsely-content-helper"],.editor-post-excerpt,.parsely-dashboard-container,.settings_page_parsely,.wp-parsely-block-overlay,.wp-parsely-content-helper,.wp-parsely-excerpt-generator,.wp-parsely-panel,.wp-parsely-popover,.wp-parsely-preview-editor,.wp-parsely-smart-linking-close-dialog,.wp-parsely-smart-linking-review-modal,.wp-parsely-suggested-title-modal,.wp-parsely-traffic-boost-add-new-link-popover,.wp-parsely-traffic-boost-preview-settings-popover{--base-font:"source-sans-pro",arial,sans-serif;--numeric-font:"ff-din-round-web",sans-serif;--parsely-green-components:107,42%,46%;--parsely-green:hsl(var(--parsely-green-components));--parsely-green-10:#c7ecb1;--parsely-green-65:#2a691b;--gray-200:#f7f8f9;--gray-300:#edeeef;--gray-400:#d7dbdf;--gray-500:#959da5;--gray-600:#586069;--gray-700:#444d56;--gray-900:#24292e;--blue-500:#44a8e5;--blue-550:#2596db;--green-500:#7bc01b;--green-900:#3d6307;--ref-direct:205,13%,52%;--ref-internal:161,91%,41%;--ref-social:210,72%,41%;--ref-search:42,100%,50%;--ref-other:3,76%,58%;--base-text:var(--gray-900);--base-text-2:var(--gray-600);--base-3:var(--gray-400);--border:var(--gray-400);--data:var(--green-500);--control:var(--blue-500);--grid-unit-5:0.25rem;--grid-unit-10:0.5rem;--grid-unit-15:0.75rem;--grid-unit-20:1rem;--grid-unit-25:1.25rem;--grid-unit-30:1.5rem;--grid-unit-40:2rem;--grid-unit-50:2.5rem;--grid-unit-60:3rem;--font-size--smaller:0.688rem;--font-size--small:0.75rem;--font-size--medium:0.875rem;--font-size--large:1rem;--font-size--extra-large:1.2rem;--font-size--bigger:2rem;--dashboard-width:64rem;--black:#000;--dashboard-accent:#b9f16b;--gray-350:#e1e3e5;--sidebar-black:#1e1e1e;--sidebar-white:#f0f0f0;--traffic-boost-header-background:rgba(56,88,233,.08);--white:#fff}.parsely-thumbnail{border-radius:3px;flex-shrink:0;overflow:hidden}.parsely-thumbnail img{border-radius:3px;height:100%;-o-object-fit:cover;object-fit:cover;width:100%}.parsely-thumbnail .parsely-thumbnail-icon-container{align-items:center;background:var(--gray-500);border-radius:3px;display:flex;height:100%;justify-content:center;width:100%}.parsely-thumbnail .parsely-thumbnail-icon-container svg{fill:var(--sidebar-white);height:60%;width:60%}.wp-parsely-snackbar-notices{bottom:4rem;display:flex;flex-direction:column-reverse;gap:.375rem;padding:1rem;position:fixed;width:100%;z-index:999999}.wp-parsely-snackbar-notices svg{fill:currentcolor}.wp-parsely-loading{align-items:center;border-radius:.25rem;color:var(--sidebar-black);display:flex;gap:var(--grid-unit-20);justify-content:center;min-height:1.5rem;min-width:25rem;padding:var(--grid-unit-10) var(--grid-unit-15)}.wp-parsely-loading .components-spinner{flex-shrink:0;margin:0}.wp-parsely-loading .wp-parsely-loading-message{flex-grow:1;font-size:var(--font-size--medium);line-height:1.4;margin:0;max-width:25rem}.wp-parsely-loading .wp-parsely-typewriter-text{display:inline-block;min-height:1.4em;min-width:1ch}.wp-parsely-loading .wp-parsely-typewriter-cursor{animation:blink 1s step-end infinite;display:inline-block;font-weight:400;margin-right:2px;opacity:.7}@keyframes blink{0%,to{opacity:0}50%{opacity:1}}.parsely-panel-settings{width:100%}.parsely-panel-settings .parsely-panel-settings-header{display:flex;margin:.625rem 0;width:100%}.parsely-panel-settings .parsely-panel-settings-header .parsely-panel-settings-header-label{flex-grow:2;margin:0 .3125rem 0 0;text-align:right}.parsely-panel-settings .parsely-panel-settings-header .parsely-panel-settings-header-label .components-base-control__field{align-items:center;display:flex;justify-content:space-between}.parsely-panel-settings .parsely-panel-settings-header .parsely-panel-settings-header-label .components-base-control__field .components-base-control__label{margin-bottom:0}.parsely-panel-settings .parsely-panel-settings-body{align-self:stretch;display:flex;flex-direction:column;gap:.625rem;padding:.375rem 0 var(--grid-unit-20) 0}.wp-parsely-dropdown-label{align-self:stretch;color:var(--sidebar-black);font-size:var(--font-size--smaller);font-style:normal;font-weight:600;line-height:var(--grid-unit-20);text-transform:uppercase}.wp-parsely-content-helper-error{align-self:flex-start;margin:0}.wp-parsely-content-helper-error p{margin:0}.wp-parsely-content-helper-error .content-helper-error-message{margin:0!important}.wp-parsely-collapsible-panel{align-items:flex-start;align-self:stretch;border-bottom:none;border-radius:2px;border-top:none;display:flex;flex-direction:column;gap:var(--grid-unit-20);margin:0 0 .5rem;padding:0}.wp-parsely-collapsible-panel .components-panel__body-title{align-items:center;align-self:stretch;display:flex;margin:0 -1rem .375rem;padding:0}.wp-parsely-collapsible-panel .components-panel__body-title .components-panel__icon,.wp-parsely-collapsible-panel .components-panel__body-title svg{margin-right:0;margin-left:var(--grid-unit-10);order:-1}.wp-parsely-collapsible-panel .components-panel__body-title:hover{background-color:transparent}.wp-parsely-collapsible-panel.is-opened{padding:0}.wp-parsely-collapsible-panel.is-opened>div:first-of-type{margin-top:calc(var(--grid-unit-20)*-1)}.wp-parsely-collapsible-panel.is-opened .components-panel__body-title{margin:0 calc(var(--grid-unit-20)*-1) .375rem;padding-bottom:0}.wp-parsely-collapsible-panel .components-panel__body-toggle.components-button{color:var(--Gutenberg-Gray-900,#1e1e1e);font-size:.6875rem;font-style:normal;font-weight:600;line-height:1rem;padding:var(--grid-unit-20) var(--grid-unit-20);text-transform:uppercase}.wp-parsely-collapsible-panel .components-panel__body-toggle.components-button .components-panel__arrow{margin-left:0}.wp-parsely-collapsible-panel .components-panel__body.is-opened{padding:0}.parsely-table-container.is-loading{align-items:center;display:flex;justify-content:center;padding:var(--grid-unit-20)}.parsely-table-container table{border-collapse:collapse;border-spacing:0;width:100%}.parsely-table-container table tr{position:relative}.parsely-table-container table tr:after{background-color:var(--border);bottom:0;content:"";height:1px;right:50%;position:absolute;transform:translateX(50%);width:100vw}.parsely-table-container table tr td,.parsely-table-container table tr th{padding:var(--grid-unit-15) 0}.parsely-table-container table tr td.post-info,.parsely-table-container table tr th.post-info-header{text-align:right}.parsely-table-container table tr th{color:var(--sidebar-black);font-size:var(--font-size--small);font-style:normal;font-weight:600;line-height:var(--font-size--large);text-transform:uppercase}.parsely-table-container table tr th.views-header{min-width:3.75rem;text-align:center}.parsely-table-container table tr th.views-header .views-header-period{color:var(--gray-500);font-size:var(--font-size--smaller);font-weight:400;margin-right:var(--grid-unit-5)}.parsely-table-container table tr td.metrics{min-width:100px}.parsely-table-container table tr td.metrics .metrics-container{align-items:center;display:flex;flex-direction:column;gap:var(--grid-unit-5)}.parsely-table-container table tr td.metrics .metrics-container .metric-change{font-size:var(--font-size--small);font-weight:600;line-height:var(--font-size--large);margin-right:var(--grid-unit-5)}.parsely-table-container table tr td.metrics .metrics-container .metric-change-positive{color:var(--parsely-green)}.parsely-table-container table tr td.metrics .metrics-container .metric-change-negative{color:#900000}.parsely-table-container table tr td.metrics .metrics-container .metric-change-neutral{color:var(--gray-500)}.parsely-table-container table tr td.metrics .metrics-container .metric-boost-percentage{color:var(--parsely-green);font-size:var(--font-size--small);font-weight:600}.parsely-table-container table tr td.actions{align-items:center;display:flex;gap:var(--grid-unit-10);justify-content:flex-end;margin-right:var(--grid-unit-10);max-width:8.4375rem;min-height:2.5rem}.parsely-table-container table tr td.actions a{text-decoration:none}.parsely-table-container table tr td.actions .boost-link-container{display:flex;flex-direction:column;gap:.25rem;height:100%;text-align:center;width:100%}.parsely-table-container table tr td.actions .boost-link-container .boost-link-status{display:flex;gap:.25rem;justify-content:center}.parsely-table-container table tr td.actions .boost-link-container .boost-link-status .hidden{opacity:0}.parsely-table-container table tr td.actions .boost-link-container .boost-link-status .boost-inbound,.parsely-table-container table tr td.actions .boost-link-container .boost-link-status .boost-outbound{align-items:center;background-color:var(--gray-300);border-radius:.375rem;color:var(--sidebar-black);display:flex;flex-grow:1;font-size:var(--font-size--small);justify-content:center;line-height:var(--font-size--medium);padding:.25rem .5625rem}.parsely-table-container table tr td.actions .boost-link-container .boost-link-status .boost-link-status-icon-inbound{transform:rotate(-180deg)}.parsely-table-container.no-results{color:#757575;font-style:italic;padding:var(--grid-unit-15);text-align:center}.parsely-table-container .suggestion-bubble{background:var(--parsely-green);border:none;border-radius:1.25rem;box-shadow:none;color:var(--sidebar-white);cursor:pointer;display:inline-block;font-size:.8125rem;line-height:.875rem;margin-right:.375rem;min-width:1.75rem;padding:3px .375rem;text-align:center}.parsely-table-container.compact table tr td.post-info{max-width:100px}.parsely-table-container.compact tr:after{width:100%}.parsely-table-container.compact .post-title{display:-webkit-box!important;overflow:hidden;-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical}.parsely-table-container.compact .post-title>span{white-space:normal!important}.parsely-table-container.hide-pagination table tr:last-child:after{display:none}.posts-table-post-info{align-items:flex-start;display:flex;flex:1 0 0;gap:var(--grid-unit-20);min-height:2.5rem}.posts-table-post-info .posts-table-thumbnail{align-self:center}.posts-table-post-info .post-details{align-items:flex-start;display:flex;flex:1 0 0;flex-direction:column;gap:var(--grid-unit-05,4px);justify-content:center;min-height:var(--grid-unit-40);min-width:0}.posts-table-post-info .post-details .post-title{align-items:center;align-self:stretch;display:flex;font-size:var(--font-size--medium);font-style:normal;font-weight:600;gap:var(--grid-unit-5);line-height:1.25rem;overflow:hidden;text-overflow:ellipsis}.posts-table-post-info .post-details .post-title>span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.posts-table-post-info .post-details .post-meta{align-content:center;align-items:center;align-self:stretch;color:var(--gray-500);display:flex;flex-wrap:wrap;gap:var(--grid-unit-5) var(--grid-unit-10)}.posts-table-post-info .post-details .post-categories{display:flex;gap:var(--grid-unit-5)}.posts-table-post-info .post-details .post-categories span{background:var(--gray-400);border-radius:.375rem;color:var(--sidebar-black);font-size:var(--font-size--smaller);font-weight:500;line-height:var(--font-size--large);padding:2px var(--grid-unit-10)}.posts-table-pagination-controls{align-items:center;align-self:stretch;display:flex;font-size:var(--font-size--smaller);font-style:normal;font-weight:600;gap:var(--grid-unit-30);justify-content:flex-end;line-height:var(--font-size--large);padding:var(--grid-unit-15);text-transform:uppercase}.posts-table-pagination-controls .page-selector{align-items:center;display:flex;gap:var(--grid-unit-10)}.posts-table-pagination-controls .page-navigation{align-items:center;display:flex;gap:var(--grid-unit-5);justify-content:flex-end}body{background:#fff}.parsely-dashboard-container{margin-right:-1.25rem;overflow:hidden}.parsely-dashboard-page-body,.parsely-dashboard-page-header{margin:0 auto;max-width:var(--dashboard-width);padding:var(--grid-unit-30)} diff --git a/build/content-helper/dashboard-page.asset.php b/build/content-helper/dashboard-page.asset.php index 2aa13ebe36..482edca409 100644 --- a/build/content-helper/dashboard-page.asset.php +++ b/build/content-helper/dashboard-page.asset.php @@ -1 +1 @@ - array('react', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-dom-ready', 'wp-element', 'wp-escape-html', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url'), 'version' => '3bcb468b04c9bad5afcb'); + array('react', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-dom-ready', 'wp-element', 'wp-escape-html', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url'), 'version' => 'd32bef74f4fde0b9ec6b'); diff --git a/build/content-helper/dashboard-page.css b/build/content-helper/dashboard-page.css index 0a6d59e657..fd370f9331 100644 --- a/build/content-helper/dashboard-page.css +++ b/build/content-helper/dashboard-page.css @@ -1,6 +1,6 @@ #wp-parsely-dashboard-widget,.components-button[aria-controls="wp-parsely-block-editor-sidebar:wp-parsely-content-helper"],.editor-post-excerpt,.parsely-dashboard-container,.settings_page_parsely,.wp-parsely-block-overlay,.wp-parsely-content-helper,.wp-parsely-excerpt-generator,.wp-parsely-panel,.wp-parsely-popover,.wp-parsely-preview-editor,.wp-parsely-smart-linking-close-dialog,.wp-parsely-smart-linking-review-modal,.wp-parsely-suggested-title-modal,.wp-parsely-traffic-boost-add-new-link-popover,.wp-parsely-traffic-boost-preview-settings-popover{--base-font:"source-sans-pro",arial,sans-serif;--numeric-font:"ff-din-round-web",sans-serif;--parsely-green-components:107,42%,46%;--parsely-green:hsl(var(--parsely-green-components));--parsely-green-10:#c7ecb1;--parsely-green-65:#2a691b;--gray-200:#f7f8f9;--gray-300:#edeeef;--gray-400:#d7dbdf;--gray-500:#959da5;--gray-600:#586069;--gray-700:#444d56;--gray-900:#24292e;--blue-500:#44a8e5;--blue-550:#2596db;--green-500:#7bc01b;--green-900:#3d6307;--ref-direct:205,13%,52%;--ref-internal:161,91%,41%;--ref-social:210,72%,41%;--ref-search:42,100%,50%;--ref-other:3,76%,58%;--base-text:var(--gray-900);--base-text-2:var(--gray-600);--base-3:var(--gray-400);--border:var(--gray-400);--data:var(--green-500);--control:var(--blue-500);--grid-unit-5:0.25rem;--grid-unit-10:0.5rem;--grid-unit-15:0.75rem;--grid-unit-20:1rem;--grid-unit-25:1.25rem;--grid-unit-30:1.5rem;--grid-unit-40:2rem;--grid-unit-50:2.5rem;--grid-unit-60:3rem;--font-size--smaller:0.688rem;--font-size--small:0.75rem;--font-size--medium:0.875rem;--font-size--large:1rem;--font-size--extra-large:1.2rem;--font-size--bigger:2rem;--dashboard-width:64rem;--black:#000;--dashboard-accent:#b9f16b;--gray-350:#e1e3e5;--sidebar-black:#1e1e1e;--sidebar-white:#f0f0f0;--traffic-boost-header-background:rgba(56,88,233,.08);--white:#fff}.parsely-menu-page-dashboard .parsely-dashboard-page-body,.parsely-menu-page-dashboard .parsely-dashboard-page-header{padding:var(--grid-unit-40) var(--grid-unit-40) var(--grid-unit-60) var(--grid-unit-40)}.parsely-menu-page-dashboard .dashboard-header-background{background:linear-gradient(303deg,#000 30.51%,#487006 133.77%)}.parsely-menu-page-dashboard .dashboard-header{align-items:flex-start;display:flex;flex-wrap:wrap;gap:var(--grid-unit-25);justify-content:space-between}.parsely-menu-page-dashboard .dashboard-header .dashboard-header-summary{align-items:flex-start;color:var(--sidebar-white);display:flex;flex-direction:column;gap:var(--grid-unit-30);width:18.75rem}.parsely-menu-page-dashboard .dashboard-header .dashboard-header-summary .summary-info{align-items:flex-start;align-self:stretch;display:flex;flex-direction:column;gap:var(--grid-unit-10)}.parsely-menu-page-dashboard .dashboard-header .dashboard-header-summary .summary-info .summary-title{align-self:stretch;font-size:var(--font-size--bigger);font-style:normal;font-weight:600;line-height:2.8125rem}.parsely-menu-page-dashboard .dashboard-header .dashboard-header-summary .summary-info .summary-text{align-self:stretch;font-size:var(--font-size--small);font-style:normal;font-weight:400;line-height:var(--font-size--large)}.parsely-menu-page-dashboard .dashboard-header .dashboard-header-summary .summary-button button{background:transparent;border:1px solid var(--sidebar-white);box-shadow:none;color:var(--sidebar-white);cursor:pointer;margin-top:var(--grid-unit-20);padding:var(--grid-unit-10) var(--grid-unit-20)}.parsely-menu-page-dashboard .dashboard-header .dashboard-header-summary .summary-button button:hover{background:var(--sidebar-white);box-shadow:none;color:var(--sidebar-black)}.parsely-menu-page-dashboard .dashboard-header .dashboard-header-stats{align-content:flex-start;align-items:flex-start;align-self:stretch;display:flex;flex-wrap:wrap;gap:var(--grid-unit-30);justify-content:flex-end;width:31.25rem}.parsely-menu-page-dashboard .dashboard-header .dashboard-header-stats .header-stat-card{align-items:flex-start;color:var(--sidebar-white);display:flex;flex-direction:column;gap:var(--grid-unit-05);height:var(--grid-unit-60);justify-content:center;width:9.375rem}.parsely-menu-page-dashboard .dashboard-header .dashboard-header-stats .header-stat-card .card-title{align-items:center;display:flex;font-size:var(--font-size--small);font-style:normal;font-weight:400;gap:var(--grid-unit-05);line-height:var(--font-size--large)}.parsely-menu-page-dashboard .dashboard-header .dashboard-header-stats .header-stat-card .card-title svg{fill:var(--sidebar-white)}.parsely-menu-page-dashboard .dashboard-header .dashboard-header-stats .header-stat-card .card-value{align-items:center;display:flex;font-size:1.5rem;font-style:normal;font-weight:600;gap:var(--grid-unit-05);letter-spacing:-.48px;line-height:2rem}.parsely-menu-page-dashboard .dashboard-header .dashboard-header-stats .header-stat-card .card-value .card-change{font-size:var(--font-size--medium);font-style:normal;font-weight:500;line-height:var(--font-size--large)}.parsely-menu-page-dashboard .dashboard-header .dashboard-header-stats .stats-top{align-items:flex-start;border-bottom:1px solid var(--sidebar-white);display:flex;gap:var(--grid-unit-30);padding-bottom:var(--grid-unit-30)}.parsely-menu-page-dashboard .dashboard-header .dashboard-header-stats .stats-top .card-title{color:var(--dashboard-accent)}.parsely-menu-page-dashboard .dashboard-header .dashboard-header-stats .stats-top .card-title svg{fill:var(--dashboard-accent)}.parsely-menu-page-dashboard .dashboard-header .dashboard-header-stats .stats-top .card-value{color:var(--dashboard-accent)}.parsely-menu-page-dashboard .dashboard-header .dashboard-header-stats .stats-top .stat-intro{justify-content:flex-start}.parsely-menu-page-dashboard .dashboard-header .dashboard-header-stats .stats-top .stat-intro .card-title{color:var(--sidebar-white);font-size:var(--font-size--medium);font-style:normal;font-weight:500;line-height:var(--font-size--extra-large)} #wp-parsely-dashboard-widget,.components-button[aria-controls="wp-parsely-block-editor-sidebar:wp-parsely-content-helper"],.editor-post-excerpt,.parsely-dashboard-container,.settings_page_parsely,.wp-parsely-block-overlay,.wp-parsely-content-helper,.wp-parsely-excerpt-generator,.wp-parsely-panel,.wp-parsely-popover,.wp-parsely-preview-editor,.wp-parsely-smart-linking-close-dialog,.wp-parsely-smart-linking-review-modal,.wp-parsely-suggested-title-modal,.wp-parsely-traffic-boost-add-new-link-popover,.wp-parsely-traffic-boost-preview-settings-popover{--base-font:"source-sans-pro",arial,sans-serif;--numeric-font:"ff-din-round-web",sans-serif;--parsely-green-components:107,42%,46%;--parsely-green:hsl(var(--parsely-green-components));--parsely-green-10:#c7ecb1;--parsely-green-65:#2a691b;--gray-200:#f7f8f9;--gray-300:#edeeef;--gray-400:#d7dbdf;--gray-500:#959da5;--gray-600:#586069;--gray-700:#444d56;--gray-900:#24292e;--blue-500:#44a8e5;--blue-550:#2596db;--green-500:#7bc01b;--green-900:#3d6307;--ref-direct:205,13%,52%;--ref-internal:161,91%,41%;--ref-social:210,72%,41%;--ref-search:42,100%,50%;--ref-other:3,76%,58%;--base-text:var(--gray-900);--base-text-2:var(--gray-600);--base-3:var(--gray-400);--border:var(--gray-400);--data:var(--green-500);--control:var(--blue-500);--grid-unit-5:0.25rem;--grid-unit-10:0.5rem;--grid-unit-15:0.75rem;--grid-unit-20:1rem;--grid-unit-25:1.25rem;--grid-unit-30:1.5rem;--grid-unit-40:2rem;--grid-unit-50:2.5rem;--grid-unit-60:3rem;--font-size--smaller:0.688rem;--font-size--small:0.75rem;--font-size--medium:0.875rem;--font-size--large:1rem;--font-size--extra-large:1.2rem;--font-size--bigger:2rem;--dashboard-width:64rem;--black:#000;--dashboard-accent:#b9f16b;--gray-350:#e1e3e5;--sidebar-black:#1e1e1e;--sidebar-white:#f0f0f0;--traffic-boost-header-background:rgba(56,88,233,.08);--white:#fff}.parsely-menu-page-traffic-boost .parsely-dashboard-page-header{padding:var(--grid-unit-30)}.parsely-menu-page-traffic-boost .traffic-boost-search-container{border-radius:2px;margin-bottom:0;padding:var(--grid-unit-15) 0}.parsely-menu-page-traffic-boost-single-post{align-items:flex-start;align-self:stretch;display:flex;flex:1 0 0;gap:var(--grid-unit-20);padding:var(--grid-unit-20) var(--grid-unit-20) var(--grid-unit-20) 0;position:relative}.wp-parsely-snackbar-notices.traffic-boost-snackbar-notices{padding-left:30rem} -#wp-parsely-dashboard-widget,.components-button[aria-controls="wp-parsely-block-editor-sidebar:wp-parsely-content-helper"],.editor-post-excerpt,.parsely-dashboard-container,.settings_page_parsely,.wp-parsely-block-overlay,.wp-parsely-content-helper,.wp-parsely-excerpt-generator,.wp-parsely-panel,.wp-parsely-popover,.wp-parsely-preview-editor,.wp-parsely-smart-linking-close-dialog,.wp-parsely-smart-linking-review-modal,.wp-parsely-suggested-title-modal,.wp-parsely-traffic-boost-add-new-link-popover,.wp-parsely-traffic-boost-preview-settings-popover{--base-font:"source-sans-pro",arial,sans-serif;--numeric-font:"ff-din-round-web",sans-serif;--parsely-green-components:107,42%,46%;--parsely-green:hsl(var(--parsely-green-components));--parsely-green-10:#c7ecb1;--parsely-green-65:#2a691b;--gray-200:#f7f8f9;--gray-300:#edeeef;--gray-400:#d7dbdf;--gray-500:#959da5;--gray-600:#586069;--gray-700:#444d56;--gray-900:#24292e;--blue-500:#44a8e5;--blue-550:#2596db;--green-500:#7bc01b;--green-900:#3d6307;--ref-direct:205,13%,52%;--ref-internal:161,91%,41%;--ref-social:210,72%,41%;--ref-search:42,100%,50%;--ref-other:3,76%,58%;--base-text:var(--gray-900);--base-text-2:var(--gray-600);--base-3:var(--gray-400);--border:var(--gray-400);--data:var(--green-500);--control:var(--blue-500);--grid-unit-5:0.25rem;--grid-unit-10:0.5rem;--grid-unit-15:0.75rem;--grid-unit-20:1rem;--grid-unit-25:1.25rem;--grid-unit-30:1.5rem;--grid-unit-40:2rem;--grid-unit-50:2.5rem;--grid-unit-60:3rem;--font-size--smaller:0.688rem;--font-size--small:0.75rem;--font-size--medium:0.875rem;--font-size--large:1rem;--font-size--extra-large:1.2rem;--font-size--bigger:2rem;--dashboard-width:64rem;--black:#000;--dashboard-accent:#b9f16b;--gray-350:#e1e3e5;--sidebar-black:#1e1e1e;--sidebar-white:#f0f0f0;--traffic-boost-header-background:rgba(56,88,233,.08);--white:#fff}.traffic-boost-preview{align-self:stretch;background-color:var(--sidebar-white);border-radius:.5rem;display:flex;flex:1 0 0;flex-direction:column;min-width:18.75rem;overflow:hidden;padding:0}.traffic-boost-preview .wp-parsely-preview{background:var(--sidebar-white);display:flex;flex:1;height:100%;overflow:hidden}.traffic-boost-preview .wp-parsely-preview .preview-iframe-wrapper{height:100%;overflow:hidden;position:relative;width:100%}.traffic-boost-preview .wp-parsely-preview .wp-parsely-preview-iframe{background:var(--sidebar-white);border:none;height:100%;left:0;overflow-x:hidden;overflow-y:auto;position:absolute;top:0;transition:all .5s ease;width:100%;z-index:0}.traffic-boost-preview .wp-parsely-preview .wp-parsely-preview-iframe.is-loading{filter:blur(4px);opacity:.7}.traffic-boost-preview .wp-parsely-preview .wp-parsely-preview-loading{align-items:center;background-color:var(--sidebar-white);display:flex;inset:0;justify-content:center;opacity:0;pointer-events:none;position:absolute;transition:opacity .1s ease-in-out,visibility .1s ease-in-out;visibility:hidden;z-index:3}.traffic-boost-preview .wp-parsely-preview .wp-parsely-preview-loading.is-loading{align-items:center;display:flex;flex-direction:column;font-size:var(--font-size--extra-large);font-weight:500;gap:var(--grid-unit-30);justify-content:center;line-height:var(--font-size--extra-large);opacity:1;visibility:visible}.traffic-boost-preview .wp-parsely-preview .wp-parsely-preview-loading.is-loading .wp-parsely-loading{width:90%}.traffic-boost-preview .wp-parsely-preview .wp-parsely-preview-loading.is-loading .wp-parsely-loading .wp-parsely-loading-message{max-width:37.5rem;text-align:center}.traffic-boost-preview .wp-parsely-preview .wp-parsely-preview-loading .components-spinner{height:var(--grid-unit-40);margin:0;width:var(--grid-unit-40)}.traffic-boost-preview .wp-parsely-preview .wp-parsely-preview-generating-placement{align-items:center;display:flex;inset:0;justify-content:center;pointer-events:none;position:absolute;z-index:3}.traffic-boost-preview-header{align-items:center;background:var(--white);border-bottom:1px solid var(--gray-400);display:flex;justify-content:space-between;padding:var(--grid-unit-15) var(--grid-unit-20);z-index:5}.traffic-boost-preview-header .traffic-boost-preview-actions{display:flex;gap:var(--grid-unit-10)}.traffic-boost-preview-header .traffic-boost-preview-actions button{border-radius:.25rem;height:1.875rem;min-width:1.875rem;padding:var(--grid-unit-5)}.traffic-boost-preview-header .traffic-boost-preview-actions button:hover{color:var(--wp-admin-theme-color)}.traffic-boost-preview-header .traffic-boost-preview-actions .components-button.is-pressed:focus:not(:disabled){box-shadow:none}.traffic-boost-preview-header .traffic-boost-preview-info{align-items:flex-start;display:flex;flex:1 0 0;flex-direction:column;gap:var(--grid-unit-5);min-width:0}.traffic-boost-preview-header .traffic-boost-preview-info .traffic-boost-preview-info-title{color:var(--gray-900);font-size:var(--font-size--large);font-weight:500;line-height:var(--font-size--extra-large);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;width:100%}.traffic-boost-preview-header .traffic-boost-preview-info .traffic-boost-preview-info-links{align-items:center;align-self:stretch;display:flex;font-size:var(--font-size--medium);font-weight:400;gap:var(--grid-unit-10);line-height:var(--font-size--extra-large);min-height:var(--grid-unit-30)}.traffic-boost-preview-header .traffic-boost-preview-info .traffic-boost-preview-info-links .traffic-boost-preview-info-links-counter{align-items:center;align-self:stretch;display:flex;gap:var(--grid-unit-10)}.traffic-boost-preview-header .traffic-boost-preview-info .traffic-boost-preview-info-links .traffic-boost-preview-info-links-counter button{height:1.375rem;padding:var(--grid-unit-5) var(--grid-unit-10)}.traffic-boost-preview-header .traffic-boost-preview-info .traffic-boost-preview-info-links .traffic-boost-preview-info-links-counter button.is-pressed,.traffic-boost-preview-header .traffic-boost-preview-info .traffic-boost-preview-info-links .traffic-boost-preview-info-links-counter button:focus{box-shadow:0 0 0 1px var(--black);outline:none}.traffic-boost-preview-header .traffic-boost-preview-info .traffic-boost-preview-info-links .traffic-boost-preview-info-links-counter button:not(.is-pressed):focus{box-shadow:inset 0 0 0 1px var(--wp-admin-theme-color);color:var(--wp-admin-theme-color)}.wp-parsely-traffic-boost-preview-settings-popover{margin-top:var(--grid-unit-20);width:17.1875rem}.wp-parsely-traffic-boost-preview-settings-popover-content{min-width:13.75rem;padding:var(--grid-unit-20);width:17.1875rem}.traffic-boost-preview-footer{align-items:center;background:var(--white);border-top:1px solid var(--gray-400);display:flex;justify-content:space-between;padding:var(--grid-unit-10) var(--grid-unit-20)}.traffic-boost-preview-footer .traffic-boost-preview-footer-next,.traffic-boost-preview-footer .traffic-boost-preview-footer-previous{min-width:2.25rem}.traffic-boost-preview-footer .traffic-boost-preview-footer-actions{align-items:center;display:flex;flex-wrap:wrap;gap:var(--grid-unit-10);justify-content:center}.traffic-boost-preview-footer .traffic-boost-preview-footer-actions .components-button{height:2.25rem}.traffic-boost-preview-footer .traffic-boost-preview-footer-actions .traffic-boost-preview-footer-navigation{align-items:center;display:flex;font-size:var(--font-size--small);font-weight:500;gap:var(--grid-unit-5);line-height:var(--font-size--medium);text-transform:uppercase}.traffic-boost-preview-footer .traffic-boost-preview-footer-actions .traffic-boost-preview-footer-navigation select{border:none;font-size:var(--font-size--small);font-weight:500;line-height:var(--font-size--medium);margin-bottom:2px} +#wp-parsely-dashboard-widget,.components-button[aria-controls="wp-parsely-block-editor-sidebar:wp-parsely-content-helper"],.editor-post-excerpt,.parsely-dashboard-container,.settings_page_parsely,.wp-parsely-block-overlay,.wp-parsely-content-helper,.wp-parsely-excerpt-generator,.wp-parsely-panel,.wp-parsely-popover,.wp-parsely-preview-editor,.wp-parsely-smart-linking-close-dialog,.wp-parsely-smart-linking-review-modal,.wp-parsely-suggested-title-modal,.wp-parsely-traffic-boost-add-new-link-popover,.wp-parsely-traffic-boost-preview-settings-popover{--base-font:"source-sans-pro",arial,sans-serif;--numeric-font:"ff-din-round-web",sans-serif;--parsely-green-components:107,42%,46%;--parsely-green:hsl(var(--parsely-green-components));--parsely-green-10:#c7ecb1;--parsely-green-65:#2a691b;--gray-200:#f7f8f9;--gray-300:#edeeef;--gray-400:#d7dbdf;--gray-500:#959da5;--gray-600:#586069;--gray-700:#444d56;--gray-900:#24292e;--blue-500:#44a8e5;--blue-550:#2596db;--green-500:#7bc01b;--green-900:#3d6307;--ref-direct:205,13%,52%;--ref-internal:161,91%,41%;--ref-social:210,72%,41%;--ref-search:42,100%,50%;--ref-other:3,76%,58%;--base-text:var(--gray-900);--base-text-2:var(--gray-600);--base-3:var(--gray-400);--border:var(--gray-400);--data:var(--green-500);--control:var(--blue-500);--grid-unit-5:0.25rem;--grid-unit-10:0.5rem;--grid-unit-15:0.75rem;--grid-unit-20:1rem;--grid-unit-25:1.25rem;--grid-unit-30:1.5rem;--grid-unit-40:2rem;--grid-unit-50:2.5rem;--grid-unit-60:3rem;--font-size--smaller:0.688rem;--font-size--small:0.75rem;--font-size--medium:0.875rem;--font-size--large:1rem;--font-size--extra-large:1.2rem;--font-size--bigger:2rem;--dashboard-width:64rem;--black:#000;--dashboard-accent:#b9f16b;--gray-350:#e1e3e5;--sidebar-black:#1e1e1e;--sidebar-white:#f0f0f0;--traffic-boost-header-background:rgba(56,88,233,.08);--white:#fff}.traffic-boost-preview{align-self:stretch;background-color:var(--sidebar-white);border-radius:.5rem;display:flex;flex:1 0 0;flex-direction:column;min-width:18.75rem;overflow:hidden;padding:0}.traffic-boost-preview .wp-parsely-preview{background:var(--sidebar-white);display:flex;flex:1;height:100%;overflow:hidden}.traffic-boost-preview .wp-parsely-preview .preview-iframe-wrapper{height:100%;overflow:hidden;position:relative;width:100%}.traffic-boost-preview .wp-parsely-preview .wp-parsely-preview-iframe{background:var(--sidebar-white);border:none;height:100%;left:0;overflow-x:hidden;overflow-y:auto;position:absolute;top:0;transition:all .5s ease;width:100%;z-index:0}.traffic-boost-preview .wp-parsely-preview .wp-parsely-preview-iframe.is-loading{filter:blur(4px);opacity:.7}.traffic-boost-preview .wp-parsely-preview .wp-parsely-preview-loading{align-items:center;background-color:var(--sidebar-white);display:flex;inset:0;justify-content:center;opacity:0;pointer-events:none;position:absolute;transition:opacity .1s ease-in-out,visibility .1s ease-in-out;visibility:hidden;z-index:3}.traffic-boost-preview .wp-parsely-preview .wp-parsely-preview-loading.is-loading{align-items:center;display:flex;flex-direction:column;font-size:var(--font-size--extra-large);font-weight:500;gap:var(--grid-unit-30);justify-content:center;line-height:var(--font-size--extra-large);opacity:1;visibility:visible}.traffic-boost-preview .wp-parsely-preview .wp-parsely-preview-loading.is-loading .wp-parsely-loading{width:90%}.traffic-boost-preview .wp-parsely-preview .wp-parsely-preview-loading.is-loading .wp-parsely-loading .wp-parsely-loading-message{max-width:37.5rem;text-align:center}.traffic-boost-preview .wp-parsely-preview .wp-parsely-preview-loading .components-spinner{height:var(--grid-unit-40);margin:0;width:var(--grid-unit-40)}.traffic-boost-preview .wp-parsely-preview .wp-parsely-preview-generating-placement{align-items:center;display:flex;inset:0;justify-content:center;pointer-events:none;position:absolute;z-index:3}.traffic-boost-preview-header{align-items:center;background:var(--white);border-bottom:1px solid var(--gray-400);display:flex;justify-content:space-between;padding:var(--grid-unit-15) var(--grid-unit-20);z-index:5}.traffic-boost-preview-header .traffic-boost-preview-header-actions{display:flex;gap:var(--grid-unit-10)}.traffic-boost-preview-header .traffic-boost-preview-header-actions button{border-radius:.25rem;height:1.875rem;min-width:1.875rem;padding:var(--grid-unit-5)}.traffic-boost-preview-header .traffic-boost-preview-header-actions button:hover{color:var(--wp-admin-theme-color)}.traffic-boost-preview-header .traffic-boost-preview-header-actions .components-button.is-pressed:focus:not(:disabled){box-shadow:none}.traffic-boost-preview-header .traffic-boost-preview-info{align-items:flex-start;display:flex;flex:1 0 0;flex-direction:column;gap:var(--grid-unit-5);min-width:0}.traffic-boost-preview-header .traffic-boost-preview-info .traffic-boost-preview-info-title{color:var(--gray-900);font-size:var(--font-size--large);font-weight:500;line-height:var(--font-size--extra-large);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;width:100%}.traffic-boost-preview-header .traffic-boost-preview-info .traffic-boost-preview-info-links{align-items:center;align-self:stretch;display:flex;font-size:var(--font-size--medium);font-weight:400;gap:var(--grid-unit-10);line-height:var(--font-size--extra-large);min-height:var(--grid-unit-30)}.traffic-boost-preview-header .traffic-boost-preview-info .traffic-boost-preview-info-links .traffic-boost-preview-info-links-counter{align-items:center;align-self:stretch;display:flex;gap:var(--grid-unit-10)}.traffic-boost-preview-header .traffic-boost-preview-info .traffic-boost-preview-info-links .traffic-boost-preview-info-links-counter button{height:1.375rem;padding:var(--grid-unit-5) var(--grid-unit-10)}.traffic-boost-preview-header .traffic-boost-preview-info .traffic-boost-preview-info-links .traffic-boost-preview-info-links-counter button.is-pressed,.traffic-boost-preview-header .traffic-boost-preview-info .traffic-boost-preview-info-links .traffic-boost-preview-info-links-counter button:focus{box-shadow:0 0 0 1px var(--black);outline:none}.traffic-boost-preview-header .traffic-boost-preview-info .traffic-boost-preview-info-links .traffic-boost-preview-info-links-counter button:not(.is-pressed):focus{box-shadow:inset 0 0 0 1px var(--wp-admin-theme-color);color:var(--wp-admin-theme-color)}.wp-parsely-traffic-boost-preview-settings-popover{margin-top:var(--grid-unit-20);width:17.1875rem}.wp-parsely-traffic-boost-preview-settings-popover-content{min-width:13.75rem;padding:var(--grid-unit-20);width:17.1875rem}.traffic-boost-preview-footer{align-items:center;background:var(--white);border-top:1px solid var(--gray-400);display:flex;justify-content:space-between;padding:var(--grid-unit-10) var(--grid-unit-20)}.traffic-boost-preview-footer .traffic-boost-preview-footer-next,.traffic-boost-preview-footer .traffic-boost-preview-footer-previous{min-width:2.25rem}.traffic-boost-preview-footer .traffic-boost-preview-footer-actions{align-items:center;display:flex;flex-wrap:wrap;gap:var(--grid-unit-10);justify-content:center}.traffic-boost-preview-footer .traffic-boost-preview-footer-actions .components-button{height:2.25rem}.traffic-boost-preview-footer .traffic-boost-preview-footer-actions .traffic-boost-preview-footer-navigation{align-items:center;display:flex;font-size:var(--font-size--small);font-weight:500;gap:var(--grid-unit-5);line-height:var(--font-size--medium);text-transform:uppercase}.traffic-boost-preview-footer .traffic-boost-preview-footer-actions .traffic-boost-preview-footer-navigation select{border:none;font-size:var(--font-size--small);font-weight:500;line-height:var(--font-size--medium);margin-bottom:2px} .traffic-boost-links{display:flex;flex:1;flex-direction:column;height:100%;min-height:0}.traffic-boost-links .traffic-boost-links-list{display:flex;flex:1 1 auto;flex-direction:column;height:0;min-height:0;overflow-y:auto;overscroll-behavior:contain}.traffic-boost-links .traffic-boost-links-list .traffic-boost-single-link{align-items:center;border-bottom:1px solid var(--gray-350);cursor:pointer;display:flex;flex:0 0 auto;gap:var(--grid-unit-15);padding:var(--grid-unit-20) var(--grid-unit-30)}.traffic-boost-links .traffic-boost-links-list .traffic-boost-single-link.active{background:var(--Scales-Theme-theme-alpha-04,rgba(56,88,233,.04));border-bottom:1px solid var(--traffic-boost-header-background)}.traffic-boost-links .traffic-boost-links-list .traffic-boost-single-link:focus{box-shadow:none;outline:2px solid var(--wp-admin-theme-color);outline-offset:-2px}.traffic-boost-links .traffic-boost-links-list .traffic-boost-single-link .single-link-title{color:var(--sidebar-black);font-size:var(--font-size--medium);font-style:normal;font-weight:400;line-height:var(--grid-unit-30);overflow:hidden;text-overflow:ellipsis}.traffic-boost-links .links-pagination{align-items:center;color:var(--sidebar-black);display:flex;flex:0 0 auto;font-size:var(--font-size--smaller);font-style:normal;font-weight:500;justify-content:space-between;line-height:var(--grid-unit-20);margin-top:auto;padding:var(--grid-unit-15) var(--grid-unit-30);text-transform:uppercase}.traffic-boost-links .links-pagination .links-pagination-children{margin-right:auto}.traffic-boost-links .links-pagination .links-pagination-children .traffic-boost-add-suggestion{box-shadow:none}.traffic-boost-links .links-pagination .page-selector{align-items:center;display:flex;gap:var(--grid-unit-5)}.traffic-boost-links .links-pagination .page-selector select{background-color:transparent;border:none;cursor:pointer;font-size:var(--font-size--smaller);font-weight:500}.traffic-boost-links .links-pagination .page-selector select:focus{outline:2px solid var(--wp-admin-theme-color);outline-offset:1px}.traffic-boost-links .links-pagination .page-navigation{display:flex;gap:var(--grid-unit-5);margin-left:var(--grid-unit-30)}.traffic-boost-links .traffic-boost-links-list-loading{align-items:center;display:flex;gap:var(--grid-unit-10);height:100%;justify-content:center;padding:var(--grid-unit-20)} #wp-parsely-dashboard-widget,.components-button[aria-controls="wp-parsely-block-editor-sidebar:wp-parsely-content-helper"],.editor-post-excerpt,.parsely-dashboard-container,.settings_page_parsely,.wp-parsely-block-overlay,.wp-parsely-content-helper,.wp-parsely-excerpt-generator,.wp-parsely-panel,.wp-parsely-popover,.wp-parsely-preview-editor,.wp-parsely-smart-linking-close-dialog,.wp-parsely-smart-linking-review-modal,.wp-parsely-suggested-title-modal,.wp-parsely-traffic-boost-add-new-link-popover,.wp-parsely-traffic-boost-preview-settings-popover{--base-font:"source-sans-pro",arial,sans-serif;--numeric-font:"ff-din-round-web",sans-serif;--parsely-green-components:107,42%,46%;--parsely-green:hsl(var(--parsely-green-components));--parsely-green-10:#c7ecb1;--parsely-green-65:#2a691b;--gray-200:#f7f8f9;--gray-300:#edeeef;--gray-400:#d7dbdf;--gray-500:#959da5;--gray-600:#586069;--gray-700:#444d56;--gray-900:#24292e;--blue-500:#44a8e5;--blue-550:#2596db;--green-500:#7bc01b;--green-900:#3d6307;--ref-direct:205,13%,52%;--ref-internal:161,91%,41%;--ref-social:210,72%,41%;--ref-search:42,100%,50%;--ref-other:3,76%,58%;--base-text:var(--gray-900);--base-text-2:var(--gray-600);--base-3:var(--gray-400);--border:var(--gray-400);--data:var(--green-500);--control:var(--blue-500);--grid-unit-5:0.25rem;--grid-unit-10:0.5rem;--grid-unit-15:0.75rem;--grid-unit-20:1rem;--grid-unit-25:1.25rem;--grid-unit-30:1.5rem;--grid-unit-40:2rem;--grid-unit-50:2.5rem;--grid-unit-60:3rem;--font-size--smaller:0.688rem;--font-size--small:0.75rem;--font-size--medium:0.875rem;--font-size--large:1rem;--font-size--extra-large:1.2rem;--font-size--bigger:2rem;--dashboard-width:64rem;--black:#000;--dashboard-accent:#b9f16b;--gray-350:#e1e3e5;--sidebar-black:#1e1e1e;--sidebar-white:#f0f0f0;--traffic-boost-header-background:rgba(56,88,233,.08);--white:#fff}.traffic-boost-sidebar{align-items:flex-start;background:var(--white);border-bottom:1px solid var(--gray-200);border-radius:var(--grid-unit-10);box-shadow:0 1px 1px 0 rgba(0,0,0,.03),0 1px 2px 0 rgba(0,0,0,.02),0 .1875rem .1875rem 0 rgba(0,0,0,.02),0 .25rem .25rem 0 rgba(0,0,0,.01);display:flex;flex-direction:column;height:calc(100vh - 4.0625rem);max-width:28.125rem;min-width:20rem}.traffic-boost-sidebar .traffic-boost-sidebar-header{align-items:center;align-self:stretch;background:var(--traffic-boost-header-background);display:flex;flex-direction:column;gap:var(--grid-unit-20);min-height:var(--grid-unit-50);padding:var(--grid-unit-20) var(--grid-unit-30)}.traffic-boost-sidebar .traffic-boost-sidebar-header .traffic-boost-sidebar-header-nav{align-items:center;align-self:stretch;display:flex;justify-content:space-between}.traffic-boost-sidebar .traffic-boost-sidebar-header .traffic-boost-sidebar-header-nav .components-button{font-size:var(--font-size--medium);font-weight:400;line-height:var(--grid-unit-25);padding-left:0;padding-right:0}.traffic-boost-sidebar .traffic-boost-sidebar-header .traffic-boost-post-details{align-items:flex-start;align-self:stretch;display:flex;flex-direction:column;gap:var(--grid-unit-10);justify-content:flex-end}.traffic-boost-sidebar .traffic-boost-sidebar-header .traffic-boost-post-details .traffic-boost-post-details-label{color:var(--gray-900);font-size:var(--font-size--small);font-style:normal;font-weight:500;line-height:var(--grid-unit-20);overflow:hidden;text-transform:uppercase}.traffic-boost-sidebar .traffic-boost-sidebar-header .traffic-boost-post-details .traffic-boost-post-details-content{align-items:center;align-self:stretch;display:flex;gap:var(--grid-unit-10)}.traffic-boost-sidebar .traffic-boost-sidebar-header .traffic-boost-post-details .traffic-boost-post-details-content .post-title{color:var(--gray-900);font-size:var(--font-size--large);font-style:normal;font-weight:500;line-height:var(--grid-unit-25)}.traffic-boost-sidebar .traffic-boost-sidebar-header .traffic-boost-post-details .traffic-boost-post-details-divider{background:var(--gray-400);height:1px;margin:var(--grid-unit-10) 0;width:100%}.traffic-boost-sidebar .traffic-boost-sidebar-header .traffic-boost-post-details .traffic-boost-post-details-description{font-size:var(--font-size--medium);font-style:normal;font-weight:400;line-height:var(--grid-unit-20)}.traffic-boost-sidebar .traffic-boost-sidebar-content{display:flex;flex:1;flex-direction:column;gap:var(--grid-unit-20);min-height:0;width:100%}.traffic-boost-sidebar .traffic-boost-sidebar-content .traffic-boost-suggestions-settings .components-panel__body{border-bottom:0;border-top:0}.traffic-boost-sidebar .traffic-boost-sidebar-content .traffic-boost-suggestions-empty-state{align-items:center;display:flex;flex-direction:column;gap:var(--grid-unit-20);padding:var(--grid-unit-20)}.traffic-boost-sidebar .traffic-boost-sidebar-content .traffic-boost-suggestions-generating-footer{align-items:center;display:flex;gap:var(--grid-unit-10);justify-content:center;min-height:2.25rem;width:100%}.traffic-boost-sidebar .traffic-boost-sidebar-content .traffic-boost-suggestions-generating-footer span{margin-top:.3125rem}.traffic-boost-sidebar .traffic-boost-sidebar-tabs{display:flex;flex:1;flex-direction:column;width:100%}.traffic-boost-sidebar .traffic-boost-sidebar-tabs .components-tab-panel__tab-content{display:flex;flex:1;flex-direction:column;min-height:0;overflow:hidden}.traffic-boost-sidebar .traffic-boost-sidebar-tabs .components-tab-panel__tabs{border-bottom:1px solid var(--gray-400);border-top:1px solid var(--gray-400);display:flex;margin:0;padding:0;position:relative;width:100%}.traffic-boost-sidebar .traffic-boost-sidebar-tabs .components-tab-panel__tabs .components-tab-panel__tabs-item{flex:1;height:var(--grid-unit-60);justify-content:center;margin:0;padding:var(--grid-unit-15)}.traffic-boost-sidebar .traffic-boost-sidebar-tabs .components-tab-panel__tabs .components-tab-panel__tabs-item.is-active{box-shadow:inset 0 0 0 var(--wp-admin-border-width-focus) transparent,inset 0 -1.5px 0 0 var(--wp-admin-theme-color)}.traffic-boost-sidebar .traffic-boost-sidebar-tabs .components-tab-panel__tabs .components-tab-panel__tabs-item .tab-count{align-items:center;background:var(--gray-400);border-radius:2px;display:inline-flex;gap:var(--grid-unit-10);justify-content:center;margin-left:var(--grid-unit-20);padding:var(--grid-unit-5) var(--grid-unit-10)}.traffic-boost-sidebar .traffic-boost-sidebar-tabs .components-tab-panel__tabs .components-tab-panel__tabs-item.icon-only-tab{flex:0 0 var(--grid-unit-50);justify-content:center!important;min-width:var(--grid-unit-60)!important;padding:var(--grid-unit-10)!important;width:var(--grid-unit-60)!important}.traffic-boost-sidebar .traffic-boost-sidebar-tabs .components-tab-panel__tabs .components-tab-panel__tabs-item.icon-only-tab .icon{color:var(--gray-600);height:var(--grid-unit-30)!important;width:var(--grid-unit-30)!important}.traffic-boost-sidebar .traffic-boost-sidebar-tabs .components-tab-panel__tabs .components-tab-panel__tabs-item.icon-only-tab .icon svg{height:var(--grid-unit-30)!important;width:var(--grid-unit-30)!important}.traffic-boost-sidebar .traffic-boost-sidebar-tabs .components-tab-panel__tabs .components-tab-panel__tabs-item.icon-only-tab:hover .icon{color:var(--gray-900)}.traffic-boost-sidebar .traffic-boost-sidebar-tabs .components-tab-panel__tabs .components-tab-panel__tabs-item .suggestions-tab{order:1}.traffic-boost-sidebar .traffic-boost-sidebar-tabs .components-tab-panel__tabs .components-tab-panel__tabs-item .inbound-links-tab{order:2}.traffic-boost-sidebar .traffic-boost-suggestions-loading-generating{align-items:center;display:flex;flex-direction:column;gap:var(--grid-unit-15);height:100%;justify-content:center;padding:var(--grid-unit-20)}.wp-parsely-traffic-boost-add-new-link-popover{width:25rem}.wp-parsely-traffic-boost-add-new-link-popover .components-popover__content{display:flex;flex-direction:column;gap:var(--grid-unit-20);padding:var(--grid-unit-20);width:100%}.wp-parsely-traffic-boost-add-new-link-popover .traffic-boost-add-link-table.no-results{text-align:left}.wp-parsely-traffic-boost-add-new-link-popover .traffic-boost-add-link-table tr{cursor:pointer} .parsely-tone-selector-dropdown{align-items:center;align-self:stretch;background:var(--Gutenberg-White,#fff);border:1px solid var(--Gutenberg-Gray-600,#949494);border-radius:2px;height:2.25rem;overflow-wrap:break-word;width:100%;word-break:break-word}.parsely-tone-selector-dropdown button{height:2.0625rem}.parsely-tone-selector-dropdown .components-dropdown-menu__toggle.has-icon svg:first-child{display:none}.parsely-tone-selector-dropdown.is-disabled{opacity:.5;pointer-events:none}.parsely-tone-selector-dropdown .components-dropdown-menu__toggle{display:flex;gap:.625rem;width:100%}.parsely-tone-selector-dropdown .components-dropdown-menu__toggle svg:first-of-type path{transform:scale(1.4);transform-origin:center}.parsely-tone-selector-dropdown .parsely-tone-selector-label{flex-grow:2;padding:0 var(--grid-unit-10);text-align:left}.parsely-tone-selector-dropdown .parsely-tone-selector-label:first-letter{text-transform:uppercase}.parsely-persona-selector-custom{width:100%}.parsely-persona-selector-custom .components-base-control__field input{display:flex;height:2.5rem;padding:var(--grid-unit-15) var(--grid-unit-20)}.parsely-persona-selector-custom .components-base-control__field input,.parsely-persona-selector-dropdown{align-items:center;align-self:stretch;border:1px solid var(--Gutenberg-Gray-600,#949494);border-radius:2px}.parsely-persona-selector-dropdown{background:var(--Gutenberg-White,#fff);height:2.25rem;overflow-wrap:break-word;width:100%;word-break:break-word}.parsely-persona-selector-dropdown .components-dropdown-menu__toggle.has-icon svg:first-child{display:none}.parsely-persona-selector-dropdown button{height:2.0625rem}.parsely-persona-selector-dropdown.is-disabled{opacity:.5;pointer-events:none}.parsely-persona-selector-dropdown .components-dropdown-menu__toggle{display:flex;gap:.625rem;width:100%}.parsely-persona-selector-dropdown .parsely-persona-selector-label{flex-grow:2;padding:0 var(--grid-unit-10);text-align:left}.parsely-persona-selector-dropdown .parsely-persona-selector-label:first-letter{text-transform:uppercase}.parsely-tone-selector-custom{width:100%}.parsely-tone-selector-custom .components-base-control__field input{align-items:center;align-self:stretch;border:1px solid var(--Gutenberg-Gray-600,#949494);border-radius:2px;display:flex;height:2.5rem;padding:var(--grid-unit-15) var(--grid-unit-20)}.parsely-inputrange-control{align-items:flex-start;align-self:stretch;display:flex;flex-direction:column;gap:var(--grid-unit-10)}.parsely-inputrange-control .parsely-inputrange-control__label{margin:0}.parsely-inputrange-control .parsely-inputrange-control__controls{align-items:center;align-self:stretch;display:flex;gap:var(--grid-unit-20);height:2.5rem}.parsely-inputrange-control .parsely-inputrange-control__controls .components-input-control{display:flex;flex:1 0 0}.parsely-inputrange-control .parsely-inputrange-control__controls .components-input-control input[type=number]::-webkit-inner-spin-button,.parsely-inputrange-control .parsely-inputrange-control__controls .components-input-control input[type=number]::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.parsely-inputrange-control .parsely-inputrange-control__controls .components-input-control input[type=number]{-moz-appearance:textfield}.parsely-inputrange-control .parsely-inputrange-control__controls .components-input-control .components-base-control__field{flex-grow:1}.parsely-inputrange-control .parsely-inputrange-control__controls .components-input-control .components-input-control__suffix{color:var(--wp-components-color-accent,var(--wp-admin-theme-color,#3858e9))}.parsely-inputrange-control .parsely-inputrange-control__controls .components-range-control{flex:1 0 0;height:36px}.parsely-inputrange-control .parsely-inputrange-control__controls .components-range-control .components-range-control__root{height:2.5rem}#wp-parsely-dashboard-widget,.components-button[aria-controls="wp-parsely-block-editor-sidebar:wp-parsely-content-helper"],.editor-post-excerpt,.parsely-dashboard-container,.settings_page_parsely,.wp-parsely-block-overlay,.wp-parsely-content-helper,.wp-parsely-excerpt-generator,.wp-parsely-panel,.wp-parsely-popover,.wp-parsely-preview-editor,.wp-parsely-smart-linking-close-dialog,.wp-parsely-smart-linking-review-modal,.wp-parsely-suggested-title-modal,.wp-parsely-traffic-boost-add-new-link-popover,.wp-parsely-traffic-boost-preview-settings-popover{--base-font:"source-sans-pro",arial,sans-serif;--numeric-font:"ff-din-round-web",sans-serif;--parsely-green-components:107,42%,46%;--parsely-green:hsl(var(--parsely-green-components));--parsely-green-10:#c7ecb1;--parsely-green-65:#2a691b;--gray-200:#f7f8f9;--gray-300:#edeeef;--gray-400:#d7dbdf;--gray-500:#959da5;--gray-600:#586069;--gray-700:#444d56;--gray-900:#24292e;--blue-500:#44a8e5;--blue-550:#2596db;--green-500:#7bc01b;--green-900:#3d6307;--ref-direct:205,13%,52%;--ref-internal:161,91%,41%;--ref-social:210,72%,41%;--ref-search:42,100%,50%;--ref-other:3,76%,58%;--base-text:var(--gray-900);--base-text-2:var(--gray-600);--base-3:var(--gray-400);--border:var(--gray-400);--data:var(--green-500);--control:var(--blue-500);--grid-unit-5:0.25rem;--grid-unit-10:0.5rem;--grid-unit-15:0.75rem;--grid-unit-20:1rem;--grid-unit-25:1.25rem;--grid-unit-30:1.5rem;--grid-unit-40:2rem;--grid-unit-50:2.5rem;--grid-unit-60:3rem;--font-size--smaller:0.688rem;--font-size--small:0.75rem;--font-size--medium:0.875rem;--font-size--large:1rem;--font-size--extra-large:1.2rem;--font-size--bigger:2rem;--dashboard-width:64rem;--black:#000;--dashboard-accent:#b9f16b;--gray-350:#e1e3e5;--sidebar-black:#1e1e1e;--sidebar-white:#f0f0f0;--traffic-boost-header-background:rgba(56,88,233,.08);--white:#fff}.parsely-thumbnail{border-radius:3px;flex-shrink:0;overflow:hidden}.parsely-thumbnail img{border-radius:3px;height:100%;-o-object-fit:cover;object-fit:cover;width:100%}.parsely-thumbnail .parsely-thumbnail-icon-container{align-items:center;background:var(--gray-500);border-radius:3px;display:flex;height:100%;justify-content:center;width:100%}.parsely-thumbnail .parsely-thumbnail-icon-container svg{fill:var(--sidebar-white);height:60%;width:60%}.wp-parsely-snackbar-notices{bottom:4rem;display:flex;flex-direction:column-reverse;gap:.375rem;padding:1rem;position:fixed;width:100%;z-index:999999}.wp-parsely-snackbar-notices svg{fill:currentcolor}.wp-parsely-loading{align-items:center;border-radius:.25rem;color:var(--sidebar-black);display:flex;gap:var(--grid-unit-20);justify-content:center;min-height:1.5rem;min-width:25rem;padding:var(--grid-unit-10) var(--grid-unit-15)}.wp-parsely-loading .components-spinner{flex-shrink:0;margin:0}.wp-parsely-loading .wp-parsely-loading-message{flex-grow:1;font-size:var(--font-size--medium);line-height:1.4;margin:0;max-width:25rem}.wp-parsely-loading .wp-parsely-typewriter-text{display:inline-block;min-height:1.4em;min-width:1ch}.wp-parsely-loading .wp-parsely-typewriter-cursor{animation:blink 1s step-end infinite;display:inline-block;font-weight:400;margin-left:2px;opacity:.7}@keyframes blink{0%,to{opacity:0}50%{opacity:1}}.parsely-panel-settings{width:100%}.parsely-panel-settings .parsely-panel-settings-header{display:flex;margin:.625rem 0;width:100%}.parsely-panel-settings .parsely-panel-settings-header .parsely-panel-settings-header-label{flex-grow:2;margin:0 0 0 .3125rem;text-align:left}.parsely-panel-settings .parsely-panel-settings-header .parsely-panel-settings-header-label .components-base-control__field{align-items:center;display:flex;justify-content:space-between}.parsely-panel-settings .parsely-panel-settings-header .parsely-panel-settings-header-label .components-base-control__field .components-base-control__label{margin-bottom:0}.parsely-panel-settings .parsely-panel-settings-body{align-self:stretch;display:flex;flex-direction:column;gap:.625rem;padding:.375rem 0 var(--grid-unit-20) 0}.wp-parsely-dropdown-label{align-self:stretch;color:var(--sidebar-black);font-size:var(--font-size--smaller);font-style:normal;font-weight:600;line-height:var(--grid-unit-20);text-transform:uppercase}.wp-parsely-content-helper-error{align-self:flex-start;margin:0}.wp-parsely-content-helper-error p{margin:0}.wp-parsely-content-helper-error .content-helper-error-message{margin:0!important}.wp-parsely-collapsible-panel{align-items:flex-start;align-self:stretch;border-bottom:none;border-radius:2px;border-top:none;display:flex;flex-direction:column;gap:var(--grid-unit-20);margin:0 0 .5rem;padding:0}.wp-parsely-collapsible-panel .components-panel__body-title{align-items:center;align-self:stretch;display:flex;margin:0 -1rem .375rem;padding:0}.wp-parsely-collapsible-panel .components-panel__body-title .components-panel__icon,.wp-parsely-collapsible-panel .components-panel__body-title svg{margin-left:0;margin-right:var(--grid-unit-10);order:-1}.wp-parsely-collapsible-panel .components-panel__body-title:hover{background-color:transparent}.wp-parsely-collapsible-panel.is-opened{padding:0}.wp-parsely-collapsible-panel.is-opened>div:first-of-type{margin-top:calc(var(--grid-unit-20)*-1)}.wp-parsely-collapsible-panel.is-opened .components-panel__body-title{margin:0 calc(var(--grid-unit-20)*-1) .375rem;padding-bottom:0}.wp-parsely-collapsible-panel .components-panel__body-toggle.components-button{color:var(--Gutenberg-Gray-900,#1e1e1e);font-size:.6875rem;font-style:normal;font-weight:600;line-height:1rem;padding:var(--grid-unit-20) var(--grid-unit-20);text-transform:uppercase}.wp-parsely-collapsible-panel .components-panel__body-toggle.components-button .components-panel__arrow{margin-right:0}.wp-parsely-collapsible-panel .components-panel__body.is-opened{padding:0}.parsely-table-container.is-loading{align-items:center;display:flex;justify-content:center;padding:var(--grid-unit-20)}.parsely-table-container table{border-collapse:collapse;border-spacing:0;width:100%}.parsely-table-container table tr{position:relative}.parsely-table-container table tr:after{background-color:var(--border);bottom:0;content:"";height:1px;left:50%;position:absolute;transform:translateX(-50%);width:100vw}.parsely-table-container table tr td,.parsely-table-container table tr th{padding:var(--grid-unit-15) 0}.parsely-table-container table tr td.post-info,.parsely-table-container table tr th.post-info-header{text-align:left}.parsely-table-container table tr th{color:var(--sidebar-black);font-size:var(--font-size--small);font-style:normal;font-weight:600;line-height:var(--font-size--large);text-transform:uppercase}.parsely-table-container table tr th.views-header{min-width:3.75rem;text-align:center}.parsely-table-container table tr th.views-header .views-header-period{color:var(--gray-500);font-size:var(--font-size--smaller);font-weight:400;margin-left:var(--grid-unit-5)}.parsely-table-container table tr td.metrics{min-width:100px}.parsely-table-container table tr td.metrics .metrics-container{align-items:center;display:flex;flex-direction:column;gap:var(--grid-unit-5)}.parsely-table-container table tr td.metrics .metrics-container .metric-change{font-size:var(--font-size--small);font-weight:600;line-height:var(--font-size--large);margin-left:var(--grid-unit-5)}.parsely-table-container table tr td.metrics .metrics-container .metric-change-positive{color:var(--parsely-green)}.parsely-table-container table tr td.metrics .metrics-container .metric-change-negative{color:#900000}.parsely-table-container table tr td.metrics .metrics-container .metric-change-neutral{color:var(--gray-500)}.parsely-table-container table tr td.metrics .metrics-container .metric-boost-percentage{color:var(--parsely-green);font-size:var(--font-size--small);font-weight:600}.parsely-table-container table tr td.actions{align-items:center;display:flex;gap:var(--grid-unit-10);justify-content:flex-end;margin-left:var(--grid-unit-10);max-width:8.4375rem;min-height:2.5rem}.parsely-table-container table tr td.actions a{text-decoration:none}.parsely-table-container table tr td.actions .boost-link-container{display:flex;flex-direction:column;gap:.25rem;height:100%;text-align:center;width:100%}.parsely-table-container table tr td.actions .boost-link-container .boost-link-status{display:flex;gap:.25rem;justify-content:center}.parsely-table-container table tr td.actions .boost-link-container .boost-link-status .hidden{opacity:0}.parsely-table-container table tr td.actions .boost-link-container .boost-link-status .boost-inbound,.parsely-table-container table tr td.actions .boost-link-container .boost-link-status .boost-outbound{align-items:center;background-color:var(--gray-300);border-radius:.375rem;color:var(--sidebar-black);display:flex;flex-grow:1;font-size:var(--font-size--small);justify-content:center;line-height:var(--font-size--medium);padding:.25rem .5625rem}.parsely-table-container table tr td.actions .boost-link-container .boost-link-status .boost-link-status-icon-inbound{transform:rotate(180deg)}.parsely-table-container.no-results{color:#757575;font-style:italic;padding:var(--grid-unit-15);text-align:center}.parsely-table-container .suggestion-bubble{background:var(--parsely-green);border:none;border-radius:1.25rem;box-shadow:none;color:var(--sidebar-white);cursor:pointer;display:inline-block;font-size:.8125rem;line-height:.875rem;margin-left:.375rem;min-width:1.75rem;padding:3px .375rem;text-align:center}.parsely-table-container.compact table tr td.post-info{max-width:100px}.parsely-table-container.compact tr:after{width:100%}.parsely-table-container.compact .post-title{display:-webkit-box!important;overflow:hidden;-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical}.parsely-table-container.compact .post-title>span{white-space:normal!important}.parsely-table-container.hide-pagination table tr:last-child:after{display:none}.posts-table-post-info{align-items:flex-start;display:flex;flex:1 0 0;gap:var(--grid-unit-20);min-height:2.5rem}.posts-table-post-info .posts-table-thumbnail{align-self:center}.posts-table-post-info .post-details{align-items:flex-start;display:flex;flex:1 0 0;flex-direction:column;gap:var(--grid-unit-05,4px);justify-content:center;min-height:var(--grid-unit-40);min-width:0}.posts-table-post-info .post-details .post-title{align-items:center;align-self:stretch;display:flex;font-size:var(--font-size--medium);font-style:normal;font-weight:600;gap:var(--grid-unit-5);line-height:1.25rem;overflow:hidden;text-overflow:ellipsis}.posts-table-post-info .post-details .post-title>span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.posts-table-post-info .post-details .post-meta{align-content:center;align-items:center;align-self:stretch;color:var(--gray-500);display:flex;flex-wrap:wrap;gap:var(--grid-unit-5) var(--grid-unit-10)}.posts-table-post-info .post-details .post-categories{display:flex;gap:var(--grid-unit-5)}.posts-table-post-info .post-details .post-categories span{background:var(--gray-400);border-radius:.375rem;color:var(--sidebar-black);font-size:var(--font-size--smaller);font-weight:500;line-height:var(--font-size--large);padding:2px var(--grid-unit-10)}.posts-table-pagination-controls{align-items:center;align-self:stretch;display:flex;font-size:var(--font-size--smaller);font-style:normal;font-weight:600;gap:var(--grid-unit-30);justify-content:flex-end;line-height:var(--font-size--large);padding:var(--grid-unit-15);text-transform:uppercase}.posts-table-pagination-controls .page-selector{align-items:center;display:flex;gap:var(--grid-unit-10)}.posts-table-pagination-controls .page-navigation{align-items:center;display:flex;gap:var(--grid-unit-5);justify-content:flex-end}body{background:#fff}.parsely-dashboard-container{margin-left:-1.25rem;overflow:hidden}.parsely-dashboard-page-body,.parsely-dashboard-page-header{margin:0 auto;max-width:var(--dashboard-width);padding:var(--grid-unit-30)} diff --git a/build/content-helper/dashboard-page.js b/build/content-helper/dashboard-page.js index 7003c28179..d7f578db96 100644 --- a/build/content-helper/dashboard-page.js +++ b/build/content-helper/dashboard-page.js @@ -1,9 +1,9 @@ -!function(){"use strict";var e={69:function(e,t){Object.prototype.toString},20:function(e,t,n){var r=n(609),o=Symbol.for("react.element"),i=Symbol.for("react.fragment"),a=Object.prototype.hasOwnProperty,s=r.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,l={key:!0,ref:!0,__self:!0,__source:!0};function c(e,t,n){var r,i={},c=null,u=null;for(r in void 0!==n&&(c=""+n),void 0!==t.key&&(c=""+t.key),void 0!==t.ref&&(u=t.ref),t)a.call(t,r)&&!l.hasOwnProperty(r)&&(i[r]=t[r]);if(e&&e.defaultProps)for(r in t=e.defaultProps)void 0===i[r]&&(i[r]=t[r]);return{$$typeof:o,type:e,key:c,ref:u,props:i,_owner:s.current}}t.Fragment=i,t.jsx=c,t.jsxs=c},848:function(e,t,n){e.exports=n(20)},609:function(e){e.exports=window.React}},t={};function n(r){var o=t[r];if(void 0!==o)return o.exports;var i=t[r]={exports:{}};return e[r](i,i.exports,n),i.exports}n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,{a:t}),t},n.d=function(e,t){for(var r in t)n.o(t,r)&&!n.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},function(){var e=n(848),t=n(609),r=(n(69),"popstate");function o(e={}){return function(e,t,n,o={}){let{window:a=document.defaultView,v5Compat:u=!1}=o,d=a.history,p="POP",f=null,h=g();function g(){return(d.state||{idx:null}).idx}function v(){p="POP";let e=g(),t=null==e?null:e-h;h=e,f&&f({action:p,location:y.location,delta:t})}function m(e){return function(e,t=!1){let n="http://localhost";"undefined"!=typeof window&&(n="null"!==window.location.origin?window.location.origin:window.location.href),i(n,"No window.location.(origin|href) available to create URL");let r="string"==typeof e?e:c(e);return r=r.replace(/ $/,"%20"),!t&&r.startsWith("//")&&(r=n+r),new URL(r,n)}(e)}null==h&&(h=0,d.replaceState({...d.state,idx:h},""));let y={get action(){return p},get location(){return e(a,d)},listen(e){if(f)throw new Error("A history only accepts one active listener");return a.addEventListener(r,v),f=e,()=>{a.removeEventListener(r,v),f=null}},createHref(e){return t(a,e)},createURL:m,encodeLocation(e){let t=m(e);return{pathname:t.pathname,search:t.search,hash:t.hash}},push:function(e,t){p="PUSH";let r=l(y.location,e,t);n&&n(r,e),h=g()+1;let o=s(r,h),i=y.createHref(r);try{d.pushState(o,"",i)}catch(e){if(e instanceof DOMException&&"DataCloneError"===e.name)throw e;a.location.assign(i)}u&&f&&f({action:p,location:y.location,delta:1})},replace:function(e,t){p="REPLACE";let r=l(y.location,e,t);n&&n(r,e),h=g();let o=s(r,h),i=y.createHref(r);d.replaceState(o,"",i),u&&f&&f({action:p,location:y.location,delta:0})},go(e){return d.go(e)}};return y}((function(e,t){let{pathname:n="/",search:r="",hash:o=""}=u(e.location.hash.substring(1));return n.startsWith("/")||n.startsWith(".")||(n="/"+n),l("",{pathname:n,search:r,hash:o},t.state&&t.state.usr||null,t.state&&t.state.key||"default")}),(function(e,t){let n=e.document.querySelector("base"),r="";if(n&&n.getAttribute("href")){let t=e.location.href,n=t.indexOf("#");r=-1===n?t:t.slice(0,n)}return r+"#"+("string"==typeof t?t:c(t))}),(function(e,t){a("/"===e.pathname.charAt(0),`relative pathnames are not supported in hash history.push(${JSON.stringify(t)})`)}),e)}function i(e,t){if(!1===e||null==e)throw new Error(t)}function a(e,t){if(!e){"undefined"!=typeof console&&console.warn(t);try{throw new Error(t)}catch(e){}}}function s(e,t){return{usr:e.state,key:e.key,idx:t}}function l(e,t,n=null,r){return{pathname:"string"==typeof e?e:e.pathname,search:"",hash:"",..."string"==typeof t?u(t):t,state:n,key:t&&t.key||r||Math.random().toString(36).substring(2,10)}}function c({pathname:e="/",search:t="",hash:n=""}){return t&&"?"!==t&&(e+="?"===t.charAt(0)?t:"?"+t),n&&"#"!==n&&(e+="#"===n.charAt(0)?n:"#"+n),e}function u(e){let t={};if(e){let n=e.indexOf("#");n>=0&&(t.hash=e.substring(n),e=e.substring(0,n));let r=e.indexOf("?");r>=0&&(t.search=e.substring(r),e=e.substring(0,r)),e&&(t.pathname=e)}return t}function d(e,t,n="/"){return function(e,t,n,r){let o=E(("string"==typeof t?u(t):t).pathname||"/",n);if(null==o)return null;let i=p(e);!function(e){e.sort(((e,t)=>e.score!==t.score?t.score-e.score:function(e,t){return e.length===t.length&&e.slice(0,-1).every(((e,n)=>e===t[n]))?e[e.length-1]-t[t.length-1]:0}(e.routesMeta.map((e=>e.childrenIndex)),t.routesMeta.map((e=>e.childrenIndex)))))}(i);let a=null;for(let e=0;null==a&&e{let s={relativePath:void 0===a?e.path||"":a,caseSensitive:!0===e.caseSensitive,childrenIndex:o,route:e};s.relativePath.startsWith("/")&&(i(s.relativePath.startsWith(r),`Absolute route path "${s.relativePath}" nested under path "${r}" is not valid. An absolute child route path must start with the combined path of all its parent routes.`),s.relativePath=s.relativePath.slice(r.length));let l=T([r,s.relativePath]),c=n.concat(s);e.children&&e.children.length>0&&(i(!0!==e.index,`Index routes must not have child routes. Please remove all child routes from route path "${l}".`),p(e.children,t,c,l)),(null!=e.path||e.index)&&t.push({path:l,score:x(l,e.index),routesMeta:c})};return e.forEach(((e,t)=>{if(""!==e.path&&e.path?.includes("?"))for(let n of f(e.path))o(e,t,n);else o(e,t)})),t}function f(e){let t=e.split("/");if(0===t.length)return[];let[n,...r]=t,o=n.endsWith("?"),i=n.replace(/\?$/,"");if(0===r.length)return o?[i,""]:[i];let a=f(r.join("/")),s=[];return s.push(...a.map((e=>""===e?i:[i,e].join("/")))),o&&s.push(...a),s.map((t=>e.startsWith("/")&&""===t?"/":t))}new WeakMap;var h=/^:[\w-]+$/,g=3,v=2,m=1,y=10,b=-2,w=e=>"*"===e;function x(e,t){let n=e.split("/"),r=n.length;return n.some(w)&&(r+=b),t&&(r+=v),n.filter((e=>!w(e))).reduce(((e,t)=>e+(h.test(t)?g:""===t?m:y)),r)}function k(e,t,n=!1){let{routesMeta:r}=e,o={},i="/",a=[];for(let e=0;e(r.push({paramName:t,isOptional:null!=n}),n?"/?([^\\/]+)?":"/([^\\/]+)")));return e.endsWith("*")?(r.push({paramName:"*"}),o+="*"===e||"/*"===e?"(.*)$":"(?:\\/(.+)|\\/*)$"):n?o+="\\/*$":""!==e&&"/"!==e&&(o+="(?:(?=\\/|$))"),[new RegExp(o,t?void 0:"i"),r]}(e.path,e.caseSensitive,e.end),o=t.match(n);if(!o)return null;let i=o[0],s=i.replace(/(.)\/+$/,"$1"),l=o.slice(1);return{params:r.reduce(((e,{paramName:t,isOptional:n},r)=>{if("*"===t){let e=l[r]||"";s=i.slice(0,i.length-e.length).replace(/(.)\/+$/,"$1")}const o=l[r];return e[t]=n&&!o?void 0:(o||"").replace(/%2F/g,"/"),e}),{}),pathname:i,pathnameBase:s,pattern:e}}function S(e){try{return e.split("/").map((e=>decodeURIComponent(e).replace(/\//g,"%2F"))).join("/")}catch(t){return a(!1,`The URL path "${e}" could not be decoded because it is a malformed URL segment. This is probably due to a bad percent encoding (${t}).`),e}}function E(e,t){if("/"===t)return e;if(!e.toLowerCase().startsWith(t.toLowerCase()))return null;let n=t.endsWith("/")?t.length-1:t.length,r=e.charAt(n);return r&&"/"!==r?null:e.slice(n)||"/"}function P(e,t,n,r){return`Cannot include a '${e}' character in a manually specified \`to.${t}\` field [${JSON.stringify(r)}]. Please separate it out to the \`to.${n}\` field. Alternatively you may provide the full path as a string in and the router will parse it for you.`}function L(e){let t=function(e){return e.filter(((e,t)=>0===t||e.route.path&&e.route.path.length>0))}(e);return t.map(((e,n)=>n===t.length-1?e.pathname:e.pathnameBase))}function j(e,t,n,r=!1){let o;"string"==typeof e?o=u(e):(o={...e},i(!o.pathname||!o.pathname.includes("?"),P("?","pathname","search",o)),i(!o.pathname||!o.pathname.includes("#"),P("#","pathname","hash",o)),i(!o.search||!o.search.includes("#"),P("#","search","hash",o)));let a,s=""===e||""===o.pathname,l=s?"/":o.pathname;if(null==l)a=n;else{let e=t.length-1;if(!r&&l.startsWith("..")){let t=l.split("/");for(;".."===t[0];)t.shift(),e-=1;o.pathname=t.join("/")}a=e>=0?t[e]:"/"}let c=function(e,t="/"){let{pathname:n,search:r="",hash:o=""}="string"==typeof e?u(e):e,i=n?n.startsWith("/")?n:function(e,t){let n=t.replace(/\/+$/,"").split("/");return e.split("/").forEach((e=>{".."===e?n.length>1&&n.pop():"."!==e&&n.push(e)})),n.length>1?n.join("/"):"/"}(n,t):t;return{pathname:i,search:C(r),hash:I(o)}}(o,a),d=l&&"/"!==l&&l.endsWith("/"),p=(s||"."===l)&&n.endsWith("/");return c.pathname.endsWith("/")||!d&&!p||(c.pathname+="/"),c}var T=e=>e.join("/").replace(/\/\/+/g,"/"),N=e=>e.replace(/\/+$/,"").replace(/^\/*/,"/"),C=e=>e&&"?"!==e?e.startsWith("?")?e:"?"+e:"",I=e=>e&&"#"!==e?e.startsWith("#")?e:"#"+e:"";function A(e){return null!=e&&"number"==typeof e.status&&"string"==typeof e.statusText&&"boolean"==typeof e.internal&&"data"in e}var R=["POST","PUT","PATCH","DELETE"],O=(new Set(R),["GET",...R]);new Set(O),Symbol("ResetLoaderData");var D=t.createContext(null);D.displayName="DataRouter";var G=t.createContext(null);G.displayName="DataRouterState";var B=t.createContext({isTransitioning:!1});B.displayName="ViewTransition",t.createContext(new Map).displayName="Fetchers",t.createContext(null).displayName="Await";var M=t.createContext(null);M.displayName="Navigation";var F=t.createContext(null);F.displayName="Location";var U=t.createContext({outlet:null,matches:[],isDataRoute:!1});U.displayName="Route";var H=t.createContext(null);function V(){return null!=t.useContext(F)}function $(){return i(V(),"useLocation() may be used only in the context of a component."),t.useContext(F).location}H.displayName="RouteError";var W="You should call navigate() in a React.useEffect(), not when your component is first rendered.";function z(e){t.useContext(M).static||t.useLayoutEffect(e)}function q(){let{isDataRoute:e}=t.useContext(U);return e?function(){let{router:e}=function(e){let n=t.useContext(D);return i(n,ee(e)),n}("useNavigate"),n=te("useNavigate"),r=t.useRef(!1);return z((()=>{r.current=!0})),t.useCallback((async(t,o={})=>{a(r.current,W),r.current&&("number"==typeof t?e.navigate(t):await e.navigate(t,{fromRouteId:n,...o}))}),[e,n])}():function(){i(V(),"useNavigate() may be used only in the context of a component.");let e=t.useContext(D),{basename:n,navigator:r}=t.useContext(M),{matches:o}=t.useContext(U),{pathname:s}=$(),l=JSON.stringify(L(o)),c=t.useRef(!1);return z((()=>{c.current=!0})),t.useCallback(((t,o={})=>{if(a(c.current,W),!c.current)return;if("number"==typeof t)return void r.go(t);let i=j(t,JSON.parse(l),s,"path"===o.relative);null==e&&"/"!==n&&(i.pathname="/"===i.pathname?n:T([n,i.pathname])),(o.replace?r.replace:r.push)(i,o.state,o)}),[n,r,l,s,e])}()}function K(e,{relative:n}={}){let{matches:r}=t.useContext(U),{pathname:o}=$(),i=JSON.stringify(L(r));return t.useMemo((()=>j(e,JSON.parse(i),o,"path"===n)),[e,i,o,n])}function Z(e,n,r,o){i(V(),"useRoutes() may be used only in the context of a component.");let{navigator:s}=t.useContext(M),{matches:l}=t.useContext(U),c=l[l.length-1],p=c?c.params:{},f=c?c.pathname:"/",h=c?c.pathnameBase:"/",g=c&&c.route;{let e=g&&g.path||"";re(f,!g||e.endsWith("*")||e.endsWith("*?"),`You rendered descendant (or called \`useRoutes()\`) at "${f}" (under ) but the parent route path has no trailing "*". This means if you navigate deeper, the parent won't match anymore and therefore the child routes will never render.\n\nPlease change the parent to .`)}let v,m=$();if(n){let e="string"==typeof n?u(n):n;i("/"===h||e.pathname?.startsWith(h),`When overriding the location using \`\` or \`useRoutes(routes, location)\`, the location pathname must begin with the portion of the URL pathname that was matched by all parent routes. The current pathname base is "${h}" but pathname "${e.pathname}" was given in the \`location\` prop.`),v=e}else v=m;let y=v.pathname||"/",b=y;if("/"!==h){let e=h.replace(/^\//,"").split("/");b="/"+y.replace(/^\//,"").split("/").slice(e.length).join("/")}let w=d(e,{pathname:b});a(g||null!=w,`No routes matched location "${v.pathname}${v.search}${v.hash}" `),a(null==w||void 0!==w[w.length-1].route.element||void 0!==w[w.length-1].route.Component||void 0!==w[w.length-1].route.lazy,`Matched leaf route at location "${v.pathname}${v.search}${v.hash}" does not have an element or Component. This means it will render an with a null value by default resulting in an "empty" page.`);let x=function(e,n=[],r=null){if(null==e){if(!r)return null;if(r.errors)e=r.matches;else{if(0!==n.length||r.initialized||!(r.matches.length>0))return null;e=r.matches}}let o=e,a=r?.errors;if(null!=a){let e=o.findIndex((e=>e.route.id&&void 0!==a?.[e.route.id]));i(e>=0,`Could not find a matching route for errors on route IDs: ${Object.keys(a).join(",")}`),o=o.slice(0,Math.min(o.length,e+1))}let s=!1,l=-1;if(r)for(let e=0;e=0?o.slice(0,l+1):[o[0]];break}}}return o.reduceRight(((e,i,c)=>{let u,d=!1,p=null,f=null;r&&(u=a&&i.route.id?a[i.route.id]:void 0,p=i.route.errorElement||J,s&&(l<0&&0===c?(re("route-fallback",!1,"No `HydrateFallback` element provided to render during initial hydration"),d=!0,f=null):l===c&&(d=!0,f=i.route.hydrateFallbackElement||null)));let h=n.concat(o.slice(0,c+1)),g=()=>{let n;return n=u?p:d?f:i.route.Component?t.createElement(i.route.Component,null):i.route.element?i.route.element:e,t.createElement(X,{match:i,routeContext:{outlet:e,matches:h,isDataRoute:null!=r},children:n})};return r&&(i.route.ErrorBoundary||i.route.errorElement||0===c)?t.createElement(Q,{location:r.location,revalidation:r.revalidation,component:p,error:u,children:g(),routeContext:{outlet:null,matches:h,isDataRoute:!0}}):g()}),null)}(w&&w.map((e=>Object.assign({},e,{params:Object.assign({},p,e.params),pathname:T([h,s.encodeLocation?s.encodeLocation(e.pathname).pathname:e.pathname]),pathnameBase:"/"===e.pathnameBase?h:T([h,s.encodeLocation?s.encodeLocation(e.pathnameBase).pathname:e.pathnameBase])}))),l,r,o);return n&&x?t.createElement(F.Provider,{value:{location:{pathname:"/",search:"",hash:"",state:null,key:"default",...v},navigationType:"POP"}},x):x}function Y(){let e=function(){let e=t.useContext(H),n=function(e){let n=t.useContext(G);return i(n,ee(e)),n}("useRouteError"),r=te("useRouteError");return void 0!==e?e:n.errors?.[r]}(),n=A(e)?`${e.status} ${e.statusText}`:e instanceof Error?e.message:JSON.stringify(e),r=e instanceof Error?e.stack:null,o="rgba(200,200,200, 0.5)",a={padding:"0.5rem",backgroundColor:o},s={padding:"2px 4px",backgroundColor:o},l=null;return console.error("Error handled by React Router default ErrorBoundary:",e),l=t.createElement(t.Fragment,null,t.createElement("p",null,"💿 Hey developer 👋"),t.createElement("p",null,"You can provide a way better UX than this when your app throws errors by providing your own ",t.createElement("code",{style:s},"ErrorBoundary")," or"," ",t.createElement("code",{style:s},"errorElement")," prop on your route.")),t.createElement(t.Fragment,null,t.createElement("h2",null,"Unexpected Application Error!"),t.createElement("h3",{style:{fontStyle:"italic"}},n),r?t.createElement("pre",{style:a},r):null,l)}t.createContext(null);var J=t.createElement(Y,null),Q=class extends t.Component{constructor(e){super(e),this.state={location:e.location,revalidation:e.revalidation,error:e.error}}static getDerivedStateFromError(e){return{error:e}}static getDerivedStateFromProps(e,t){return t.location!==e.location||"idle"!==t.revalidation&&"idle"===e.revalidation?{error:e.error,location:e.location,revalidation:e.revalidation}:{error:void 0!==e.error?e.error:t.error,location:t.location,revalidation:e.revalidation||t.revalidation}}componentDidCatch(e,t){console.error("React Router caught the following error during render",e,t)}render(){return void 0!==this.state.error?t.createElement(U.Provider,{value:this.props.routeContext},t.createElement(H.Provider,{value:this.state.error,children:this.props.component})):this.props.children}};function X({routeContext:e,match:n,children:r}){let o=t.useContext(D);return o&&o.static&&o.staticContext&&(n.route.errorElement||n.route.ErrorBoundary)&&(o.staticContext._deepestRenderedBoundaryId=n.route.id),t.createElement(U.Provider,{value:e},r)}function ee(e){return`${e} must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.`}function te(e){let n=function(e){let n=t.useContext(U);return i(n,ee(e)),n}(e),r=n.matches[n.matches.length-1];return i(r.route.id,`${e} can only be used on routes that contain a unique "id"`),r.route.id}var ne={};function re(e,t,n){t||ne[e]||(ne[e]=!0,a(!1,n))}function oe({to:e,replace:n,state:r,relative:o}){i(V()," may be used only in the context of a component.");let{static:s}=t.useContext(M);a(!s," must not be used on the initial render in a . This is a no-op, but you should modify your code so the is only ever rendered in response to some user interaction or state change.");let{matches:l}=t.useContext(U),{pathname:c}=$(),u=q(),d=j(e,L(l),c,"path"===o),p=JSON.stringify(d);return t.useEffect((()=>{u(JSON.parse(p),{replace:n,state:r,relative:o})}),[u,p,o,n,r]),null}function ie(e){i(!1,"A is only ever to be used as the child of element, never rendered directly. Please wrap your in a .")}function ae({basename:e="/",children:n=null,location:r,navigationType:o="POP",navigator:s,static:l=!1}){i(!V(),"You cannot render a inside another . You should never have more than one in your app.");let c=e.replace(/^\/*/,"/"),d=t.useMemo((()=>({basename:c,navigator:s,static:l,future:{}})),[c,s,l]);"string"==typeof r&&(r=u(r));let{pathname:p="/",search:f="",hash:h="",state:g=null,key:v="default"}=r,m=t.useMemo((()=>{let e=E(p,c);return null==e?null:{location:{pathname:e,search:f,hash:h,state:g,key:v},navigationType:o}}),[c,p,f,h,g,v,o]);return a(null!=m,` is not able to match the URL "${p}${f}${h}" because it does not start with the basename, so the won't render anything.`),null==m?null:t.createElement(M.Provider,{value:d},t.createElement(F.Provider,{children:n,value:m}))}function se({children:e,location:t}){return Z(le(e),t)}function le(e,n=[]){let r=[];return t.Children.forEach(e,((e,o)=>{if(!t.isValidElement(e))return;let a=[...n,o];if(e.type===t.Fragment)return void r.push.apply(r,le(e.props.children,a));i(e.type===ie,`[${"string"==typeof e.type?e.type:e.type.name}] is not a component. All component children of must be a or `),i(!e.props.index||!e.props.children,"An index route cannot have child routes.");let s={id:e.props.id||a.join("-"),caseSensitive:e.props.caseSensitive,element:e.props.element,Component:e.props.Component,index:e.props.index,path:e.props.path,loader:e.props.loader,action:e.props.action,hydrateFallbackElement:e.props.hydrateFallbackElement,HydrateFallback:e.props.HydrateFallback,errorElement:e.props.errorElement,ErrorBoundary:e.props.ErrorBoundary,hasErrorBoundary:!0===e.props.hasErrorBoundary||null!=e.props.ErrorBoundary||null!=e.props.errorElement,shouldRevalidate:e.props.shouldRevalidate,handle:e.props.handle,lazy:e.props.lazy};e.props.children&&(s.children=le(e.props.children,a)),r.push(s)})),r}t.memo((function({routes:e,future:t,state:n}){return Z(e,void 0,n,t)})),t.Component;var ce="get",ue="application/x-www-form-urlencoded";function de(e){return null!=e&&"string"==typeof e.tagName}var pe=null,fe=new Set(["application/x-www-form-urlencoded","multipart/form-data","text/plain"]);function he(e){return null==e||fe.has(e)?e:(a(!1,`"${e}" is not a valid \`encType\` for \`
\`/\`\` and will default to "${ue}"`),null)}function ge(e,t){if(!1===e||null==e)throw new Error(t)}function ve(e){return null!=e&&(null==e.href?"preload"===e.rel&&"string"==typeof e.imageSrcSet&&"string"==typeof e.imageSizes:"string"==typeof e.rel&&"string"==typeof e.href)}function me(e,t,n,r,o,i){let a=(e,t)=>!n[t]||e.route.id!==n[t].route.id,s=(e,t)=>n[t].pathname!==e.pathname||n[t].route.path?.endsWith("*")&&n[t].params["*"]!==e.params["*"];return"assets"===i?t.filter(((e,t)=>a(e,t)||s(e,t))):"data"===i?t.filter(((t,i)=>{let l=r.routes[t.route.id];if(!l||!l.hasLoader)return!1;if(a(t,i)||s(t,i))return!0;if(t.route.shouldRevalidate){let r=t.route.shouldRevalidate({currentUrl:new URL(o.pathname+o.search+o.hash,window.origin),currentParams:n[0]?.params||{},nextUrl:new URL(e,window.origin),nextParams:t.params,defaultShouldRevalidate:!0});if("boolean"==typeof r)return r}return!0})):[]}function ye(e,t,{includeHydrateFallback:n}={}){return r=e.map((e=>{let r=t.routes[e.route.id];if(!r)return[];let o=[r.module];return r.clientActionModule&&(o=o.concat(r.clientActionModule)),r.clientLoaderModule&&(o=o.concat(r.clientLoaderModule)),n&&r.hydrateFallbackModule&&(o=o.concat(r.hydrateFallbackModule)),r.imports&&(o=o.concat(r.imports)),o})).flat(1),[...new Set(r)];var r}Object.getOwnPropertyNames(Object.prototype).sort().join("\0"),"undefined"!=typeof window?window:"undefined"!=typeof globalThis&&globalThis,Symbol("SingleFetchRedirect");function be(){let e=t.useContext(D);return ge(e,"You must render this element inside a element"),e}function we(){let e=t.useContext(G);return ge(e,"You must render this element inside a element"),e}t.Component;var xe=t.createContext(void 0);function ke(){let e=t.useContext(xe);return ge(e,"You must render this element inside a element"),e}function _e(e,t){return n=>{e&&e(n),n.defaultPrevented||t(n)}}function Se({page:e,...n}){let{router:r}=be(),o=t.useMemo((()=>d(r.routes,e,r.basename)),[r.routes,e,r.basename]);return o?t.createElement(Pe,{page:e,matches:o,...n}):null}function Ee(e){let{manifest:n,routeModules:r}=ke(),[o,i]=t.useState([]);return t.useEffect((()=>{let t=!1;return async function(e,t,n){return function(e,t){let n=new Set,r=new Set(t);return e.reduce(((e,o)=>{if(t&&(null==(i=o)||"string"!=typeof i.page)&&"script"===o.as&&o.href&&r.has(o.href))return e;var i;let a=JSON.stringify(function(e){let t={},n=Object.keys(e).sort();for(let r of n)t[r]=e[r];return t}(o));return n.has(a)||(n.add(a),e.push({key:a,link:o})),e}),[])}((await Promise.all(e.map((async e=>{let r=t.routes[e.route.id];if(r){let e=await async function(e,t){if(e.id in t)return t[e.id];try{let n=await import(e.module);return t[e.id]=n,n}catch(t){return console.error(`Error loading route module \`${e.module}\`, reloading page...`),console.error(t),window.__reactRouterContext&&window.__reactRouterContext.isSpaMode,window.location.reload(),new Promise((()=>{}))}}(r,n);return e.links?e.links():[]}return[]})))).flat(1).filter(ve).filter((e=>"stylesheet"===e.rel||"preload"===e.rel)).map((e=>"stylesheet"===e.rel?{...e,rel:"prefetch",as:"style"}:{...e,rel:"prefetch"})))}(e,n,r).then((e=>{t||i(e)})),()=>{t=!0}}),[e,n,r]),o}function Pe({page:e,matches:n,...r}){let o=$(),{manifest:i,routeModules:a}=ke(),{basename:s}=be(),{loaderData:l,matches:c}=we(),u=t.useMemo((()=>me(e,n,c,i,o,"data")),[e,n,c,i,o]),d=t.useMemo((()=>me(e,n,c,i,o,"assets")),[e,n,c,i,o]),p=t.useMemo((()=>{if(e===o.pathname+o.search+o.hash)return[];let t=new Set,r=!1;if(n.forEach((e=>{let n=i.routes[e.route.id];n&&n.hasLoader&&(!u.some((t=>t.route.id===e.route.id))&&e.route.id in l&&a[e.route.id]?.shouldRevalidate||n.hasClientLoader?r=!0:t.add(e.route.id))})),0===t.size)return[];let c=function(e,t){let n="string"==typeof e?new URL(e,"undefined"==typeof window?"server://singlefetch/":window.location.origin):e;return"/"===n.pathname?n.pathname="_root.data":t&&"/"===E(n.pathname,t)?n.pathname=`${t.replace(/\/$/,"")}/_root.data`:n.pathname=`${n.pathname.replace(/\/$/,"")}.data`,n}(e,s);return r&&t.size>0&&c.searchParams.set("_routes",n.filter((e=>t.has(e.route.id))).map((e=>e.route.id)).join(",")),[c.pathname+c.search]}),[s,l,o,i,u,n,e,a]),f=t.useMemo((()=>ye(d,i)),[d,i]),h=Ee(d);return t.createElement(t.Fragment,null,p.map((e=>t.createElement("link",{key:e,rel:"prefetch",as:"fetch",href:e,...r}))),f.map((e=>t.createElement("link",{key:e,rel:"modulepreload",href:e,...r}))),h.map((({key:e,link:n})=>t.createElement("link",{key:e,...n}))))}xe.displayName="FrameworkContext";function Le(...e){return t=>{e.forEach((e=>{"function"==typeof e?e(t):null!=e&&(e.current=t)}))}}var je="undefined"!=typeof window&&void 0!==window.document&&void 0!==window.document.createElement;try{je&&(window.__reactRouterVersion="7.6.1")}catch(e){}function Te({basename:e,children:n,window:r}){let i=t.useRef();null==i.current&&(i.current=o({window:r,v5Compat:!0}));let a=i.current,[s,l]=t.useState({action:a.action,location:a.location}),c=t.useCallback((e=>{t.startTransition((()=>l(e)))}),[l]);return t.useLayoutEffect((()=>a.listen(c)),[a,c]),t.createElement(ae,{basename:e,children:n,location:s.location,navigationType:s.action,navigator:a})}var Ne=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i,Ce=t.forwardRef((function({onClick:e,discover:n="render",prefetch:r="none",relative:o,reloadDocument:s,replace:l,state:u,target:d,to:p,preventScrollReset:f,viewTransition:h,...g},v){let m,{basename:y}=t.useContext(M),b="string"==typeof p&&Ne.test(p),w=!1;if("string"==typeof p&&b&&(m=p,je))try{let e=new URL(window.location.href),t=p.startsWith("//")?new URL(e.protocol+p):new URL(p),n=E(t.pathname,y);t.origin===e.origin&&null!=n?p=n+t.search+t.hash:w=!0}catch(e){a(!1,` contains an invalid URL which will probably break when clicked - please update to a valid URL path.`)}let x=function(e,{relative:n}={}){i(V(),"useHref() may be used only in the context of a component.");let{basename:r,navigator:o}=t.useContext(M),{hash:a,pathname:s,search:l}=K(e,{relative:n}),c=s;return"/"!==r&&(c="/"===s?r:T([r,s])),o.createHref({pathname:c,search:l,hash:a})}(p,{relative:o}),[k,_,S]=function(e,n){let r=t.useContext(xe),[o,i]=t.useState(!1),[a,s]=t.useState(!1),{onFocus:l,onBlur:c,onMouseEnter:u,onMouseLeave:d,onTouchStart:p}=n,f=t.useRef(null);t.useEffect((()=>{if("render"===e&&s(!0),"viewport"===e){let e=new IntersectionObserver((e=>{e.forEach((e=>{s(e.isIntersecting)}))}),{threshold:.5});return f.current&&e.observe(f.current),()=>{e.disconnect()}}}),[e]),t.useEffect((()=>{if(o){let e=setTimeout((()=>{s(!0)}),100);return()=>{clearTimeout(e)}}}),[o]);let h=()=>{i(!0)},g=()=>{i(!1),s(!1)};return r?"intent"!==e?[a,f,{}]:[a,f,{onFocus:_e(l,h),onBlur:_e(c,g),onMouseEnter:_e(u,h),onMouseLeave:_e(d,g),onTouchStart:_e(p,h)}]:[!1,f,{}]}(r,g),P=function(e,{target:n,replace:r,state:o,preventScrollReset:i,relative:a,viewTransition:s}={}){let l=q(),u=$(),d=K(e,{relative:a});return t.useCallback((t=>{if(function(e,t){return!(0!==e.button||t&&"_self"!==t||function(e){return!!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)}(e))}(t,n)){t.preventDefault();let n=void 0!==r?r:c(u)===c(d);l(e,{replace:n,state:o,preventScrollReset:i,relative:a,viewTransition:s})}}),[u,l,d,r,o,n,e,i,a,s])}(p,{replace:l,state:u,target:d,preventScrollReset:f,relative:o,viewTransition:h}),L=t.createElement("a",{...g,...S,href:m||x,onClick:w||s?e:function(t){e&&e(t),t.defaultPrevented||P(t)},ref:Le(v,_),target:d,"data-discover":b||"render"!==n?void 0:"true"});return k&&!b?t.createElement(t.Fragment,null,L,t.createElement(Se,{page:x})):L}));function Ie(e){let n=t.useContext(D);return i(n,function(e){return`${e} must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.`}(e)),n}Ce.displayName="Link",t.forwardRef((function({"aria-current":e="page",caseSensitive:n=!1,className:r="",end:o=!1,style:a,to:s,viewTransition:l,children:c,...u},d){let p=K(s,{relative:u.relative}),f=$(),h=t.useContext(G),{navigator:g,basename:v}=t.useContext(M),m=null!=h&&function(e,n={}){let r=t.useContext(B);i(null!=r,"`useViewTransitionState` must be used within `react-router-dom`'s `RouterProvider`. Did you accidentally import `RouterProvider` from `react-router`?");let{basename:o}=Ie("useViewTransitionState"),a=K(e,{relative:n.relative});if(!r.isTransitioning)return!1;let s=E(r.currentLocation.pathname,o)||r.currentLocation.pathname,l=E(r.nextLocation.pathname,o)||r.nextLocation.pathname;return null!=_(a.pathname,l)||null!=_(a.pathname,s)}(p)&&!0===l,y=g.encodeLocation?g.encodeLocation(p).pathname:p.pathname,b=f.pathname,w=h&&h.navigation&&h.navigation.location?h.navigation.location.pathname:null;n||(b=b.toLowerCase(),w=w?w.toLowerCase():null,y=y.toLowerCase()),w&&v&&(w=E(w,v)||w);const x="/"!==y&&y.endsWith("/")?y.length-1:y.length;let k,S=b===y||!o&&b.startsWith(y)&&"/"===b.charAt(x),P=null!=w&&(w===y||!o&&w.startsWith(y)&&"/"===w.charAt(y.length)),L={isActive:S,isPending:P,isTransitioning:m},j=S?e:void 0;k="function"==typeof r?r(L):[r,S?"active":null,P?"pending":null,m?"transitioning":null].filter(Boolean).join(" ");let T="function"==typeof a?a(L):a;return t.createElement(Ce,{...u,"aria-current":j,className:k,ref:d,style:T,to:s,viewTransition:l},"function"==typeof c?c(L):c)})).displayName="NavLink",t.forwardRef((({discover:e="render",fetcherKey:n,navigate:r,reloadDocument:o,replace:a,state:s,method:l=ce,action:u,onSubmit:d,relative:p,preventScrollReset:f,viewTransition:h,...g},v)=>{let m=function(){let{router:e}=Ie("useSubmit"),{basename:n}=t.useContext(M),r=te("useRouteId");return t.useCallback((async(t,o={})=>{let{action:i,method:a,encType:s,formData:l,body:c}=function(e,t){let n,r,o,i,a;if(de(s=e)&&"form"===s.tagName.toLowerCase()){let a=e.getAttribute("action");r=a?E(a,t):null,n=e.getAttribute("method")||ce,o=he(e.getAttribute("enctype"))||ue,i=new FormData(e)}else if(function(e){return de(e)&&"button"===e.tagName.toLowerCase()}(e)||function(e){return de(e)&&"input"===e.tagName.toLowerCase()}(e)&&("submit"===e.type||"image"===e.type)){let a=e.form;if(null==a)throw new Error('Cannot submit a + + + { ! selectedText && +
+ +
+ { __( 'TIP: Customize by selecting different text.', 'wp-parsely' ) } +
+
+ } + { selectedText && ( + + ) } + + ) } + + { isInboundLink && selectedText && ( + <> + + { activeLink.smartLink?.is_link_replacement && ( + { + setRestoreOriginal( value ); + } } + /> + ) } + + + + ) } + + { isInboundLink && ! selectedText && ( + <> + + { activeLink.smartLink?.is_link_replacement && ( + { + setRestoreOriginal( value ); + } } + /> + ) } + + ) } + + + ) } + + ); +}; diff --git a/src/content-helper/dashboard-page/pages/traffic-boost/preview/components/preview-footer.tsx b/src/content-helper/dashboard-page/pages/traffic-boost/preview/components/preview-footer.tsx index b00715afab..3dd4e9f116 100644 --- a/src/content-helper/dashboard-page/pages/traffic-boost/preview/components/preview-footer.tsx +++ b/src/content-helper/dashboard-page/pages/traffic-boost/preview/components/preview-footer.tsx @@ -1,19 +1,14 @@ /** - * WordPress imports + * WordPress dependencies */ -import { Button, CheckboxControl } from '@wordpress/components'; -import { useSelect } from '@wordpress/data'; -import { useState } from '@wordpress/element'; +import { Button } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; -import { arrowLeft, arrowRight, check, close, undo } from '@wordpress/icons'; +import { arrowLeft, arrowRight } from '@wordpress/icons'; /** - * Internal imports + * Internal dependencies */ -import { VerticalDivider } from '../../../../../common/components/vertical-divider'; import { TrafficBoostLink } from '../../provider'; -import { TrafficBoostStore } from '../../store'; -import { TextSelection } from '../preview'; /** * Props structure for PreviewFooter. @@ -22,16 +17,10 @@ import { TextSelection } from '../preview'; */ interface PreviewFooterProps { activeLink: TrafficBoostLink | null; - onAccept: ( link: TrafficBoostLink ) => void; - onRemove: ( link: TrafficBoostLink, restoreOriginal: boolean ) => void; - onUpdateLink: ( link: TrafficBoostLink, restoreOriginal: boolean ) => void; - onDiscard: ( link: TrafficBoostLink ) => void; onNext: () => void; onPrevious: () => void; totalItems: number; itemIndex: number; - onRestoreOriginal: () => void; - selectedText: TextSelection | null; } /** @@ -44,31 +33,13 @@ interface PreviewFooterProps { */ export const PreviewFooter = ( { activeLink, - onAccept, - onUpdateLink, - onDiscard, onNext, onPrevious, - onRemove, totalItems, itemIndex, - onRestoreOriginal, - selectedText, }: PreviewFooterProps ): React.JSX.Element => { - const isInboundLink = ! activeLink?.isSuggestion; const hasNext = itemIndex < totalItems; const hasPrevious = itemIndex > 1; - const [ restoreOriginal, setRestoreOriginal ] = useState( true ); - - const { - isAccepting, - isRemoving, - isGenerating, - } = useSelect( ( select ) => ( { - isAccepting: activeLink ? select( TrafficBoostStore ).isAccepting( activeLink ) : false, - isRemoving: activeLink ? select( TrafficBoostStore ).isRemoving( activeLink ) : false, - isGenerating: activeLink ? select( TrafficBoostStore ).isGenerating( activeLink ) : false, - } ), [ activeLink ] ); if ( ! activeLink ) { return <>; @@ -88,94 +59,6 @@ export const PreviewFooter = ( { ) } - { ! isGenerating && ( -
- { ! isInboundLink && ( - <> - - - { selectedText && ( - <> - - - - ) } - - ) } - - { isInboundLink && ( - <> - { selectedText ? ( - <> - - { activeLink.smartLink?.is_link_replacement && ( - { - setRestoreOriginal( value ); - } } - /> - ) } - - - - ) : ( - <> - - { activeLink.smartLink?.is_link_replacement && ( - { - setRestoreOriginal( value ); - } } - /> - ) } - - ) } - - ) } -
- ) } -
{ hasNext && (
- ); }; diff --git a/src/content-helper/dashboard-page/pages/traffic-boost/preview/components/preview-header.tsx b/src/content-helper/dashboard-page/pages/traffic-boost/preview/components/preview-header.tsx index 6929ba6688..f16f14f829 100644 --- a/src/content-helper/dashboard-page/pages/traffic-boost/preview/components/preview-header.tsx +++ b/src/content-helper/dashboard-page/pages/traffic-boost/preview/components/preview-header.tsx @@ -1,5 +1,5 @@ /** - * WordPress imports + * WordPress dependencies */ import { Button, DropdownMenu, MenuGroup, MenuItem } from '@wordpress/components'; import { useEffect } from '@wordpress/element'; @@ -7,7 +7,7 @@ import { __ } from '@wordpress/i18n'; import { desktop, edit, external, moreVertical } from '@wordpress/icons'; /** - * Internal imports + * Internal dependencies */ import { LeafIcon } from '../../../../../common/icons/leaf-icon'; import { HydratedPost } from '../../../../../common/providers/base-wordpress-provider'; @@ -154,7 +154,7 @@ export const PreviewHeader = ( { selectedLinkType={ null } /> -
+
{ ! isExternalURL( activeLink ) && false && (
diff --git a/src/content-helper/dashboard-page/pages/traffic-boost/preview/components/text-selection-tooltip.tsx b/src/content-helper/dashboard-page/pages/traffic-boost/preview/components/text-selection-tooltip.tsx index ee2bd7a890..6c5edbb435 100644 --- a/src/content-helper/dashboard-page/pages/traffic-boost/preview/components/text-selection-tooltip.tsx +++ b/src/content-helper/dashboard-page/pages/traffic-boost/preview/components/text-selection-tooltip.tsx @@ -1,11 +1,12 @@ /** - * WordPress imports + * WordPress dependencies */ import { Button } from '@wordpress/components'; import { debounce } from '@wordpress/compose'; import { createRoot, useCallback, useEffect, useState } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { link, warning } from '@wordpress/icons'; +import { useWordpressComponentStyles } from '../hooks/use-wordpress-component-styles'; import { getContentArea } from '../utils'; /** @@ -16,17 +17,15 @@ import { getContentArea } from '../utils'; * @param {Document} iframeDocument The iframe's document object. */ const useIframeStyles = ( iframeDocument: Document ) => { + const { injectWordpressComponentStyles } = useWordpressComponentStyles(); + useEffect( () => { + injectWordpressComponentStyles( iframeDocument ); + // Get computed styles from parent window. const adminColor = window.getComputedStyle( document.documentElement ) .getPropertyValue( '--wp-admin-theme-color' ).trim(); - // Inject WordPress components styles. - const wpComponentsLink = iframeDocument.createElement( 'link' ); - wpComponentsLink.rel = 'stylesheet'; - wpComponentsLink.href = '/wp-includes/css/dist/components/style.css'; - iframeDocument.head.appendChild( wpComponentsLink ); - // Create and inject custom styles into the iframe. const style = iframeDocument.createElement( 'style' ); style.textContent = ` @@ -34,7 +33,7 @@ const useIframeStyles = ( iframeDocument: Document ) => { .parsely-traffic-boost-highlight { position: absolute; pointer-events: none; - z-index: 1000; + z-index: 1010; transition: all 0.15s ease-out; } @@ -59,6 +58,8 @@ const useIframeStyles = ( iframeDocument: Document ) => { } .parsely-traffic-boost-iframe-popover { + /* Reset font family to editor defaults to avoid inheriting frontend font in tooltip. */ + font-family: -apple-system, BlinkMacSystemFont,"Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell,"Helvetica Neue", sans-serif; padding: 0; pointer-events: auto; white-space: nowrap; @@ -106,10 +107,9 @@ const useIframeStyles = ( iframeDocument: Document ) => { // Cleanup function to remove styles when component unmounts. return () => { - wpComponentsLink.remove(); style.remove(); }; - }, [ iframeDocument ] ); + }, [ iframeDocument, injectWordpressComponentStyles ] ); }; /** @@ -149,6 +149,12 @@ const TextSelectionPopover = ( { onSelect, iframeDocument, selection, onErrorCli let currentNode: Node | null = range.startContainer; const endNode = range.endContainer; + if ( currentNode === endNode && currentNode.firstChild ) { + // On triple-click, startContainer and endContainer will be the same node. + // Start iterating from the first child of the selected section to find inner anchors. + currentNode = currentNode.firstChild; + } + while ( currentNode !== null ) { if ( currentNode.nodeType === Node.ELEMENT_NODE ) { const element = currentNode as Element; @@ -262,82 +268,6 @@ export const TextSelectionTooltip = ( { iframeRef, onTextSelected, }: TextSelectionTooltipProps ): null => { - /** - * Expands the current selection to word boundaries. - * - * @since 3.19.0 - * - * @param {Selection} docSelection The document's current selection. - * @param {Range} range The current selection range. - */ - const expandToWordBoundary = ( docSelection: Selection, range: Range ) => { - const startNode = range.startContainer as Text; - const endNode = range.endContainer as Text; - const startText = startNode.textContent ?? ''; - const endText = endNode.textContent ?? ''; - - // Get initial selection boundaries before expanding. - const initialStart = range.startOffset; - const initialEnd = range.endOffset; - - // Find word boundary at start. - let startOffset = range.startOffset; - while ( startOffset > 0 && /[^\s.,!?;:'")\]}]/g.test( startText[ startOffset - 1 ] ) ) { - startOffset--; - } - - // Find word boundary at end. - let endOffset = range.endOffset; - while ( endOffset < endText.length && /[^\s.,!?;:'"([{]/g.test( endText[ endOffset ] ) ) { - endOffset++; - } - - // Only update if boundaries have changed. - if ( startOffset !== initialStart || endOffset !== initialEnd ) { - range.setStart( startNode, startOffset ); - range.setEnd( endNode, endOffset ); - docSelection.removeAllRanges(); - docSelection.addRange( range ); - } - }; - - /** - * Expands the current selection to encompass the entire link node if - * selection is within a link. - * - * @since 3.19.0 - * - * @param {Selection} docSelection The document's current selection. - * @param {Range} range The current selection range. - * - * @return {boolean} True if selection was expanded to a link, false otherwise. - */ - const expandToLinkNode = ( docSelection: Selection, range: Range ): boolean => { - // Find if selection is within an anchor tag. - const container = range.commonAncestorContainer as Element; - const linkNode = container.nodeType === Node.ELEMENT_NODE - ? container.closest( 'a' ) - : container.parentElement?.closest( 'a' ); - - // If the selection is already the full link, return true. - if ( docSelection.toString() === linkNode?.textContent ) { - return true; - } - - if ( linkNode ) { - // Create a new range that encompasses the entire link. - const newRange = range.cloneRange(); - newRange.selectNodeContents( linkNode ); - - // Update the selection. - docSelection.removeAllRanges(); - docSelection.addRange( newRange ); - return true; - } - - return false; - }; - /** * Calculates the offset of the selected text by counting previous occurrences. * @@ -416,6 +346,21 @@ export const TextSelectionTooltip = ( { return; } + if ( docSelection.rangeCount > 1 ) { + // If docSelection has multiple ranges, it can be because they've selected over + // embedded markup, or they've selected a range over multiple paragraphs (invalid). + // Verify that the first and last ranges have the same start and end containers. + const firstRange = docSelection.getRangeAt( 0 ); + const lastRange = docSelection.getRangeAt( docSelection.rangeCount - 1 ); + + const startParagraph = getClosestSelectableItem( firstRange.startContainer ); + const endParagraph = getClosestSelectableItem( lastRange.endContainer ); + + if ( ! startParagraph || ! endParagraph || startParagraph !== endParagraph ) { + return; + } + } + const range = docSelection.getRangeAt( 0 ); // Check if selection is within content area. @@ -424,19 +369,19 @@ export const TextSelectionTooltip = ( { } // Check if selection spans multiple paragraphs. - const startParagraph = range.startContainer.parentElement?.closest( 'p, li' ); - const endParagraph = range.endContainer.parentElement?.closest( 'p, li' ); + const normalizedRange = normalizeRange( range, iframeDocument ); + if ( isRangeChanged( range, normalizedRange ) ) { + docSelection.removeAllRanges(); + docSelection.addRange( normalizedRange ); + } + + const startParagraph = getClosestSelectableItem( normalizedRange.startContainer ); + const endParagraph = getClosestSelectableItem( normalizedRange.endContainer ); if ( ! startParagraph || ! endParagraph || startParagraph !== endParagraph ) { return; } - // If selection is inside a link, expand to encompass the entire link. - if ( ! expandToLinkNode( docSelection, range ) ) { - // Only expand to word boundary if we didn't expand to a link. - expandToWordBoundary( docSelection, range ); - } - // Create highlight overlay. const highlight = iframeDocument.createElement( 'div' ); highlight.className = 'parsely-traffic-boost-highlight'; @@ -465,7 +410,22 @@ export const TextSelectionTooltip = ( { popoverContainer.classList.add( 'closing' ); const offset = calculateOffset( iframeDocument, docSelection, contentArea ); - onTextSelected( docSelection.toString().trim(), offset ); + + // Using docSelection.toString() directly will replace some characters like   with a space. + // Later when we're highlighting the text, this will cause the text to not match the content on the page. + // Get text content using cloneContents() and .textContent to exactly match page content. + const selectionContainer = iframeDocument.createElement( 'div' ); + for ( let rangeIndex = 0; rangeIndex < docSelection.rangeCount; rangeIndex++ ) { + const currentRange = docSelection.getRangeAt( rangeIndex ); + const rangeContents = currentRange.cloneContents(); + selectionContainer.appendChild( rangeContents ); + } + + const docSelectionText = selectionContainer.textContent?.trim() ?? ''; + + // Remove newlines that can be present from prior toolbar HTML injection. + onTextSelected( docSelectionText, offset ); + docSelection.removeAllRanges(); // Wait for animation to complete before cleanup. @@ -482,7 +442,7 @@ export const TextSelectionTooltip = ( { * @since 3.19.0 */ const updatePosition = () => { - const rect = range.getBoundingClientRect(); + const rect = normalizedRange.getBoundingClientRect(); const scrollY = iframeDocument.defaultView?.scrollY ?? 0; highlight.style.top = `${ rect.top + scrollY }px`; @@ -526,18 +486,39 @@ export const TextSelectionTooltip = ( { return; } - // Add selection event listener. - const handleSelectionChange = debounce( () => { - handleSelection(); - }, 300, { + // Add selection event listener to update the highlight. + const handleSelectionChange = debounce( handleSelection, 300, { leading: true, trailing: true, } ); - iframeDocument.addEventListener( 'selectionchange', handleSelectionChange ); + // Add mouseup listener to expand selection to word boundaries. + const handleSelectionEnd = () => { + const selection = iframeDocument.getSelection(); + + if ( selection === null || selection.rangeCount === 0 ) { + return; + } + + const range = selection.getRangeAt( 0 ); + const rangeHasContent = range?.collapsed === false; + + if ( selection && range && rangeHasContent ) { + const normalizedRange = normalizeRange( range, iframeDocument ); + if ( isRangeChanged( range, normalizedRange ) ) { + selection.removeAllRanges(); + selection.addRange( normalizedRange ); + } + + expandToWordBoundary( selection, normalizedRange ); + } + }; + iframeDocument.addEventListener( 'mouseup', handleSelectionEnd ); + return () => { iframeDocument.removeEventListener( 'selectionchange', handleSelectionChange ); + iframeDocument.removeEventListener( 'mouseup', handleSelectionEnd ); }; }, [ handleSelection, iframeRef ] ); @@ -571,3 +552,165 @@ const getNextNode = function( node: Node, skipChildren: boolean, endNode: Node ) return node.nextSibling ? node.nextSibling : getNextNode( node.parentNode, true, endNode ); }; + +/** + * Normalize selection range browser differences. + * + * @since 3.20.0 + * + * @param {Range} range The range to normalize. + * @param {Document} currentDocument The current document. + * + * @return {Range} The normalized range. + */ +const normalizeRange = ( range: Range, currentDocument: Document ): Range => { + // Only care about instances the range is over multiple nodes, and the endOffset is on a node boundary. + if ( range.startContainer !== range.endContainer && range.endOffset === 0 ) { + // In Chrome, triple-clicking a text section will select: + // - The entire section contents (e.g. a paragraph) + // - Any whitespace text nodes after the section (e.g. some "\n" characters) + // - The next element in the document at endOffset 0 (e.g. the beginning of a
  • list after the paragraph) + // + // This makes selecting the initial triple-click location difficult, because the range + // can include unrelated nodes at a different depth in the DOM. + // + // Fortunately, we only see range.endOffset === 0 when the user triple-clicks in Chrome, + // or drag a selection just past the end of a selectable section. + // When we detect this, we can use the startContainer to find the paragraph, + // and then set the endContainer to the same paragraph. This ignores the extra nodes + // appended to the selection, and gives a strong approximation of the original triple-click location. + + const startParagraph = getClosestSelectableItem( range.startContainer ); + + if ( startParagraph === null ) { + return range; + } + + const newRange = currentDocument.createRange(); + newRange.selectNodeContents( startParagraph as Node ); + + return newRange; + } + + return range; +}; + +/** + * Returns true if the two range parameters are different. + * + * @param {Range} range The original range. + * @param {Range} newRange The new range. + * + * @return {boolean} True if the ranges are different, false otherwise. + * @since 3.20.0 + */ +const isRangeChanged = ( range: Range, newRange: Range ): boolean => { + return range.startContainer !== newRange.startContainer || + range.startOffset !== newRange.startOffset || + range.endContainer !== newRange.endContainer || + range.endOffset !== newRange.endOffset; +}; + +/** + * Gets the closest selectable item (p, li) element from a node. + * + * @since 3.20.0 + * + * @param {Node} node The node to start searching from. + * + * @return {Element|null} The closest selectable item element, or null if not found. + */ +const getClosestSelectableItem = ( node: Node ): Element | null => { + const selectableItems = [ 'p', 'li' ].join( ', ' ); + + // If the node itself is a matching item, return it. + if ( node.nodeType === Node.ELEMENT_NODE && ( node as Element ).matches( selectableItems ) ) { + return node as Element; + } + + return node.parentElement?.closest( selectableItems ) ?? null; +}; + +/** + * Expands the current selection to encompass the entire link node if + * selection is within a link. + * + * @since 3.19.0 + * + * @param {Selection} docSelection The document's current selection. + * @param {Range} range The current selection range. + * + * @return {boolean} True if selection was expanded to a link, false otherwise. + */ +const expandToLinkNode = ( docSelection: Selection, range: Range ): boolean => { + // Find if selection is within an anchor tag. + const container = range.commonAncestorContainer as Element; + const linkNode = container.nodeType === Node.ELEMENT_NODE + ? container.closest( 'a' ) + : container.parentElement?.closest( 'a' ); + + // If the selection is already the full link, return true. + if ( docSelection.toString() === linkNode?.textContent ) { + return true; + } + + if ( linkNode ) { + // Create a new range that encompasses the entire link. + const newRange = range.cloneRange(); + newRange.selectNodeContents( linkNode ); + + // Update the selection. + docSelection.removeAllRanges(); + docSelection.addRange( newRange ); + return true; + } + + return false; +}; + +/** + * Expands the current selection to word boundaries. + * + * @since 3.19.0 + * + * @param {Selection} docSelection The document's current selection. + * @param {Range} range The current selection range. + */ +const expandToWordBoundary = ( docSelection: Selection, range: Range ) => { + // If selection is inside a link, expand to encompass the entire link. + if ( ! expandToLinkNode( docSelection, range ) ) { + // Only expand to word boundary if we didn't expand to a link. + // Find word boundary at start. + const startNode = range.startContainer as Text; + const initialStart = range.startOffset; + let startOffset = range.startOffset; + + if ( startNode.nodeType === Node.TEXT_NODE ) { + const startText = startNode.textContent ?? ''; + + while ( startOffset > 0 && /[^\s.,!?;:'"’)\]}]/g.test( startText[ startOffset - 1 ] ) ) { + startOffset--; + } + } + + // Find word boundary at end. + const endNode = range.endContainer as Text; + const initialEnd = range.endOffset; + let endOffset = range.endOffset; + + if ( endNode.nodeType === Node.TEXT_NODE ) { + const endText = endNode.textContent ?? ''; + while ( endOffset < endText.length && /[^\s.,!?;:'"’([{]/g.test( endText[ endOffset ] ) ) { + endOffset++; + } + } + + // Only update if boundaries have changed. + if ( startOffset !== initialStart || endOffset !== initialEnd ) { + range.setStart( startNode, startOffset ); + range.setEnd( endNode, endOffset ); + docSelection.removeAllRanges(); + docSelection.addRange( range ); + } + } +}; diff --git a/src/content-helper/dashboard-page/pages/traffic-boost/preview/hooks/use-draggable.ts b/src/content-helper/dashboard-page/pages/traffic-boost/preview/hooks/use-draggable.ts new file mode 100644 index 0000000000..25bf96bc8d --- /dev/null +++ b/src/content-helper/dashboard-page/pages/traffic-boost/preview/hooks/use-draggable.ts @@ -0,0 +1,231 @@ +/** + * WordPress dependencies + */ +import { useCallback, useEffect, useRef, useState } from '@wordpress/element'; + +/** + * @typedef {import('@wordpress/element').RefObject} RefObject + * @typedef {import('@wordpress/element').LegacyRef} LegacyRef + */ + +export const DRAG_MARGIN_PX = 8; + +/** + * Represents the position and dimensions of a draggable item. + * + * This is a subset of DOMRect, containing only the properties we need + * to avoid calculating additional parameters like top, left, right, bottom. + */ +interface ItemRect { + x: number; + y: number; + width: number; + height: number; +} + +/** + * Properties passed to the onDrag callback function during drag operations. + */ +export interface OnDragProps { + // The total accumulated delta movement from the start of the drag. + totalDelta: { x: number; y: number }; + // The original position and dimensions of the item before dragging. + originalItemRect: ItemRect; + // The bounding rectangle of the iframe containing the draggable item. + iframeRect: DOMRect; +} + +/** + * Configuration properties for the useDraggable hook. + */ +export interface UseDraggableProps { + // Callback function called during drag operations to calculate new position. + onDrag: ( props: OnDragProps ) => { x: number; y: number }; + // Reference to the iframe element containing the draggable item. + iframeRef: React.RefObject; + // CSS selector for the drag handle element within the draggable item. + dragHandleSelector: string; +} + +/** + * Custom hook that makes an element draggable within an iframe. + * + * onDrag will be called with the drag delta of the original item, and will + * update the visible position on return. + * + * @param {UseDraggableProps} props Configuration object for the draggable behavior. + * @param {Function} props.onDrag Callback function that receives current drag information + * and returns new position. + * @param {RefObject} props.iframeRef Reference to the iframe containing the draggable element. + * @param {string} props.dragHandleSelector CSS selector for the element that triggers dragging. + * + * @return {[LegacyRef, boolean]} A tuple containing a ref to attach to the draggable element, and a + * boolean indicating whether the element is currently being dragged. + */ +export const useDraggable = ( + { onDrag, iframeRef, dragHandleSelector }: UseDraggableProps +): [ React.LegacyRef, boolean ] => { + const [ pressed, setPressed ] = useState( false ); + + // Avoid storing positions in useState, as it will cause the component to + // re-render on every state change. + const totalDelta = useRef( { x: 0, y: 0 } ); + const positionDelta = useRef( { x: 0, y: 0 } ); + const iframeRect = useRef( null ); + const originalItemRect = useRef( null ); + const ref = useRef( null ); + const startPosition = useRef<{ x: number; y: number } | null>( null ); + const startTotalDelta = useRef<{ x: number; y: number }>( { x: 0, y: 0 } ); + + const unsubscribe = useRef<( () => void ) | null>( null ); + const externalRef = useCallback( ( elem: HTMLDivElement | null ) => { + ref.current = elem; + + if ( unsubscribe.current ) { + unsubscribe.current(); + } + + if ( ! elem ) { + return; + } + + const handleMouseDown = ( e: MouseEvent ) => { + // Only start dragging if the click was on the drag handle. + const target = e.target as Element; + if ( ! target.closest( dragHandleSelector ) ) { + return; + } + + e.preventDefault(); + + const iframeDocument = iframeRef.current?.contentDocument ?? iframeRef.current?.contentWindow?.document; + if ( iframeDocument ) { + iframeRect.current = iframeDocument.documentElement.getBoundingClientRect(); + } + + originalItemRect.current = ref.current?.getBoundingClientRect() ?? null; + + // Store the starting mouse position within the iframe, used for offset calculations. + startPosition.current = { x: e.clientX, y: e.clientY }; + + // Store the current total delta at the start of the drag, used for offset calculations. + startTotalDelta.current = { x: totalDelta.current.x, y: totalDelta.current.y }; + + // If the item already has a transform from being dragged, we need to adjust + // the originalItemRect to the item's position without any transformations to + // avoid an offset on drag. Undo the transformations that already exist on the + // item and store the result in originalItemRect. + const transform = ref.current?.style.transform; + if ( transform && originalItemRect.current ) { + const matrix = new DOMMatrix( transform ); + originalItemRect.current = { + x: originalItemRect.current.x - matrix.e, + y: originalItemRect.current.y - matrix.f, + width: originalItemRect.current.width, + height: originalItemRect.current.height, + }; + } + + setPressed( true ); + }; + + elem.addEventListener( 'mousedown', handleMouseDown ); + + unsubscribe.current = () => { + elem.removeEventListener( 'mousedown', handleMouseDown ); + }; + }, [ iframeRef, dragHandleSelector ] ); + + useEffect( () => { + if ( ! pressed ) { + return; + } + + const iframeDocument = iframeRef.current?.contentDocument ?? iframeRef.current?.contentWindow?.document; + if ( ! iframeDocument ) { + return; + } + + const handleMouseMove = throttleToAnimationFrames( ( event: MouseEvent ) => { + if ( ! ref.current || ! iframeRect.current || ! originalItemRect.current || ! startPosition.current ) { + return; + } + + // Calculate total delta using absolute positions instead of event movementX + // and movementY, which don't work properly in Safari within an iframe. + const currentMouseDelta = { + x: event.clientX - startPosition.current.x, + y: event.clientY - startPosition.current.y, + }; + + totalDelta.current = { + x: startTotalDelta.current.x + currentMouseDelta.x, + y: startTotalDelta.current.y + currentMouseDelta.y, + }; + + positionDelta.current = onDrag( { + totalDelta: { x: totalDelta.current.x, y: totalDelta.current.y }, + originalItemRect: originalItemRect.current, + iframeRect: iframeRect.current, + } ); + + ref.current.style.transform = `translate(${ positionDelta.current.x }px, ${ positionDelta.current.y }px)`; + } ); + + const handleMouseUp = () => { + // After the drag ends, reset total delta to match the current position. + totalDelta.current = positionDelta.current; + setPressed( false ); + }; + + iframeDocument.addEventListener( 'mousemove', handleMouseMove ); + iframeDocument.addEventListener( 'mouseup', handleMouseUp ); + + return () => { + handleMouseMove.cancel(); + iframeDocument.removeEventListener( 'mousemove', handleMouseMove ); + iframeDocument.removeEventListener( 'mouseup', handleMouseUp ); + }; + }, [ pressed, onDrag, iframeRef ] ); + + return [ externalRef, pressed ]; +}; + +/** + * Throttles function calls to animation frames for smooth rendering. + * + * This utility function ensures that the provided function is called at most + * once per animation frame, preventing excessive executions during rapid events + * like mouse movements. + * + * @template Args The type of arguments the function accepts. + * @template Return The return type of the function. + * @param {Function} f The function to throttle. + * + * @return {Function & { cancel: Function }} An object with a throttled wrap of the function + * and a cancel method to cancel pending calls. + */ +const throttleToAnimationFrames = ( + f: ( ...args: Args ) => Return +) => { + let token: number|null = null; + let lastArgs: Args|null = null; + + const invoke = () => { + if ( lastArgs !== null ) { + f( ...lastArgs ); + } + + token = null; + }; + + const result = ( ...args: Args ) => { + lastArgs = args; + if ( ! token ) { + token = requestAnimationFrame( invoke ); + } + }; + + result.cancel = () => token && cancelAnimationFrame( token ); + return result; +}; diff --git a/src/content-helper/dashboard-page/pages/traffic-boost/preview/hooks/use-iframe-highlight.ts b/src/content-helper/dashboard-page/pages/traffic-boost/preview/hooks/use-iframe-highlight.ts index 37bd7754bb..e70814532a 100644 --- a/src/content-helper/dashboard-page/pages/traffic-boost/preview/hooks/use-iframe-highlight.ts +++ b/src/content-helper/dashboard-page/pages/traffic-boost/preview/hooks/use-iframe-highlight.ts @@ -1,16 +1,25 @@ /** - * WordPress imports + * WordPress dependencies */ -import { useCallback } from '@wordpress/element'; +import { createRoot, useCallback } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; /** - * Internal imports + * Internal dependencies */ +import { throttle } from '@wordpress/compose'; import { escapeRegExp } from '../../../../../common/utils/functions'; import { TrafficBoostLink } from '../../provider'; import { LinkType } from '../components/link-counter'; import { TextSelection } from '../preview'; +import { DRAG_MARGIN_PX } from './use-draggable'; +import { useWordpressComponentStyles } from './use-wordpress-component-styles'; + +declare global { + interface Window { + wpParselyTrafficBoostCleanupActionsBar?: () => void; + } +} /** * Props for the useIframeHighlight hook. @@ -24,6 +33,7 @@ interface UseIframeHighlightProps { selectedText?: TextSelection | null; isInboundLink: boolean; onRestoreOriginal: () => void; + actionsBar: React.ReactNode; } /** @@ -42,7 +52,10 @@ export const useIframeHighlight = ( { selectedText, isInboundLink, onRestoreOriginal, + actionsBar, }: UseIframeHighlightProps ) => { + const { injectWordpressComponentStyles } = useWordpressComponentStyles(); + /** * Injects highlight styles into the iframe. * @@ -56,6 +69,8 @@ export const useIframeHighlight = ( { return; } + injectWordpressComponentStyles( iframeDocument ); + const style = iframeDocument.createElement( 'style' ); style.textContent = ` /** Smart link highlight styles. */ @@ -152,9 +167,89 @@ export const useIframeHighlight = ( { color: inherit; } } + + /* Actions bar styles. */ + .parsely-traffic-boost-actions-container { + position: absolute; + z-index: 1000; + top: ${ DRAG_MARGIN_PX }px; + left: ${ DRAG_MARGIN_PX }px; + user-select: none; + opacity: 0; + transition: opacity 0.1s ease-in-out; + } + + .parsely-traffic-boost-actions-container.fade-in { + opacity: 1; + } + + .parsely-traffic-boost-actions-container.align-left { + left: ${ DRAG_MARGIN_PX }px; + } + + .parsely-traffic-boost-actions-container.align-right { + left: auto; + right: ${ DRAG_MARGIN_PX }px; + } + + .parsely-traffic-boost-actions-container .traffic-boost-preview-actions { + /* Reset font family to editor defaults to avoid inheriting frontend font in actions toolbar. */ + font-family: -apple-system, BlinkMacSystemFont,"Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell,"Helvetica Neue", sans-serif; + height: 48px; + display: flex; + justify-content: center; + align-items: center; + background: rgba(255, 255, 255, 1); + border: 1px solid #1e1e1e; + border-radius: 2px; + gap: 8px; + } + + .parsely-traffic-boost-actions-container .traffic-boost-preview-actions-drag-handle { + flex-shrink: 0; + cursor: grab; + border-right: 1px solid #1e1e1e; + padding: 0 8px; + height: 100%; + display: flex; + align-items: center; + } + + .parsely-traffic-boost-actions-container .traffic-boost-preview-actions-drag-handle.dragging { + cursor: grabbing; + } + + .parsely-traffic-boost-actions-container .traffic-boost-preview-actions-buttons { + display: flex; + gap: 4px; + align-items: center; + flex-wrap: nowrap; + justify-content: center; + padding-right: 8px; + } + + .parsely-traffic-boost-actions-container .traffic-boost-preview-actions-buttons .components-button { + height: 36px; + white-space: nowrap; + } + + .parsely-traffic-boost-actions-container .traffic-boost-preview-actions-hint { + display: flex; + cursor: help; + user-select: none; + align-items: center; + } + + .parsely-traffic-boost-actions-container .traffic-boost-preview-actions-hint-text { + font-size: 13px; + font-family: inherit; + white-space: nowrap; + margin-left: 4px; + color: #2F2F2F; + } `; iframeDocument.head.appendChild( style ); - }, [] ); + }, [ injectWordpressComponentStyles ] ); /** * Finds all ranges containing the text. @@ -263,6 +358,11 @@ export const useIframeHighlight = ( { return; } + const existingActions = iframeDocument.querySelector( '.parsely-traffic-boost-actions-container' ); + if ( existingActions && window.wpParselyTrafficBoostCleanupActionsBar ) { + window.wpParselyTrafficBoostCleanupActionsBar(); + } + const fragment = range.cloneContents(); const highlightSpan = iframeDocument.createElement( 'span' ); highlightSpan.className = isPrevious @@ -288,7 +388,6 @@ export const useIframeHighlight = ( { if ( isFullLinkSelected && linkNode ) { // Create a new span and insert it before the link. linkNode.parentNode?.insertBefore( highlightSpan, linkNode ); - // Move the link into the span. highlightSpan.appendChild( linkNode ); } else { @@ -298,12 +397,109 @@ export const useIframeHighlight = ( { range.insertNode( highlightSpan ); } + const actionsContainer = iframeDocument.createElement( 'div' ); + actionsContainer.className = 'parsely-traffic-boost-actions-container'; + iframeDocument.body.appendChild( actionsContainer ); + + // Create popover content. + const root = createRoot( actionsContainer ); + root.render( actionsBar ); + + /** + * Sets up the actions bar cleanup function. + * + * @since 3.20.0 + */ + window.wpParselyTrafficBoostCleanupActionsBar = () => { + window.wpParselyTrafficBoostCleanupActionsBar = undefined; + + // resizeHandler is throttled, so cancel any pending calls. + if ( typeof resizeHandler.cancel === 'function' ) { + resizeHandler.cancel(); + } + + iframeDocument.defaultView?.removeEventListener( 'resize', resizeHandler ); + + root.unmount(); + + if ( actionsContainer.parentNode ) { + actionsContainer.parentNode.removeChild( actionsContainer ); + } + }; + + /** + * Positions the actions bar, ensuring it remains visible and + * aligned within boundaries. + * + * @since 3.20.0 + */ + const positionActionsBar = () => { + const renderedActionsBar = iframeDocument.querySelector( '.traffic-boost-preview-actions' ) as HTMLElement; + if ( ! renderedActionsBar ) { + return; + } + + // Reset any transform that's already applied to the + // actionsBar from a manual drag. + renderedActionsBar.style.transform = ''; + + const highlightRect = highlightSpan.getBoundingClientRect(); + const iframeRect = iframeDocument.documentElement.getBoundingClientRect(); + const actionsRect = renderedActionsBar.getBoundingClientRect(); + + // Reset any existing alignment classes. + actionsContainer.classList.remove( 'align-left', 'align-right' ); + + // Calculate base position above highlight, accounting for scroll position. + const PIXELS_ABOVE_HIGHLIGHT = 35; + const scrollTop = iframeDocument.documentElement.scrollTop; + const top = highlightRect.top + scrollTop - PIXELS_ABOVE_HIGHLIGHT - actionsRect.height; + const left = highlightRect.left + ( highlightRect.width / 2 ) - ( actionsRect.width / 2 ); + + // Set initial position + actionsContainer.style.top = `${ Math.max( top, 0 ) }px`; + + // Check if the actions bar would be cut off on either side. + const actionsWidth = actionsRect.width; + const iframeWidth = iframeRect.width; + const actionsLeft = left; + const actionsRight = left + actionsWidth; + + if ( actionsRight > iframeWidth ) { + // Would be cut off on right, align to right. + actionsContainer.classList.add( 'align-right' ); + actionsContainer.style.left = ''; // Clear inline left style. + } else if ( actionsLeft < 0 ) { + // Would be cut off on left, align to left. + actionsContainer.classList.add( 'align-left' ); + actionsContainer.style.left = ''; // Clear inline left style. + } else { + // Center position is fine, set left directly. + actionsContainer.style.left = `${ left }px`; + } + + // Add fade-in animation after positioning. + actionsContainer.classList.add( 'fade-in' ); + }; + + // Setup initial position. Wait 400ms for auto-scroll to complete + // so that position calculations from scrollTop are correct. + if ( iframeDocument.documentElement.scrollTop === 0 ) { + setTimeout( positionActionsBar, 400 ); + } else { + setTimeout( positionActionsBar, 0 ); + } + + // Reposition on resize. + const resizeHandler = throttle( () => positionActionsBar(), 100 ); + iframeDocument.defaultView?.addEventListener( 'resize', resizeHandler ); + return highlightSpan; } catch ( e ) { // eslint-disable-next-line no-console console.error( 'WP Parsely: Error highlighting range', e ); } - }, [ iframeRef ] ); + }, [ iframeRef, actionsBar ] ); /** * Removes highlight spans from the iframe content. @@ -327,9 +523,14 @@ export const useIframeHighlight = ( { * @since 3.19.0 * * @param {Element} highlight The highlight element to remove. - * @param {ParentNode} parent The parent node of the highlight. + * @param {ParentNode} parent The parent node of the container, e.g. a

    tag. */ const removeAndClean = ( highlight: Element, parent: ParentNode ) => { + // Clean up actions bar if it exists + if ( window.wpParselyTrafficBoostCleanupActionsBar ) { + window.wpParselyTrafficBoostCleanupActionsBar(); + } + // Create a document fragment to temporarily hold the children. const fragment = iframeDocument.createDocumentFragment(); @@ -405,7 +606,7 @@ export const useIframeHighlight = ( { } } ); } catch ( error ) { - // Silently fail if there's an error removing highlights. + console.error( 'WP Parsely: Error removing highlights:', error ); // eslint-disable-line no-console } }, [] ); diff --git a/src/content-helper/dashboard-page/pages/traffic-boost/preview/hooks/use-wordpress-component-styles.ts b/src/content-helper/dashboard-page/pages/traffic-boost/preview/hooks/use-wordpress-component-styles.ts new file mode 100644 index 0000000000..4a2b61bc6d --- /dev/null +++ b/src/content-helper/dashboard-page/pages/traffic-boost/preview/hooks/use-wordpress-component-styles.ts @@ -0,0 +1,37 @@ +/** + * WordPress dependencies + */ +import { useCallback } from '@wordpress/element'; + +/** + * Injects WordPress component styles into a document. + * + * @since 3.20.0 + * + * @return {Object} An object containing the injectWordpressComponentStyles function. + */ +export const useWordpressComponentStyles = () => { + const injectWordpressComponentStyles = useCallback( ( iframeDocument: Document ) => { + const urlWpComponents = window?.wpParselyDependencies?.urlWpComponents; + + if ( ! urlWpComponents ) { + console.error( 'WordPress component styles URL not found' ); // eslint-disable-line no-console + return; + } + + let wordpressComponentStyling: HTMLLinkElement | null = iframeDocument.querySelector( 'link[data-wp-parsely-component-styles]' ); + + if ( wordpressComponentStyling === null ) { + // Inject WordPress components styles. + wordpressComponentStyling = iframeDocument.createElement( 'link' ); + wordpressComponentStyling.rel = 'stylesheet'; + wordpressComponentStyling.href = urlWpComponents; + wordpressComponentStyling.setAttribute( 'data-wp-parsely-component-styles', 'true' ); + iframeDocument.head.appendChild( wordpressComponentStyling ); + } + }, [] ); + + return { + injectWordpressComponentStyles, + }; +}; diff --git a/src/content-helper/dashboard-page/pages/traffic-boost/preview/preview.scss b/src/content-helper/dashboard-page/pages/traffic-boost/preview/preview.scss index e879a327e2..c6d7a37a08 100644 --- a/src/content-helper/dashboard-page/pages/traffic-boost/preview/preview.scss +++ b/src/content-helper/dashboard-page/pages/traffic-boost/preview/preview.scss @@ -108,7 +108,7 @@ background: var(--white); z-index: 5; - .traffic-boost-preview-actions { + .traffic-boost-preview-header-actions { display: flex; gap: var(--grid-unit-10); diff --git a/src/content-helper/dashboard-page/pages/traffic-boost/preview/preview.tsx b/src/content-helper/dashboard-page/pages/traffic-boost/preview/preview.tsx index 76c822b50d..ad3f79a240 100644 --- a/src/content-helper/dashboard-page/pages/traffic-boost/preview/preview.tsx +++ b/src/content-helper/dashboard-page/pages/traffic-boost/preview/preview.tsx @@ -3,7 +3,7 @@ */ import { Icon } from '@wordpress/components'; import { useDispatch, useSelect } from '@wordpress/data'; -import { useEffect, useState } from '@wordpress/element'; +import { useEffect, useState, useCallback } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { error, link as linkIcon, linkOff } from '@wordpress/icons'; import { addQueryArgs } from '@wordpress/url'; @@ -162,6 +162,11 @@ export const TrafficBoostPreview = ( { } }, [ activePost, isFrontendPreview, previewUrl ] ); // eslint-disable-line react-hooks/exhaustive-deps + // Use useCallback for onRestoreOriginal to maintain reference stability + const handleRestoreOriginal = useCallback( () => { + setSelectedText( null ); + }, [] ); + /** * Opens the post in a new tab. * @@ -476,6 +481,7 @@ export const TrafficBoostPreview = ( { isFrontendPreview={ isFrontendPreview } setIsFrontendPreview={ setIsFrontendPreview } /> + { setSelectedText( { text, offset } ); } } - onRestoreOriginal={ () => { - setSelectedText( null ); - } } + onRestoreOriginal={ handleRestoreOriginal } isFrontendPreview={ isFrontendPreview } onLoadingChange={ setIsLoading } + onAccept={ handleAccept } + onDiscard={ handleDiscard } + onUpdateLink={ handleUpdateLink } + onRemove={ handleRemove } /> + { - setSelectedText( null ); - } } - selectedText={ selectedText } /> ); diff --git a/src/content-helper/editor-sidebar/smart-linking/utils.ts b/src/content-helper/editor-sidebar/smart-linking/utils.ts index 1f215bccef..f7c8152e21 100644 --- a/src/content-helper/editor-sidebar/smart-linking/utils.ts +++ b/src/content-helper/editor-sidebar/smart-linking/utils.ts @@ -769,4 +769,3 @@ export function addSmartLinkITMParamsToURL( url: string, smartLinkUid: string ): term: smartLinkUid, } ); } - diff --git a/src/services/suggestions-api/class-suggestions-api-service.php b/src/services/suggestions-api/class-suggestions-api-service.php index 132cc6ead9..fb8cb5437f 100644 --- a/src/services/suggestions-api/class-suggestions-api-service.php +++ b/src/services/suggestions-api/class-suggestions-api-service.php @@ -19,8 +19,6 @@ * * @since 3.17.0 * - * @link https://content-suggestions-api.parsely.net/prod/docs - * * @phpstan-import-type Endpoint_Suggest_Brief_Options from Endpoints\Endpoint_Suggest_Brief * @phpstan-import-type Endpoint_Suggest_Headline_Options from Endpoints\Endpoint_Suggest_Headline * @phpstan-import-type Endpoint_Suggest_Linked_Reference_Options from Endpoints\Endpoint_Suggest_Linked_Reference @@ -36,7 +34,7 @@ class Suggestions_API_Service extends Base_API_Service { * @return string */ public static function get_base_url(): string { - return 'https://content-suggestions-api.parsely.net/prod'; + return 'https://suggestions-api.parsely.com'; } /** diff --git a/src/services/suggestions-api/endpoints/class-endpoint-suggest-brief.php b/src/services/suggestions-api/endpoints/class-endpoint-suggest-brief.php index 8e0d3a20cc..8837d00e73 100644 --- a/src/services/suggestions-api/endpoints/class-endpoint-suggest-brief.php +++ b/src/services/suggestions-api/endpoints/class-endpoint-suggest-brief.php @@ -17,8 +17,6 @@ * * @since 3.17.0 * - * @link https://content-suggestions-api.parsely.net/prod/docs#/default/suggest_brief_suggest_brief_post - * * @phpstan-type Endpoint_Suggest_Brief_Options = array{ * persona?: string, * style?: string, diff --git a/src/services/suggestions-api/endpoints/class-endpoint-suggest-headline.php b/src/services/suggestions-api/endpoints/class-endpoint-suggest-headline.php index 05e6694df8..9a50c92200 100644 --- a/src/services/suggestions-api/endpoints/class-endpoint-suggest-headline.php +++ b/src/services/suggestions-api/endpoints/class-endpoint-suggest-headline.php @@ -17,8 +17,6 @@ * * @since 3.17.0 * - * @link https://content-suggestions-api.parsely.net/prod/docs#/default/suggest_headline_suggest_headline_post - * * @phpstan-type Traffic_Source = array{ * source: string, * weight: float diff --git a/src/services/suggestions-api/endpoints/class-endpoint-suggest-inbound-link-positions.php b/src/services/suggestions-api/endpoints/class-endpoint-suggest-inbound-link-positions.php index 0eca244941..2f94853320 100644 --- a/src/services/suggestions-api/endpoints/class-endpoint-suggest-inbound-link-positions.php +++ b/src/services/suggestions-api/endpoints/class-endpoint-suggest-inbound-link-positions.php @@ -19,8 +19,6 @@ * * @since 3.19.0 * - * @link https://content-suggestions-api.parsely.net/prod/docs#/prototype/suggest_inbound_link_positions_suggest_inbound_link_positions_post - * * @phpstan-type LinkPositionResponse = array{ * anchor_texts: array, * title: string, diff --git a/src/services/suggestions-api/endpoints/class-endpoint-suggest-inbound-links.php b/src/services/suggestions-api/endpoints/class-endpoint-suggest-inbound-links.php index 04b2c9705e..ef595e8720 100644 --- a/src/services/suggestions-api/endpoints/class-endpoint-suggest-inbound-links.php +++ b/src/services/suggestions-api/endpoints/class-endpoint-suggest-inbound-links.php @@ -18,8 +18,6 @@ * * @since 3.19.0 * - * @link https://content-suggestions-api.parsely.net/prod/docs#/default/suggest_inbound_links_suggest_inbound_links_post - * * @phpstan-type Endpoint_Suggest_Inbound_Links_Options = array{ * max_items?: int, * url_exclusion_list?: array, diff --git a/src/services/suggestions-api/endpoints/class-endpoint-suggest-linked-reference.php b/src/services/suggestions-api/endpoints/class-endpoint-suggest-linked-reference.php index ae962ba1b4..f47802e6cb 100644 --- a/src/services/suggestions-api/endpoints/class-endpoint-suggest-linked-reference.php +++ b/src/services/suggestions-api/endpoints/class-endpoint-suggest-linked-reference.php @@ -18,8 +18,6 @@ * * @since 3.17.0 * - * @link https://content-suggestions-api.parsely.net/prod/docs#/default/suggest_linked_reference_suggest_linked_reference_post - * * @phpstan-type Traffic_Source = array{ * source: string, * weight: float diff --git a/src/services/suggestions-api/endpoints/class-suggestions-api-base-endpoint.php b/src/services/suggestions-api/endpoints/class-suggestions-api-base-endpoint.php index c352d13e07..8c6eda584a 100644 --- a/src/services/suggestions-api/endpoints/class-suggestions-api-base-endpoint.php +++ b/src/services/suggestions-api/endpoints/class-suggestions-api-base-endpoint.php @@ -18,8 +18,6 @@ * * @since 3.17.0 * - * @link https://content-suggestions-api.parsely.net/prod/docs - * * @phpstan-import-type WP_HTTP_Response from Base_Service_Endpoint * @phpstan-import-type WP_HTTP_Request_Args from Base_Service_Endpoint */ @@ -46,7 +44,10 @@ abstract class Suggestions_API_Base_Endpoint extends Base_Service_Endpoint { protected function get_request_options( string $method ): array { $options = array( 'method' => $method, - 'headers' => array( 'Content-Type' => 'application/json; charset=utf-8' ), + 'headers' => array( + 'Content-Type' => 'application/json; charset=utf-8', + 'X-INTERNAL-SERVICE' => 'content-helper', + ), 'data_format' => 'body', 'timeout' => 90, //phpcs:ignore WordPressVIPMinimum.Performance.RemoteRequestTimeout.timeout_timeout 'body' => '{}', diff --git a/tests/Unit/Utils/UtilsTest.php b/tests/Unit/Utils/UtilsTest.php index 732c6af846..fbf67132d6 100644 --- a/tests/Unit/Utils/UtilsTest.php +++ b/tests/Unit/Utils/UtilsTest.php @@ -41,7 +41,7 @@ * * @phpstan-type Test_Convert_To_Associative_Data array{ * args: array{ - * obj: stdClass, + * obj: \stdClass, * }, * expected_output: array, * msg: string, diff --git a/tests/e2e/utils.ts b/tests/e2e/utils.ts index 9d8496e892..543beee31b 100644 --- a/tests/e2e/utils.ts +++ b/tests/e2e/utils.ts @@ -8,7 +8,7 @@ import { type Page } from '@playwright/test'; */ import { Admin } from '@wordpress/e2e-test-utils-playwright'; -export const PLUGIN_VERSION = '3.19.3'; +export const PLUGIN_VERSION = '3.20.0'; export const VALID_SITE_ID = 'demoaccount.parsely.com'; export const INVALID_SITE_ID = 'invalid.parsely.com'; export const VALID_API_SECRET = 'valid_api_secret'; diff --git a/wp-parsely.php b/wp-parsely.php index 3c2426c5c1..cc25df2024 100644 --- a/wp-parsely.php +++ b/wp-parsely.php @@ -11,14 +11,14 @@ * Plugin Name: Parse.ly * Plugin URI: https://docs.parse.ly/wordpress * Description: This plugin makes it a snap to add Parse.ly tracking code and metadata to your WordPress blog. - * Version: 3.19.3 + * Version: 3.20.0 * Author: Parse.ly * Author URI: https://www.parse.ly * Text Domain: wp-parsely * License: GPL-2.0-or-later * License URI: http://www.gnu.org/licenses/gpl-2.0.txt * GitHub Plugin URI: https://github.com/Parsely/wp-parsely - * Requires PHP: 7.2 + * Requires PHP: 7.4 * Requires WP: 6.0.0 */ @@ -49,7 +49,7 @@ return; } -const PARSELY_VERSION = '3.19.3'; +const PARSELY_VERSION = '3.20.0'; const PARSELY_FILE = __FILE__; const PARSELY_DATA_SCHEMA_VERSION = '1'; const PARSELY_CACHE_GROUP = 'wp-parsely';