From 7b76d712c677f8c407c1a4617593f728a896ac64 Mon Sep 17 00:00:00 2001 From: Imants Date: Mon, 12 Oct 2026 09:21:43 +0000 Subject: [PATCH 1/2] prep release v4.0.0 (#584) Co-authored-by: Shea Bunge Co-authored-by: TallblokeUK Co-authored-by: Rami Yushuvaev Co-authored-by: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Co-authored-by: lightbulbman --- .github/workflows/ci.yml | 2 + .github/workflows/playwright-test.yml | 22 +- .github/workflows/playwright.yml | 4 +- .gitignore | 7 + .nvmrc | 1 + AGENTS.md | 43 +- CHANGELOG.md | 306 ++++--- config/playwright/playwright.config.ts | 39 +- config/webpack/webpack-js.ts | 5 +- package-lock.json | 532 +++++++++---- package.json | 5 +- scripts/install-wp-tests.sh | 41 +- scripts/linters/lint-changelog.ts | 5 - scripts/linters/lint-readme.ts | 5 - scripts/test-setup-phpunit.ts | 126 +-- scripts/test-setup-playwright.ts | 20 +- src/code-snippets.php | 6 +- src/css/admin-bar.scss | 18 +- src/css/common/_badges.scss | 61 +- src/css/common/_banners.scss | 43 - src/css/common/_buttons.scss | 32 + src/css/common/_cards.scss | 20 +- src/css/common/_checkbox.scss | 14 +- src/css/common/_codemirror.scss | 4 +- src/css/common/_demo.scss | 225 ++++++ src/css/common/_direction.scss | 8 - src/css/common/_kebab-menu.scss | 24 +- src/css/common/_list-table.scss | 27 + src/css/common/_modal.scss | 44 +- src/css/common/_notices.scss | 23 +- src/css/common/_page-header.scss | 22 +- src/css/common/_page-subtitle.scss | 13 + src/css/common/_subnav.scss | 63 +- src/css/common/_switch.scss | 35 +- src/css/common/_theme.scss | 92 +-- src/css/common/_toolbar.scss | 203 +++-- src/css/common/_tooltips.scss | 17 +- src/css/common/_upsell.scss | 26 +- src/css/common/_wp-admin.scss | 10 +- src/css/common/list-table/_layout.scss | 88 ++- src/css/common/list-table/_navigation.scss | 24 +- src/css/common/list-table/_pagination.scss | 2 +- src/css/common/list-table/_responsive.scss | 33 +- src/css/edit.scss | 27 +- src/css/edit/_conditions.scss | 9 +- src/css/edit/_form.scss | 27 +- src/css/edit/_gpt.scss | 35 +- src/css/edit/_sidebar.scss | 157 ++-- src/css/feedback.scss | 326 ++++++++ src/css/import.scss | 2 + src/css/import/_card.scss | 7 +- src/css/import/_migrate.scss | 19 +- src/css/import/_page.scss | 4 +- src/css/import/_upload.scss | 75 +- src/css/manage.scss | 12 +- src/css/manage/_ai-agent-demo.scss | 129 +++ src/css/manage/_ai-agent.scss | 748 ++++++++++++++++++ src/css/manage/_blueprints-demo.scss | 96 +++ src/css/manage/_blueprints.scss | 3 + src/css/manage/_cloud-community-cards.scss | 30 +- src/css/manage/_cloud-community.scss | 44 +- src/css/manage/_cloud-library-demo.scss | 5 + src/css/manage/_cloud-library.scss | 135 ++++ src/css/manage/_snippets-table.scss | 167 +++- src/css/manage/blueprints/_detail.scss | 50 ++ src/css/manage/blueprints/_form-fields.scss | 137 ++++ src/css/manage/blueprints/_form-layout.scss | 162 ++++ src/css/menu.scss | 10 +- src/css/prism.scss | 2 +- src/css/settings.scss | 98 ++- src/css/stats.scss | 272 +++++++ src/css/welcome.scss | 96 ++- .../ConditionModal/ConditionModalButton.tsx | 6 +- src/js/components/EditMenu/EditMenu.tsx | 3 +- .../EditMenu/EditorSidebar/EditorSidebar.tsx | 11 +- .../EditorSidebar/actions/ExportButtons.tsx | 12 +- .../EditorSidebar/controls/LockControl.tsx | 1 + .../EditorSidebar/controls/PriorityInput.tsx | 2 +- .../EditMenu/SnippetForm/SnippetForm.tsx | 2 + .../SnippetForm/fields/CodeEditor.tsx | 57 +- .../fields/SnippetLocationInput.tsx | 36 +- .../EditMenu/SnippetForm/page/PageHeading.tsx | 56 +- .../FeedbackReporter/BugDetailFields.tsx | 44 ++ .../FeedbackReporter/DuplicateReports.tsx | 20 + .../EnvironmentDisclosure.tsx | 33 + .../FeedbackReporter/FeedbackForm.tsx | 66 ++ .../FeedbackReporter/FeedbackPanel.tsx | 53 ++ .../FeedbackReporter/FeedbackReporter.tsx | 32 + .../FeedbackReporter/FeedbackSuccess.tsx | 26 + .../FeedbackReporter/HeadingBadge.tsx | 32 + .../FeedbackReporter/ReportSummaryFields.tsx | 59 ++ .../FeedbackReporter/ReporterFields.tsx | 38 + .../ImportMenu/MigrateForm/MigrateForm.tsx | 2 - .../MigrateForm/WithMigrationData.tsx | 6 +- .../UploadForm/SelectFiles/UploadButton.tsx | 2 +- .../SelectSnippets/SelectSnippets.tsx | 2 +- .../ManageMenu/AiAgentDemo/AiAgentDemo.tsx | 178 +++++ .../ManageMenu/AiAgentDemo/DemoPlanCard.tsx | 58 ++ .../ManageMenu/AiAgentDemo/DemoPromptBox.tsx | 54 ++ .../ManageMenu/AiAgentDemo/DemoResultCard.tsx | 107 +++ .../ManageMenu/AiAgentDemo/DemoSidebar.tsx | 192 +++++ .../ManageMenu/AiAgentDemo/DemoThread.tsx | 35 + .../ManageMenu/AiAgentDemo/DemoUpsell.tsx | 12 + .../ManageMenu/AiAgentDemo/callouts.ts | 61 ++ .../ManageMenu/AiAgentDemo/demoScript.ts | 191 +++++ .../ManageMenu/AiAgentDemo/types.ts | 79 ++ .../ManageMenu/AiAgentDemo/useAiAgentDemo.ts | 206 +++++ .../BlueprintsDemo/BlueprintFormPanel.tsx | 92 +++ .../BlueprintsDemo/BlueprintSidebar.tsx | 51 ++ .../BlueprintsDemo/BlueprintsDemo.tsx | 141 ++++ .../BlueprintsDemo/GeneratedNotice.tsx | 23 + .../ManageMenu/BlueprintsDemo/callouts.ts | 38 + .../BlueprintsDemo/demoBlueprint.ts | 131 +++ .../ManageMenu/BlueprintsDemo/types.ts | 52 ++ .../BlueprintsDemo/useBlueprintsDemo.ts | 145 ++++ .../CloudLibraryDemo/CloudLibraryDemo.tsx | 113 +++ .../ManageMenu/CloudLibraryDemo/callouts.ts | 29 + .../cloudLibraryDemoColumns.tsx | 141 ++++ .../CloudLibraryDemo/demoCloudSnippets.ts | 100 +++ .../ManageMenu/CloudLibraryDemo/types.ts | 20 + .../CloudLibraryDemo/useCloudLibraryDemo.ts | 98 +++ .../ManageMenu/CommunityCloud/CloudSearch.tsx | 85 +- .../CommunityCloud/CloudSnippetsTable.tsx | 51 +- .../CommunityCloud/WithCloudSearchContext.tsx | 15 +- src/js/components/ManageMenu/ManageMenu.tsx | 22 +- .../SnippetsTable/ManageSnippetCard.tsx | 38 +- .../ManageMenu/SnippetsTable/RowActions.tsx | 7 +- .../SnippetsTable/SnippetsTable.tsx | 6 +- .../ManageMenu/SnippetsTable/TableColumns.tsx | 75 +- .../WithFilteredSnippetsContext.tsx | 15 +- src/js/components/ManageMenu/subpages.tsx | 41 + .../StatsMenu/StatsChartViewToggle.tsx | 66 ++ src/js/components/StatsMenu/StatsCharts.tsx | 171 ++++ .../components/StatsMenu/StatsDashboard.tsx | 211 +++++ src/js/components/StatsMenu/StatsMenu.tsx | 24 + src/js/components/StatsMenu/index.ts | 1 + src/js/components/WelcomeMenu/WelcomeMenu.tsx | 11 +- .../common/ListTable/TableNavigation.tsx | 2 +- .../common/LoadingStatusNotices.tsx | 5 +- src/js/components/common/SubnavTabs.tsx | 78 +- src/js/components/common/Toolbar.tsx | 120 ++- .../cloud/CloudSnippetCard.tsx} | 37 +- .../cloud/CloudSnippetDownloadButton.tsx | 4 +- src/js/components/common/demo/DemoCallout.tsx | 26 + .../components/common/demo/DemoPageHeader.tsx | 53 ++ .../components/common/demo/DemoSpotlight.tsx | 104 +++ src/js/components/common/demo/DemoUpsell.tsx | 38 + src/js/components/common/demo/useDemoSeen.ts | 34 + .../components/common/icons/AiAgentIcon.tsx | 13 + .../common/icons/CloudUpdateIcon.tsx | 13 - .../common/icons/cloud/CloudAvailableIcon.tsx | 12 + .../common/icons/cloud/CloudErrorIcon.tsx | 12 + .../common/icons/cloud/CloudSyncedIcon.tsx | 12 + .../common/snippets/ConfirmDeleteDialog.tsx | 17 +- .../common/snippets/SnippetPreviewModal.tsx | 35 +- .../common/snippets/SnippetPriorityInput.tsx | 2 +- src/js/entries/feedback-capture.ts | 24 + src/js/entries/feedback.ts | 4 + src/js/entries/stats.ts | 4 + src/js/hooks/useDuplicateReports.ts | 42 + src/js/hooks/useFeedbackReport.ts | 155 ++++ src/js/hooks/useSnippetView.ts | 2 +- src/js/hooks/useSnippetsAPI.tsx | 6 +- src/js/hooks/useSubmitSnippet.tsx | 53 +- src/js/services/settings/editor-preview.ts | 5 + src/js/services/settings/tabs.ts | 23 +- src/js/types/Feedback.ts | 67 ++ src/js/types/Snippet.ts | 8 + src/js/types/Stats.ts | 34 + src/js/types/Window.ts | 16 +- src/js/types/schema/SnippetSchema.ts | 11 +- src/js/utils/errors.ts | 23 +- src/js/utils/files.ts | 5 +- src/js/utils/restAPI.ts | 57 +- src/js/utils/screen.ts | 3 + src/js/utils/snippets/objects.ts | 19 +- src/js/utils/snippets/snippets.ts | 34 +- src/js/utils/snippets/tags.ts | 89 +++ src/js/utils/urls.ts | 12 +- src/php/Admin/Bootstrap_Admin.php | 2 + src/php/Admin/Contextual_Help.php | 224 +++++- src/php/Admin/Feedback_Error_Capture.php | 60 ++ src/php/Admin/Feedback_Panel.php | 232 ++++++ src/php/Admin/Menus/Admin_Menu.php | 17 +- src/php/Admin/Menus/Edit_Menu.php | 124 ++- src/php/Admin/Menus/Manage/Manage_Menu.php | 96 ++- .../Admin/Menus/Manage/Manage_Menu_Assets.php | 12 + .../Manage/Manage_Menu_Bulk_Download.php | 2 +- .../Manage/Manage_Menu_Screen_Options.php | 34 +- src/php/Admin/Menus/Settings_Menu.php | 48 +- src/php/Admin/Menus/Stats/Stats_Menu.php | 78 ++ src/php/Admin/Menus/Stats/Stats_Summary.php | 217 +++++ src/php/Admin/Menus/Welcome_Menu.php | 13 + src/php/Client/Feedback_Client.php | 294 +++++++ src/php/Core/DB.php | 39 +- src/php/Core/Uninstaller.php | 13 +- src/php/Core/load.php | 13 +- .../Handlers/Functions_Snippet_Handler.php | 10 +- src/php/Flat_Files/Snippet_Files.php | 11 +- src/php/Integration/Admin_Bar.php | 57 +- src/php/Integration/Evaluate_Content.php | 72 +- src/php/Integration/Evaluate_Functions.php | 42 +- .../Promotions/Other/Elementor_Editor.php | 18 + src/php/Integration/Shortcodes.php | 60 +- src/php/Model/Basic_Cloud_Connection.php | 3 +- src/php/Model/Cloud_Snippets.php | 21 +- src/php/Model/Feedback_Connection.php | 206 +++++ src/php/Model/Model.php | 2 +- src/php/Model/Snippet.php | 7 + src/php/Plugin.php | 70 +- .../Cloud/Cloud_Snippets_REST_Controller.php | 37 +- .../Feedback/Feedback_REST_Controller.php | 359 +++++++++ .../Demos_Seen_REST_Controller.php | 119 +++ .../Preference_REST_Controller.php | 129 +++ .../Snippet_View_REST_Controller.php | 79 ++ .../Stats_View_Rest_Controller.php | 135 ++++ .../REST_API/REST_Collection_Controller.php | 1 - src/php/REST_API/REST_Controller.php | 1 - .../Snippets/Preferences_REST_Controller.php | 124 --- .../Snippets/Snippets_REST_Controller.php | 45 +- src/php/Settings/Setting_Field.php | 15 +- src/php/Settings/Settings_Fields.php | 154 ++-- src/php/Settings/Settings_Layout.php | 216 +++++ src/php/Settings/settings.php | 129 ++- src/php/Utils/System_Info.php | 134 ++++ src/php/Utils/Validator.php | 84 +- src/php/Utils/editor.php | 2 +- src/php/Utils/options.php | 26 +- src/php/snippet-ops.php | 199 ++++- tests/bootstrap.php | 2 - tests/e2e/admin-screen-headings.spec.ts | 42 + tests/e2e/ai-agent-demo.spec.ts | 122 +++ tests/e2e/auth.setup.ts | 11 +- tests/e2e/badge-contrast.spec.ts | 10 +- tests/e2e/blueprints-demo.spec.ts | 179 +++++ tests/e2e/cloud-download-eligibility.spec.ts | 37 +- tests/e2e/cloud-library-demo.spec.ts | 159 ++++ .../code-snippets-community-featured.spec.ts | 8 +- tests/e2e/code-snippets-evaluation.spec.ts | 160 +++- tests/e2e/code-snippets-list.spec.ts | 269 +++++-- tests/e2e/code-snippets-paste-tags.spec.ts | 132 ++++ .../code-snippets-quicknav-admin-bar.spec.ts | 23 +- tests/e2e/code-snippets-stats.spec.ts | 383 +++++++++ tests/e2e/editor-labels.spec.ts | 21 + tests/e2e/editor-save-outcomes.spec.ts | 64 ++ tests/e2e/feedback-reporter.spec.ts | 139 ++++ tests/e2e/flat-files.setup.ts | 12 +- tests/e2e/helpers/SnippetsTestHelper.ts | 63 +- tests/e2e/helpers/checkbox.ts | 9 +- tests/e2e/helpers/constants.ts | 29 +- tests/e2e/helpers/demoPacing.ts | 37 + tests/e2e/helpers/rtlUser.ts | 13 + tests/e2e/list-toolbar-fit.spec.ts | 35 + tests/e2e/rtl-layout.spec.ts | 80 ++ tests/e2e/rtl.setup.ts | 49 ++ tests/e2e/rtl.teardown.ts | 19 + tests/e2e/screen-meta-slot.spec.ts | 28 + tests/e2e/settings-tabs.spec.ts | 22 +- tests/e2e/toolbar-active-subpage.spec.ts | 37 + tests/unit/Admin/Admin_Bar_Test.php | 14 +- tests/unit/Admin/Contextual_Help_Test.php | 49 ++ tests/unit/Admin/Feedback_Panel_Test.php | 247 ++++++ tests/unit/Admin/Menus/Edit_Menu_Test.php | 92 ++- .../Menus/Manage/Manage_Menu_Assets_Test.php | 56 +- .../Manage/Manage_Menu_Demo_Reset_Test.php | 147 ++++ .../Manage/Manage_Menu_Run_Once_Test.php | 209 +++++ .../Manage_Menu_Screen_Options_Test.php | 56 ++ .../Admin/Menus/Stats/Stats_Menu_Test.php | 49 ++ .../Admin/Menus/Stats/Stats_Summary_Test.php | 284 +++++++ tests/unit/Admin/Notice_Filter_Test.php | 2 + tests/unit/AdminUnitTestCase.php | 2 +- tests/unit/Authorship_Test.php | 144 ++++ tests/unit/Client/Feedback_Client_Test.php | 404 ++++++++++ tests/unit/Core/Autoloader_Prefixes_Test.php | 2 +- tests/unit/Core/Uninstaller_Test.php | 55 ++ tests/unit/Core/Versioned_Cache_Test.php | 66 ++ .../Functions_Snippet_Handler_Test.php | 2 +- .../Other/Elementor_Editor_Test.php | 86 ++ tests/unit/Model/Cloud_Snippets_Test.php | 53 ++ tests/unit/Model/Feedback_Connection_Test.php | 250 ++++++ tests/unit/Plugin_Test.php | 54 ++ .../Cloud_Snippets_REST_Controller_Test.php} | 95 ++- .../Feedback_REST_Controller_Test.php | 484 ++++++++++++ .../Demos_Seen_REST_Controller_Test.php | 153 ++++ .../Snippet_View_Rest_Controller_Test.php} | 33 +- .../Stats_View_Rest_Controller_Test.php | 171 ++++ .../REST_API_Snippets_Permissions_Test.php | 4 +- ...PI_Snippets_Shared_Network_Toggle_Test.php | 2 +- .../{ => Snippets}/REST_API_Snippets_Test.php | 22 +- tests/unit/Settings/Feedback_Setting_Test.php | 59 ++ .../Settings_Early_Translation_Test.php | 103 +++ tests/unit/Settings/Settings_Layout_Test.php | 161 ++++ tests/unit/Snippets/Batch_Activation_Test.php | 15 + .../Cache_Invalidation_Order_Test.php | 219 +++++ .../Snippets/Normalize_Snippet_Code_Test.php | 120 +++ tests/unit/Utils/System_Info_Test.php | 145 ++++ tests/unit/Utils/Validator_Test.php | 70 ++ 297 files changed, 18646 insertions(+), 2136 deletions(-) create mode 100644 .nvmrc delete mode 100644 src/css/common/_banners.scss create mode 100644 src/css/common/_buttons.scss create mode 100644 src/css/common/_demo.scss delete mode 100644 src/css/common/_direction.scss create mode 100644 src/css/common/_page-subtitle.scss create mode 100644 src/css/feedback.scss create mode 100644 src/css/manage/_ai-agent-demo.scss create mode 100644 src/css/manage/_ai-agent.scss create mode 100644 src/css/manage/_blueprints-demo.scss create mode 100644 src/css/manage/_blueprints.scss create mode 100644 src/css/manage/_cloud-library-demo.scss create mode 100644 src/css/manage/_cloud-library.scss create mode 100644 src/css/manage/blueprints/_detail.scss create mode 100644 src/css/manage/blueprints/_form-fields.scss create mode 100644 src/css/manage/blueprints/_form-layout.scss create mode 100644 src/css/stats.scss create mode 100644 src/js/components/FeedbackReporter/BugDetailFields.tsx create mode 100644 src/js/components/FeedbackReporter/DuplicateReports.tsx create mode 100644 src/js/components/FeedbackReporter/EnvironmentDisclosure.tsx create mode 100644 src/js/components/FeedbackReporter/FeedbackForm.tsx create mode 100644 src/js/components/FeedbackReporter/FeedbackPanel.tsx create mode 100644 src/js/components/FeedbackReporter/FeedbackReporter.tsx create mode 100644 src/js/components/FeedbackReporter/FeedbackSuccess.tsx create mode 100644 src/js/components/FeedbackReporter/HeadingBadge.tsx create mode 100644 src/js/components/FeedbackReporter/ReportSummaryFields.tsx create mode 100644 src/js/components/FeedbackReporter/ReporterFields.tsx create mode 100644 src/js/components/ManageMenu/AiAgentDemo/AiAgentDemo.tsx create mode 100644 src/js/components/ManageMenu/AiAgentDemo/DemoPlanCard.tsx create mode 100644 src/js/components/ManageMenu/AiAgentDemo/DemoPromptBox.tsx create mode 100644 src/js/components/ManageMenu/AiAgentDemo/DemoResultCard.tsx create mode 100644 src/js/components/ManageMenu/AiAgentDemo/DemoSidebar.tsx create mode 100644 src/js/components/ManageMenu/AiAgentDemo/DemoThread.tsx create mode 100644 src/js/components/ManageMenu/AiAgentDemo/DemoUpsell.tsx create mode 100644 src/js/components/ManageMenu/AiAgentDemo/callouts.ts create mode 100644 src/js/components/ManageMenu/AiAgentDemo/demoScript.ts create mode 100644 src/js/components/ManageMenu/AiAgentDemo/types.ts create mode 100644 src/js/components/ManageMenu/AiAgentDemo/useAiAgentDemo.ts create mode 100644 src/js/components/ManageMenu/BlueprintsDemo/BlueprintFormPanel.tsx create mode 100644 src/js/components/ManageMenu/BlueprintsDemo/BlueprintSidebar.tsx create mode 100644 src/js/components/ManageMenu/BlueprintsDemo/BlueprintsDemo.tsx create mode 100644 src/js/components/ManageMenu/BlueprintsDemo/GeneratedNotice.tsx create mode 100644 src/js/components/ManageMenu/BlueprintsDemo/callouts.ts create mode 100644 src/js/components/ManageMenu/BlueprintsDemo/demoBlueprint.ts create mode 100644 src/js/components/ManageMenu/BlueprintsDemo/types.ts create mode 100644 src/js/components/ManageMenu/BlueprintsDemo/useBlueprintsDemo.ts create mode 100644 src/js/components/ManageMenu/CloudLibraryDemo/CloudLibraryDemo.tsx create mode 100644 src/js/components/ManageMenu/CloudLibraryDemo/callouts.ts create mode 100644 src/js/components/ManageMenu/CloudLibraryDemo/cloudLibraryDemoColumns.tsx create mode 100644 src/js/components/ManageMenu/CloudLibraryDemo/demoCloudSnippets.ts create mode 100644 src/js/components/ManageMenu/CloudLibraryDemo/types.ts create mode 100644 src/js/components/ManageMenu/CloudLibraryDemo/useCloudLibraryDemo.ts create mode 100644 src/js/components/ManageMenu/subpages.tsx create mode 100644 src/js/components/StatsMenu/StatsChartViewToggle.tsx create mode 100644 src/js/components/StatsMenu/StatsCharts.tsx create mode 100644 src/js/components/StatsMenu/StatsDashboard.tsx create mode 100644 src/js/components/StatsMenu/StatsMenu.tsx create mode 100644 src/js/components/StatsMenu/index.ts rename src/js/components/{ManageMenu/CommunityCloud/SearchResult.tsx => common/cloud/CloudSnippetCard.tsx} (75%) create mode 100644 src/js/components/common/demo/DemoCallout.tsx create mode 100644 src/js/components/common/demo/DemoPageHeader.tsx create mode 100644 src/js/components/common/demo/DemoSpotlight.tsx create mode 100644 src/js/components/common/demo/DemoUpsell.tsx create mode 100644 src/js/components/common/demo/useDemoSeen.ts create mode 100644 src/js/components/common/icons/AiAgentIcon.tsx delete mode 100644 src/js/components/common/icons/CloudUpdateIcon.tsx create mode 100644 src/js/components/common/icons/cloud/CloudAvailableIcon.tsx create mode 100644 src/js/components/common/icons/cloud/CloudErrorIcon.tsx create mode 100644 src/js/components/common/icons/cloud/CloudSyncedIcon.tsx create mode 100644 src/js/entries/feedback-capture.ts create mode 100644 src/js/entries/feedback.ts create mode 100644 src/js/entries/stats.ts create mode 100644 src/js/hooks/useDuplicateReports.ts create mode 100644 src/js/hooks/useFeedbackReport.ts create mode 100644 src/js/types/Feedback.ts create mode 100644 src/js/types/Stats.ts create mode 100644 src/js/utils/snippets/tags.ts create mode 100644 src/php/Admin/Feedback_Error_Capture.php create mode 100644 src/php/Admin/Feedback_Panel.php create mode 100644 src/php/Admin/Menus/Stats/Stats_Menu.php create mode 100644 src/php/Admin/Menus/Stats/Stats_Summary.php create mode 100644 src/php/Client/Feedback_Client.php create mode 100644 src/php/Model/Feedback_Connection.php create mode 100644 src/php/REST_API/Feedback/Feedback_REST_Controller.php create mode 100644 src/php/REST_API/Preferences/Demos_Seen_REST_Controller.php create mode 100644 src/php/REST_API/Preferences/Preference_REST_Controller.php create mode 100644 src/php/REST_API/Preferences/Snippet_View_REST_Controller.php create mode 100644 src/php/REST_API/Preferences/Stats_View_Rest_Controller.php delete mode 100644 src/php/REST_API/Snippets/Preferences_REST_Controller.php create mode 100644 src/php/Settings/Settings_Layout.php create mode 100644 src/php/Utils/System_Info.php create mode 100644 tests/e2e/admin-screen-headings.spec.ts create mode 100644 tests/e2e/ai-agent-demo.spec.ts create mode 100644 tests/e2e/blueprints-demo.spec.ts create mode 100644 tests/e2e/cloud-library-demo.spec.ts create mode 100644 tests/e2e/code-snippets-paste-tags.spec.ts create mode 100644 tests/e2e/code-snippets-stats.spec.ts create mode 100644 tests/e2e/editor-labels.spec.ts create mode 100644 tests/e2e/editor-save-outcomes.spec.ts create mode 100644 tests/e2e/feedback-reporter.spec.ts create mode 100644 tests/e2e/helpers/demoPacing.ts create mode 100644 tests/e2e/helpers/rtlUser.ts create mode 100644 tests/e2e/list-toolbar-fit.spec.ts create mode 100644 tests/e2e/rtl-layout.spec.ts create mode 100644 tests/e2e/rtl.setup.ts create mode 100644 tests/e2e/rtl.teardown.ts create mode 100644 tests/e2e/screen-meta-slot.spec.ts create mode 100644 tests/e2e/toolbar-active-subpage.spec.ts create mode 100644 tests/unit/Admin/Contextual_Help_Test.php create mode 100644 tests/unit/Admin/Feedback_Panel_Test.php create mode 100644 tests/unit/Admin/Menus/Manage/Manage_Menu_Demo_Reset_Test.php create mode 100644 tests/unit/Admin/Menus/Manage/Manage_Menu_Run_Once_Test.php create mode 100644 tests/unit/Admin/Menus/Stats/Stats_Menu_Test.php create mode 100644 tests/unit/Admin/Menus/Stats/Stats_Summary_Test.php create mode 100644 tests/unit/Authorship_Test.php create mode 100644 tests/unit/Client/Feedback_Client_Test.php create mode 100644 tests/unit/Core/Uninstaller_Test.php create mode 100644 tests/unit/Integration/Promotions/Other/Elementor_Editor_Test.php create mode 100644 tests/unit/Model/Feedback_Connection_Test.php create mode 100644 tests/unit/Plugin_Test.php rename tests/unit/REST_API/{REST_API_Cloud_Test.php => Cloud/Cloud_Snippets_REST_Controller_Test.php} (80%) create mode 100644 tests/unit/REST_API/Feedback_REST_Controller_Test.php create mode 100644 tests/unit/REST_API/Preferences/Demos_Seen_REST_Controller_Test.php rename tests/unit/REST_API/{REST_API_Preferences_Test.php => Preferences/Snippet_View_Rest_Controller_Test.php} (75%) create mode 100644 tests/unit/REST_API/Preferences/Stats_View_Rest_Controller_Test.php rename tests/unit/REST_API/{ => Snippets}/REST_API_Snippets_Shared_Network_Toggle_Test.php (99%) rename tests/unit/REST_API/{ => Snippets}/REST_API_Snippets_Test.php (93%) create mode 100644 tests/unit/Settings/Feedback_Setting_Test.php create mode 100644 tests/unit/Settings/Settings_Early_Translation_Test.php create mode 100644 tests/unit/Settings/Settings_Layout_Test.php create mode 100644 tests/unit/Snippets/Cache_Invalidation_Order_Test.php create mode 100644 tests/unit/Snippets/Normalize_Snippet_Code_Test.php create mode 100644 tests/unit/Utils/System_Info_Test.php create mode 100644 tests/unit/Utils/Validator_Test.php diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d1da8d726..7f847239a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -114,6 +114,8 @@ jobs: runs-on: ubuntu-latest permissions: statuses: write + contents: read + checks: read steps: - name: Checkout gate action uses: actions/checkout@v4 diff --git a/.github/workflows/playwright-test.yml b/.github/workflows/playwright-test.yml index debf07092..becb1949c 100644 --- a/.github/workflows/playwright-test.yml +++ b/.github/workflows/playwright-test.yml @@ -240,19 +240,35 @@ jobs: WP_E2E_WPCLI_URL: http://localhost:8888 WP_E2E_WP_CONTAINER: ${{ job.services.wordpress.id }} WP_E2E_MULTISITE_MODE: ${{ inputs.multisite }} + PLAYWRIGHT_PROJECTS: ${{ inputs.project-name }} + PLAYWRIGHT_SUFFIX: ${{ inputs.project-name }}${{ inputs.multisite && '-multisite' || '' }} + shell: bash run: | set -euo pipefail mkdir -p test-results/ci - suffix="${{ inputs.project-name }}${{ inputs.multisite && '-multisite' || '' }}" + suffix="$PLAYWRIGHT_SUFFIX" : > "test-results/ci/playwright-${suffix}.log" - npm run test:playwright -- --project=${{ inputs.project-name }} 2>&1 | tee -a "test-results/ci/playwright-${suffix}.log" + # One --project flag per name, so several projects can be listed in the input. + # The names arrive through the environment rather than being pasted into + # the script, and are split into an array without pathname expansion, so + # no input value is ever interpreted as shell or as a glob. + [[ -n "${PLAYWRIGHT_PROJECTS// /}" ]] || { echo "The project-name input is empty; nothing to run." >&2; exit 1; } + read -ra projects <<< "$PLAYWRIGHT_PROJECTS" + project_args=() + for project in "${projects[@]}"; do + project_args+=(--project "$project") + done + npm run test:playwright -- "${project_args[@]}" 2>&1 | tee -a "test-results/ci/playwright-${suffix}.log" - name: Normalize Playwright report filenames if: always() + env: + PLAYWRIGHT_SUFFIX: ${{ inputs.project-name }}${{ inputs.multisite && '-multisite' || '' }} + shell: bash run: | set -euo pipefail mkdir -p test-results/ci - suffix="${{ inputs.project-name }}${{ inputs.multisite && '-multisite' || '' }}" + suffix="$PLAYWRIGHT_SUFFIX" if [ -f test-results/results.xml ]; then mv test-results/results.xml "test-results/ci/results-${suffix}.xml" diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index aac57a8ce..1ea1c46a4 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -20,7 +20,9 @@ jobs: uses: ./.github/workflows/playwright-test.yml with: test-mode: 'default' - project-name: 'chromium-db-snippets' + # The right-to-left project runs after the default one on the same site; + # its own setup switches the test user's locale and its teardown restores it. + project-name: 'chromium-db-snippets chromium-rtl' multisite: ${{ inputs.multisite }} playwright-file-based-execution: diff --git a/.gitignore b/.gitignore index 453a38177..d52c8fce4 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,10 @@ tmp /.env .superpowers/ docs/superpowers/ +/docs/ + +# Local dev only (wp-env mu-plugin mappings, staging access) +/dev/ + +# Local wp-env overrides — may contain credentials, never commit +.wp-env.override.json diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 000000000..80a9956e1 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +v20.16.0 diff --git a/AGENTS.md b/AGENTS.md index 03d43b5a4..849532580 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -9,16 +9,12 @@ Tool-specific configuration lives in dedicated locations: - **Cursor:** `.cursor/rules/*.mdc` - **Claude:** `claude.md` ---- - ## Project Overview **Code Snippets** is a WordPress plugin that lets site owners manage and execute PHP, HTML, CSS, and JavaScript code snippets through a graphical interface — replacing the need to edit `functions.php` or maintain multiple single-purpose plugins. ---- - ## Repository Structure ``` @@ -50,15 +46,13 @@ plugins. │ ├── dist/ # Webpack output (built assets, not committed) │ ├── vendor/ # Composer dependencies │ └── composer.json # PHP dependency management +├── .github/ # CI workflows, issue templates, Copilot instructions +├── assets/ # WordPress.org screenshots, icons, and banners ├── config/ # Webpack and PostCSS configuration ├── scripts/ # Release, versioning, and linter scripts -├── tests/ # PHPUnit suites, Playwright E2E specs -├── assets/ # WordPress.org screenshots, icons, and banners -└── .github/ # CI workflows, issue templates, Copilot instructions +└── tests/ # PHPUnit suites, Playwright E2E specs ``` ---- - ## Tech Stack | Layer | Technology | @@ -76,8 +70,6 @@ plugins. | Pre-commit | Husky + lint-staged (auto-fix on commit) | | PHP deps | Composer with Imposter (namespace-prefixing) | ---- - ## Coding Standards ### General @@ -107,6 +99,7 @@ plugins. `defined('ABSPATH') || exit;`; do not add guards to autoloaded class files. - Use `wp_die()` for fatal admin errors; never `die()` or `exit` with user-facing output. - In `src/php/Plugin.php` (and Core bootstrap), rely on `autoload.php`; avoid manual `require_once` chains. +- Delete plugin-owned options, transients, and other persisted state during uninstall. - Avoid creating custom database tables. Prefer WordPress-native storage: `wp_options` for settings/flags, transients for cached/temporary data, or hidden custom post types for structured content. Custom tables require manual schema management, migration, and uninstall logic — only justify them when native storage genuinely cannot meet the @@ -138,6 +131,8 @@ plugins. - **Translator comments**: Place `// translators:` comments on the line immediately before the translatable string, not before the enclosing JSX expression. - **Headings**: Admin screens render a single `h1`; section headings within a page start at `h2`. +- **Section names**: Give meaningful sections an accessible name: use `aria-labelledby` for an existing visible + heading and `aria-label` only when no visible heading exists. - **Component composition**: Separate components when doing so clarifies responsibility or enables reuse. Use JSX for markup rather than `createElement` in utility functions. - Do not create 'index.ts' barrel files for components or hooks; import them directly to avoid circular dependencies and @@ -148,6 +143,14 @@ plugins. ### SCSS - Use logical CSS properties (e.g., `margin-inline-start` not `margin-left`) — enforced by stylelint. +- Use CSS variables (`var(--cs-*)`) for themeable runtime values instead of SASS variables. Define shared runtime tokens + under `:root` in `src/css/common/_theme.scss`. +- Keep SASS variables and maps for compile-time generation only, such as selector generators, mixins, and SASS color + operations that cannot operate on runtime `var()` values. +- For custom interactive controls, show keyboard focus with `:focus-visible` using `outline: 2px solid var(--cs-color-accent)`. + A `:focus` rule may clear inherited box shadows, but must not remove the visible keyboard-focus indicator. +- Reuse an existing semantic `--cs-*` token before adding one. Do not add a `var()` fallback unless it is deliberately + required, and use shared card and control radius tokens for repeated radii. ### Code Organization @@ -156,8 +159,6 @@ plugins. - Maintain a direct mapping between source classes and their test files; split source classes and tests only when doing so creates a meaningful separation or clearer testing boundary. ---- - ## Architecture Patterns - **PSR-4 autoloading via Composer** — class files are discovered automatically; do not `require` them manually. @@ -172,8 +173,6 @@ plugins. - **Safe mode** — `src/php/Core/load.php` boots a recovery path when safe mode is active; any change to snippet execution must preserve this path. ---- - ## Branching & Git Workflow - **Production branch:** `core` @@ -208,13 +207,11 @@ plugins. - On the public repository, do not disclose premium (Pro) decisions, features, or roadmap, and avoid unnecessary references to private repositories, issues, or cross-edition work. ---- - ## Development Commands ```bash # Install dependencies -npm install # Node deps + Husky hooks +npm install # Node deps composer -d src install # PHP deps (or: npm run bundle) # WordPress environment @@ -254,8 +251,6 @@ npm run version-rc # Pre-release release candidate npm run version # Final release ``` ---- - ## Security Checklist Apply to every change: @@ -268,19 +263,19 @@ Apply to every change: - Apply `rel="noopener noreferrer"` to every `target="_blank"` link. - Use `$wpdb->prepare()` for every dynamic SQL value. - Any feature fetching remote content must document its trust model. - ---- +- State-changing query-string actions must use an action-specific nonce, sanitize inputs, verify capability, and + redirect to a URL without the action parameters. ## Testing Requirements - **Unit tests** (PHPUnit) — required for all PHP logic changes. - **Integration tests** (PHPUnit) — required when changing DB schema, REST endpoints, or hook behaviour. - **E2E tests** (Playwright) — required for changes affecting snippet create/edit/execute flows. +- **E2E assertions** — prefer roles, accessible names, and states over structural selectors. For viewport geometry, + use only a documented tolerance for unavoidable browser sub-pixel rounding. - **Minimum requirements** - Tests must be compatible with the minimum supported PHP 7.4 and WordPress 5.5. - **Build** - Generated build artifacts (`src/dist/`, Composer autoload maps) must be regenerated when source changes. ---- - ## Do Not - Do not create custom database tables without strong justification — use `wp_options`, transients, or custom post types diff --git a/CHANGELOG.md b/CHANGELOG.md index f947b9eb3..8aff7200c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +1,95 @@ # Changelog +## [4.0.0] (UPCOMING) + +### Added +* Stats page for viewing snippet totals and various snippet breakdowns. +* AI Agent for building snippets from a description: it proposes a plan you can refine or approve before any code is + written, and can revise the snippets it created. (PRO) +* Snippet revisions, with a history of past versions, a side-by-side diff against the current code, and one-click + restore. (PRO) +* Display conditions for controlling where and when a snippet runs, without writing the checks by hand. (PRO) +* Per-role snippet permissions, so you can decide which roles may view, edit, activate, or delete snippets. (PRO) +* Blueprints for setting up a site from a saved collection of snippets and settings. (PRO) +* Flat file storage, for keeping snippets as files so they can be version-controlled alongside the rest of a site. (PRO) +* "Ran on this page" tracking, showing which snippets actually executed on the page you are viewing. (PRO) +* Natural-language search in Community Cloud, so you can describe what you need instead of guessing keywords. (PRO) +* Snippet deployment from Code Snippets Cloud to connected sites, including deploying several snippets under one shared + display condition. (PRO) +* Installing a cloud bundle onto a connected site as a standalone plugin. (PRO) +* Updating Code Snippets Pro on a connected site from the cloud dashboard. (PRO) +* Drift detection, which reports when a snippet on the site no longer matches the copy stored in the cloud. (PRO) +* New admin interface for managing snippets, with a cleaner layout, faster interactions, and a more consistent + experience across plugin screens. (PRO) +* Card view for browsing snippets, with a view switcher on the snippets table and Community Cloud. (PRO) +* Snippet preview modal for viewing snippet code from the snippets table without opening the editor. (PRO) +* Automatic hiding of unrelated admin notices from other plugins on Code Snippets screens. (PRO) +* Admin bar snippet drawer, based on the Deckerweb Snippets workflow, for quick access to snippets and Safe Mode from + the WordPress admin bar. (PRO) +* Snippet locking to help prevent accidental edits or deletion of important snippets. Props + to https://github.com/mgiannopoulos24. (PRO) +* Improved screen options on the main snippets table, including controls for visible columns and truncating long snippet + names or descriptions. (PRO) +* Bulk actions and bulk code download support in the redesigned snippets table. (PRO) +* Featured snippets and improved browsing in Community Cloud. (PRO) +* WordPress modern theme admin styling compatibility. (PRO) +* Clearer accessibility labels, headings, tab markup, table checkboxes, sort buttons, copy buttons, and drag-and-drop + upload controls. (PRO) +* Feedback reporter for sending bug reports, feature requests and general feedback from the plugin screens, with an + optional summary of the site environment so the team can reproduce the problem. (PRO) + +### Changed +* Redesigned the main snippets table with improved search, filtering, sorting, pagination, row actions, and bulk + selection. (PRO) +* Improved the snippet import and migration experience, including clearer file upload handling and third-party plugin + migration flows. (PRO) +* Improved snippet error handling so activation failures, validation errors, and stack traces are easier to understand. + (PRO) +* Improved Community Cloud search and filtering, including server-side filters, better result loading, and clearer empty + states. (PRO) +* Updated the welcome screen, toolbar, import screen, and cloud screens to match the new admin experience. (PRO) +* Updated internal plugin architecture to a cleaner PSR-4 structure for better long-term maintainability. (PRO) +* Improved accessibility across the snippets table, import screen, migration flow, Community Cloud, welcome screen, + toolbar, dialogs, tooltips, and code editor. (PRO) +* Improved colour contrast and reduced-motion support across admin screens. (PRO) +* Faster loading of large code vaults, which are now fetched a page at a time instead of all at once. (PRO) + +### Fixed +* Fixed AI conversations from one connected site being visible from another. (PRO) +* Fixed a snippet list stored in Code Snippets Cloud being emptied when the site was temporarily unable to read its own + snippets. (PRO) +* Fixed REST API server error responses on missing snippets. (PRO) +* Fixed redundant frontend logic, improving overall performance. (PRO) +* Fixed Community Cloud search results and pagination to respect WordPress screen options. (PRO) +* Fixed snippet saving and activation feedback to improve validation and runtime error display. (PRO) +* Fixed downloaded Community Cloud snippets appearing as not downloaded after a page reload. (PRO) +* Fixed network snippet lookups using the wrong database table on multisite. (PRO) +* Fixed the inactive snippets count including trashed snippets. (PRO) +* Fixed featured Community Cloud snippets failing to load with some cloud API responses. (PRO) +* Fixed bulk actions in Community Cloud running against an empty selection, so selected snippets were never downloaded. + (PRO) +* Fixed a fatal error in the Elementor editor on Elementor versions older than 3.19. +* Fixed snippet descriptions staying truncated after the "Truncate long snippet names and descriptions" screen option + was switched off. +* Fixed saving, activating or deactivating a snippet reporting its previous state, which could leave a deactivated + snippet running under file-based execution. +* Fixed the snippet editor reporting a save as failed and undelivered when no response was received, which it cannot + determine. +* Fixed translations being loaded too early, with a notice from WordPress, when posts were queried while plugins were + still loading. + ## [3.10.2] (2026-09-01) ### Added -* Added a confirmation flow for run-once snippet execution and hardened the handler to prevent failed or confusing actions. +* Added a confirmation flow for run-once snippet execution and hardened the handler to prevent failed or confusing + actions. ### Changed * Snippet names now respect the row truncation Screen Option in the admin list for better readability. * Version switching AJAX requests now validate the correct nonce, improving reliability when updating snippet versions. ### Fixed -* Fixed safe mode fatal errors caused by an undefined wp_get_current_user() call. +* Fixed safe mode fatal errors caused by an undefined wp_get_current_user () call. * Fixed PHP validation being triggered incorrectly when activating snippets in bulk. * Fixed saving issues after a user session expires. * Fixed warnings caused by aliased field names when reading modified snippet fields. @@ -25,7 +104,8 @@ ### Fixed * Fixed a fatal error affecting snippets that use a `namespace` or `declare` statement. * Fixed the snippets page rendering blank when another plugin's screen settings filter returned an invalid value. -* Fixed snippet saving on hosts that block REST API `PUT` and `PATCH` requests, by sending writes as `POST` with a method override. +* Fixed snippet saving on hosts that block REST API `PUT` and `PATCH` requests, by sending writes as `POST` with a + method override. * Fixed the Snippets List Order setting not being applied to the snippets list. * Fixed admin bar snippet scripts failing to load on the free version, including on the site front end. * Fixed snippet modified dates being sent without the correct UTC offset. @@ -36,26 +116,35 @@ ## [3.10.0] (2026-08-24) ### Added -* New admin interface for managing snippets, with a cleaner layout, faster interactions, and a more consistent experience across plugin screens. +* New admin interface for managing snippets, with a cleaner layout, faster interactions, and a more consistent + experience across plugin screens. * Card view for browsing snippets, with a view switcher on the snippets table and Community Cloud. * Snippet preview modal for viewing snippet code from the snippets table without opening the editor. * Automatic hiding of unrelated admin notices from other plugins on Code Snippets screens. -* Admin bar snippet drawer, based on the Deckerweb Snippets workflow, for quick access to snippets and Safe Mode from the WordPress admin bar. -* Snippet locking to help prevent accidental edits or deletion of important snippets. Props to https://github.com/mgiannopoulos24. -* Improved screen options on the main snippets table, including controls for visible columns and truncating long snippet names or descriptions. +* Admin bar snippet drawer, based on the Deckerweb Snippets workflow, for quick access to snippets and Safe Mode from + the WordPress admin bar. +* Snippet locking to help prevent accidental edits or deletion of important snippets. Props + to https://github.com/mgiannopoulos24. +* Improved screen options on the main snippets table, including controls for visible columns and truncating long snippet + names or descriptions. * Bulk actions and bulk code download support in the redesigned snippets table. * Featured snippets and improved browsing in Community Cloud. * WordPress modern theme admin styling compatibility. -* Clearer accessibility labels, headings, tab markup, table checkboxes, sort buttons, copy buttons, and drag-and-drop upload controls. +* Clearer accessibility labels, headings, tab markup, table checkboxes, sort buttons, copy buttons, and drag-and-drop + upload controls. ### Changed -* Redesigned the main snippets table with improved search, filtering, sorting, pagination, row actions, and bulk selection. -* Improved the snippet import and migration experience, including clearer file upload handling and third-party plugin migration flows. +* Redesigned the main snippets table with improved search, filtering, sorting, pagination, row actions, and bulk + selection. +* Improved the snippet import and migration experience, including clearer file upload handling and third-party plugin + migration flows. * Improved snippet error handling so activation failures, validation errors, and stack traces are easier to understand. -* Improved Community Cloud search and filtering, including server-side filters, better result loading, and clearer empty states. +* Improved Community Cloud search and filtering, including server-side filters, better result loading, and clearer empty + states. * Updated the welcome screen, toolbar, import screen, and cloud screens to match the new admin experience. * Updated internal plugin architecture to a cleaner PSR-4 structure for better long-term maintainability. -* Improved accessibility across the snippets table, import screen, migration flow, Community Cloud, welcome screen, toolbar, dialogs, tooltips, and code editor. +* Improved accessibility across the snippets table, import screen, migration flow, Community Cloud, welcome screen, + toolbar, dialogs, tooltips, and code editor. * Improved colour contrast and reduced-motion support across admin screens. ### Fixed @@ -83,7 +172,8 @@ ### Added * New import functionality to migrate snippets from file uploads with drag-and-drop interface. -* Support for importing snippets from other popular plugins (Header Footer Code Manager, Insert Headers and Footers, Insert PHP Code Snippet). +* Support for importing snippets from other popular plugins (Header Footer Code Manager, Insert Headers and Footers, + Insert PHP Code Snippet). * Enhanced file based execution support with improved multisite mode compatibility. ### Fixed @@ -94,7 +184,8 @@ ## [3.9.3] (2025-12-03) ### Added -* End-to-end tests to verify the toggle visual state in the snippets list page, improving UI verification and test reliability. +* End-to-end tests to verify the toggle visual state in the snippets list page, improving UI verification and test + reliability. ### Fixed * Restored missing styles styling and direction-aware layout from Manage menu. @@ -131,7 +222,8 @@ * Expanded Multisite Sharing settings for clearer control over network-wide snippet sharing ### Changed -* Modernized browser support targets and polished admin UI (clearer row-action badges, improved Pro badge hover, refined active snippet name styling) +* Modernized browser support targets and polished admin UI (clearer row-action badges, improved Pro badge hover, refined + active snippet name styling) ### Fixed * Fixed REST API pagination to return correct results and page counts @@ -243,14 +335,16 @@ * Improved UX of snippet activation toggle. ### Fixed -* Fetching active snippets on a multisite network now respects the 'priority' field above all else when ordering snippets. +* Fetching active snippets on a multisite network now respects the 'priority' field above all else when ordering + snippets. * Cloud search appears correctly and allows downloading snippets in the free version of Code Snippets. * Improved performance of loading admin menu icon. ## [3.6.9] (2025-02-17) ### Changed -* Updated `Cloud_API::get_bundles()` to properly check bundle data and return an empty array if no valid bundles are present. +* Updated `Cloud_API::get_bundles()` to properly check bundle data and return an empty array if no valid bundles are + present. * Refactored `Cloud_List_Table::fetch_snippets()` to always return a valid `Cloud_Snippets` instance. * Cleaned up bundle iteration code and improved translation handling in the bundles view. @@ -266,7 +360,8 @@ * Updated Freemius SDK to the latest version. (PRO) ### Removed -* Functionality allowing `[code_snippet]` shortcodes to be embedded recursively – it will be re-added in a future version. +* Functionality allowing `[code_snippet]` shortcodes to be embedded recursively – it will be re-added in a future + version. ### Fixed * Shortcodes embedded within `[code_snippet]` shortcodes not evaluating correctly. @@ -279,15 +374,19 @@ ### Added * Generated snippet shortcode tags will include the snippet name, for easier identification. -* Admin notices will dismiss automatically after five seconds. ([#208](https://github.com/codesnippetspro/code-snippets/issues/208)) +* Admin notices will dismiss automatically after five seconds. + ([#208](https://github.com/codesnippetspro/code-snippets/issues/208)) ### Changed * Updated CSS to use latest Sass features. -* Moved theme selector to just above editor preview on settings page (thanks to [brandonjp]). ([#206](https://github.com/codesnippetspro/code-snippets/issues/206)) -* `[code_snippet]` shortcodes can now be nested within each other. ([#198](https://github.com/codesnippetspro/code-snippets/issues/198)) +* Moved theme selector to just above editor preview on settings page (thanks to [brandonjp]). + ([#206](https://github.com/codesnippetspro/code-snippets/issues/206)) +* `[code_snippet]` shortcodes can now be nested within each other. + ([#198](https://github.com/codesnippetspro/code-snippets/issues/198)) ### Fixed -* Save buttons above editor did not follow usual validation process in Pro. (PRO) ([#197](https://github.com/codesnippetspro/code-snippets/issues/197)) +* Save buttons above editor did not follow usual validation process in Pro. (PRO) + ([#197](https://github.com/codesnippetspro/code-snippets/issues/197)) * Minor inconsistencies in consistent UI elements between Core and Pro. * Tags input not allowing input. ([#211](https://github.com/codesnippetspro/code-snippets/issues/211)) * Issue with Elementor source code widget. (PRO) ([#205](https://github.com/codesnippetspro/code-snippets/issues/205)) @@ -413,13 +512,16 @@ * Scroll new notices into view on edit menu. ### Fixed -* Error when attempting to update network shared snippets after saving. [[#](https://wordpress.org/support/topic/activating-snippets-breaks-on-wordpress-6-3/)] +* Error when attempting to update network shared snippets after + saving. [[#](https://wordpress.org/support/topic/activating-snippets-breaks-on-wordpress-6-3/)] ## [3.4.2] (2023-07-05) ### Fixed -* Issue causing export process to fail with fatal error. [[#](https://wordpress.org/support/topic/critical-error-on-exporting-snippets/)] -* Type issue on `the_posts` filter when no posts available. [[#](https://wordpress.org/support/topic/collision-with-plugin-xml-sitemap-google-news/)] +* Issue causing export process to fail with fatal + error. [[#](https://wordpress.org/support/topic/critical-error-on-exporting-snippets/)] +* Type issue on `the_posts` filter when no posts + available. [[#](https://wordpress.org/support/topic/collision-with-plugin-xml-sitemap-google-news/)] ## [3.4.1] (2023-06-29) @@ -427,14 +529,18 @@ * Added better debugging when calling REST API methods from the edit menu. ### Changed -* Escape special characters when sending snippet code through AJAX to avoid false-positives from security modules. [[#](https://wordpress.org/support/topic/latest-3-4-0-ajax-bug-cannot-save-snippets-403-error/)] +* Escape special characters when sending snippet code through AJAX to avoid false-positives from security + modules. [[#](https://wordpress.org/support/topic/latest-3-4-0-ajax-bug-cannot-save-snippets-403-error/)] * Only display the latest update or error notice on the edit page, instead of allowing them to stack. ### Fixed -* Undefined array key error. [[#](https://wordpress.org/support/topic/after-updating-occasionally-getting-undefined-array-key-query/)] -* Potential type issue when loading Prism. [[#](https://wordpress.org/support/topic/code-snippets-fatal-error-breaking-xml-sitemaps/)] +* Undefined array key + error. [[#](https://wordpress.org/support/topic/after-updating-occasionally-getting-undefined-array-key-query/)] +* Potential type issue when loading + Prism. [[#](https://wordpress.org/support/topic/code-snippets-fatal-error-breaking-xml-sitemaps/)] * Potential type issue when sorting snippets. [[#](https://github.com/codesnippetspro/code-snippets/issues/166)] -* Issue preventing asset revision numbers from updating correctly. (PRO) [[#](https://github.com/codesnippetspro/code-snippets/issues/166)] +* Issue preventing asset revision numbers from updating correctly. + (PRO) [[#](https://github.com/codesnippetspro/code-snippets/issues/166)] ## [3.4.0] (2023-05-17) @@ -445,10 +551,10 @@ ### Changed * Better compatibility with modern versions of PHP (7.0+). * Converted Edit/Add New Snippet page to use React: - - Converted action buttons to asynchronously use REST API endpoints through AJAX. - - Load page components dynamically through React. - - Added action notice queue system - - Replaced native alert dialog with proper React modal. + - Converted action buttons to asynchronously use REST API endpoints through AJAX. + - Load page components dynamically through React. + - Added action notice queue system + - Replaced native alert dialog with proper React modal. * Catch snippet execution errors to prevent site from crashing. * Display recent snippet errors in admin dashboard instead. * Updated editor block to use new REST API endpoints. (PRO) @@ -468,7 +574,8 @@ ### Added * Added additional editor shortcuts to list in tooltip. -* Filter for changing Snippets admin menu position. [See this help article for more information.](https://codesnippets.pro/doc/snippets-menu-location/) +* Filter for changing Snippets admin menu + position. [See this help article for more information.](https://codesnippets.pro/doc/snippets-menu-location/) * Ability to filter shortcode output. Thanks to contributions from [Jack Szwergold](https://github.com/JackSzwergold). ### Fixed @@ -485,10 +592,12 @@ ### Added * `Ctrl`+`/` or `Cmd`+`/` as shortcut for commenting out code in the snippet editor. -* Additional hooks to various snippet actions, thanks to contributions made by [ancient-spirit](https://github.com/ancient-spirit). +* Additional hooks to various snippet actions, thanks to contributions made + by [ancient-spirit](https://github.com/ancient-spirit). * Fold markers, additional keyboard shortcuts and keymap options to snippet editor, thanks to contributions made by [Amaral Krichman](https://github.com/karmaral). -* WP-CLI commands for retrieving, activating, deactivating, deleting, creating, updating, exporting and importing snippets. +* WP-CLI commands for retrieving, activating, deactivating, deleting, creating, updating, exporting and importing + snippets. ### Changed * Removed duplicate tables exist query. ([#](https://wordpress.org/support/topic/duplicate-queries-21)). @@ -507,7 +616,8 @@ * Support for multiple code styles in the source code Gutenberg editor block. (PRO) * Admin notice announcing release of Code Snippets Pro. * Button for copying shortcode text to clipboard. -* Option to choose from 44 different themes for the Prism code highlighter in the source editor block and Elementor widget. (PRO) +* Option to choose from 44 different themes for the Prism code highlighter in the source editor block and Elementor + widget. (PRO) ### Changed * Include Code Snippets CSS and JS source code in distributed package. @@ -626,15 +736,16 @@ ## [2.14.3] (2021-12-10) ### Fixed -* Potential security issue outputting snippets-safe-mode query variable value as-is. Thanks to Krzysztof Zając for reporting. +* Potential security issue outputting snippets-safe-mode query variable value as-is. Thanks to Krzysztof Zając for + reporting. ## [2.14.2] (2021-09-09) ### Added * Added translations: - - Spanish by [Ibidem Group](https://www.ibidemgroup.com) - - Urdu by [Samuel Badree](https://mobilemall.pk/) - - Greek by [Toni Bishop from Jrop](https://www.jrop.com/) + - Spanish by [Ibidem Group](https://www.ibidemgroup.com) + - Urdu by [Samuel Badree](https://mobilemall.pk/) + - Greek by [Toni Bishop from Jrop](https://www.jrop.com/) * Support for `:class` syntax to the code validator. * PHP8 support to the code linter. * Color picker feature to the code editor. @@ -657,7 +768,8 @@ * Code validator now supports `function_exists` and `class_exists` checks. * Code validator now supports anonymous functions. * Issue with saving the hidden columns setting. -* Replaced the outdated tag-it library with [tagger](https://github.com/jcubic/tagger) for powering the snippet tags editor. +* Replaced the outdated tag-it library with [tagger](https://github.com/jcubic/tagger) for powering the snippet tags + editor. ## [2.14.0] (2020-01-26) @@ -678,7 +790,8 @@ * Fixed a bug preventing the editor theme from being set to default. * Ensure that imported snippets are always inactive. * Check the referer on the import menu to prevent CSRF attacks. - Thanks to [Chloe with the Wordfence Threat Intelligence team](https://www.wordfence.com/blog/author/wfchloe/) for reporting. + Thanks to [Chloe with the Wordfence Threat Intelligence team](https://www.wordfence.com/blog/author/wfchloe/) for + reporting. * Ensure that individual snippet action links use proper verification. ## [2.13.3] (2019-03-13) @@ -725,7 +838,8 @@ ## [2.13.0] (2018-12-17) ### Added -* Search/replace functionality to the snippet editor. [See here for a list of keyboard shortcuts.](https://codemirror.net/demo/search.html) [[#](https://wordpress.org/support/topic/feature-request-codemirror-search-and-replace/)] +* Search/replace functionality to the snippet + editor. [See here for a list of keyboard shortcuts.](https://codemirror.net/demo/search.html) [[#](https://wordpress.org/support/topic/feature-request-codemirror-search-and-replace/)] * Option to make admin menu more compact. * Added additional styles to editor settings preview. * PHP linter to code editor. @@ -745,7 +859,8 @@ * CodeMirror updated to version 5.41.0. * Attempt to create database columns that might be missing after a table upgrade. * Streamlined upgrade process. -* Made search box appear at top of page on mobile. [[#](https://wordpress.org/support/topic/small-modification-for-mobile-ux/)] +* Made search box appear at top of page on + mobile. [[#](https://wordpress.org/support/topic/small-modification-for-mobile-ux/)] * Updated screenshots. ### Fixed @@ -802,7 +917,7 @@ ### Fixed * Prevent errors when trying to export no snippets. -* Use wp_json_encode() to encode export data. +* Use wp_json_encode () to encode export data. * Check both the file extension and MIME type of uploaded import files. ## [2.10.0] (2018-01-18) @@ -874,7 +989,7 @@ ### Changed * Moved code to disable snippet execution into a filter hook. -* execute_active_snippets() function updated with improved efficiency. +* execute_active_snippets () function updated with improved efficiency. * Renamed Snippet class to avoid name collisions with other plugins. * Don't hide output when executing a snippet. @@ -892,8 +1007,10 @@ ## [2.8.6] (2017-05-14) ### Fixed -* Fixed snippet description field alias not mapping correctly, causing snippet descriptions to not be displayed in the table or when editing a snippet. -* Ensured that get_snippets() function retrieves snippets with the correct 'network' setting. Fixes snippet edit links in network admin. +* Fixed snippet description field alias not mapping correctly, causing snippet descriptions to not be displayed in the + table or when editing a snippet. +* Ensured that get_snippets () function retrieves snippets with the correct 'network' setting. Fixes snippet edit links + in network admin. ## [2.8.5] (2017-05-13) @@ -937,7 +1054,8 @@ ### Fixed * Fixed admin menu items not translating. * Corrected editor alignment on RTL sites. ([#](https://wordpress.org/support/topic/suggestion-css-fix-for-rtl-sites/)) -* Fixed bulk actions running when Filter button is clicked. ([#](https://wordpress.org/support/topic/bug-with-filtering-action-buttons/)) +* Fixed bulk actions running when Filter button is clicked. + ([#](https://wordpress.org/support/topic/bug-with-filtering-action-buttons/)) ## [2.8.0] (2016-12-14) @@ -976,7 +1094,8 @@ * Updated CodeMirror to version 5.19.0. ### Security -* Ensured that the editor theme setting is properly validated. Thanks to [Netsparker](https://www.netsparker.com) for reporting. +* Ensured that the editor theme setting is properly validated. Thanks to [Netsparker](https://www.netsparker.com) for + reporting. * Ensured that snippet tags are properly escaped. Thanks to [Netsparker](https://www.netsparker.com) for reporting. ## [2.7.0] (2016-07-23) @@ -992,9 +1111,11 @@ ### Fixed * Fixed plugin translations being loaded. * Fixed description field not being imported. -* Fixed issue with CodeMirror rubyblue theme. [[#](https://wordpress.org/support/topic/a-problem-with-the-cursor-color-and-the-fix-that-worked-for-me)] +* Fixed issue with CodeMirror rubyblue + theme. [[#](https://wordpress.org/support/topic/a-problem-with-the-cursor-color-and-the-fix-that-worked-for-me)] * Fixed snippet fields not importing. -* Fixed a minor XSS vulnerability discovered by Burak Kelebek. [[#](https://wordpress.org/support/topic/security-vulnerability-20)] +* Fixed a minor XSS vulnerability discovered by Burak + Kelebek. [[#](https://wordpress.org/support/topic/security-vulnerability-20)] ## [2.6.1] (2016-02-10) @@ -1056,7 +1177,8 @@ ## [2.4.1] (2015-09-17) ### Fixed -* Fixed CodeMirror themes not being detected on settings page [[#](https://wordpress.org/support/topic/updated-to-240-now-i-cant-switch-theme)] +* Fixed CodeMirror themes not being detected on settings + page [[#](https://wordpress.org/support/topic/updated-to-240-now-i-cant-switch-theme)] ## [2.4.0] (2015-09-17) @@ -1082,7 +1204,8 @@ ### Added * Added icons for admin and front-end snippets to manage table. -* Added filter switch to prevent a snippet from executing. ([#25](https://github.com/codesnippetspro/code-snippets/issues/25)) +* Added filter switch to prevent a snippet from executing. + ([#25](https://github.com/codesnippetspro/code-snippets/issues/25)) ### Changed * Improved settings retrieval by caching settings. @@ -1118,7 +1241,7 @@ ### Fixed * Resolved JavaScript error on edit snippet pages. -* Added polyfill for array_replace_recursive() function for PHP 5.2. +* Added polyfill for array_replace_recursive () function for PHP 5.2. ## [2.2.1] (2015-05-10) @@ -1191,7 +1314,8 @@ * Added Russian translation by Alexander Samsonov. * Added Slovak translation by [Ján Fajčák] from [WordPress Slovakia](https://wp.sk). * Added setting to always save and activate snippets by default. -* Added braces to single-line conditionals in line with [new coding standards](https://make.wordpress.org/core/2013/11/13/proposed-coding-standards-change-always-require-braces/). +* Added braces to single-line conditionals in line + with [new coding standards](https://make.wordpress.org/core/2013/11/13/proposed-coding-standards-change-always-require-braces/). ### Changed * Improved plugin file structure. @@ -1233,7 +1357,8 @@ ### Added * Added French translation thanks to translator [oWEB](http://office-web.net). -* Added 'Save & Deactivate' button to the edit snippet page. ([#](https://wordpress.org/support/topic/deactivate-button-in-edit-snippet-page)) +* Added 'Save & Deactivate' button to the edit snippet page. + ([#](https://wordpress.org/support/topic/deactivate-button-in-edit-snippet-page)) * Added nonce to edit snippet page. * Added a fallback MP6 icon. @@ -1243,8 +1368,10 @@ * Updated CodeMirror to version 3.19. * Updated WordPress.org plugin banner. * Add and remove network capabilities as super admins are added and removed. -* Replaced buggy trim `` functionality with a much more reliable regex method. ([#](https://wordpress.org/support/topic/character-gets-cut)) -* Make the title of each snippet on the manage page a clickable link to edit the snippet ([#](https://wordpress.org/support/topic/deactivate-button-in-edit-snippet-page?replies=9#post-4682757)) +* Replaced buggy trim `` functionality with a much more reliable regex method. + ([#](https://wordpress.org/support/topic/character-gets-cut)) +* Make the title of each snippet on the manage page a clickable link to edit the snippet + ([#](https://wordpress.org/support/topic/deactivate-button-in-edit-snippet-page?replies=9#post-4682757)) * Hide row actions on manage snippet page by default. * Use the proper WordPress database APIs consistently. * Rewritten export functionality. @@ -1255,7 +1382,9 @@ * Removed CodeMirror bundled with plugin. ### Fixed -* Fixed snippet failing to save when code contains `%` character, props to [nikan06](https://wordpress.org/support/profile/nikan06). ([#](https://wordpress.org/support/topic/percent-sign-bug)) +* Fixed snippet failing to save when code contains `%` character, props + to [nikan06](https://wordpress.org/support/profile/nikan06). + ([#](https://wordpress.org/support/topic/percent-sign-bug)) * Fixed HTML breaking in export files. ([#](https://wordpress.org/support/topic/import-problem-7)) * Fixed incorrect export filename. * Fixed CodeMirror incompatibility with the WP Editor plugin. @@ -1281,7 +1410,9 @@ * Added error message handling for import snippets page. ### Changed -* Improved database table creation method: on a single-site install, the snippets table will always be created. On a multisite install, the network snippets table will always be created; the site-specific table will always be created for the main site; for sub-sites the snippets table will only be created on a visit to a snippets admin page. +* Improved database table creation method: on a single-site install, the snippets table will always be created. On a + multisite install, the network snippets table will always be created; the site-specific table will always be created + for the main site; for sub-sites the snippets table will only be created on a visit to a snippets admin page. * Updated to CodeMirror 3.14. * Allow no snippet name or code to be set. * Prevented an error on fresh multisite installations. @@ -1307,7 +1438,8 @@ ### Added * Added German translation thanks to [David Decker](https://deckerweb.de) -* Allow or deny site administrators access to snippet admin menus. Set your preference in the **Enable Administration Menus** setting under the *Settings > Network Settings* network admin menu. +* Allow or deny site administrators access to snippet admin menus. Set your preference in the **Enable Administration + Menus** setting under the *Settings > Network Settings* network admin menu. ### Changed * Updated PHP Documentation completely. [[View online](https://bungeshea.github.io/code-snippets/api)] @@ -1322,7 +1454,8 @@ ### Added * Added icon for the new MP6 admin UI ([#](https://wordpress.org/support/topic/icon-disappears-with-mp6)) -* Allow plugin to be activated on individual sites on multisite ([#](https://wordpress.org/support/topic/dont-work-at-multisite)) +* Allow plugin to be activated on individual sites on multisite + ([#](https://wordpress.org/support/topic/dont-work-at-multisite)) * Strip PHP tags from the beginning and end of a snippet on save ([#](https://wordpress.org/support/topic/php-tags)) * Change label in admin menu when editing a snippet. @@ -1338,12 +1471,14 @@ * Removed HTML, CSS and JavaScript CodeMirror modes that were messing things up. ### Fixed -* Fixed a bug with saving snippets per page option ([#](https://wordpress.org/support/topic/plugin-code-snippets-snippets-per-page-does-not-work#post-3710991)) +* Fixed a bug with saving snippets per page option + ([#](https://wordpress.org/support/topic/plugin-code-snippets-snippets-per-page-does-not-work#post-3710991)) ## [1.6.1] (2012-12-29) ### Fixed -* Fixed a bug with permissions not being applied on install ([#](https://wordpress.org/support/topic/permissions-problem-after-install)) +* Fixed a bug with permissions not being applied on install + ([#](https://wordpress.org/support/topic/permissions-problem-after-install)) * Fixed a bug in the uninstall method ([#](https://wordpress.org/support/topic/bug-in-delete-script)) ## [1.6.0] (2012-12-22) @@ -1370,19 +1505,22 @@ ### Added * Added custom capabilities. -* Added 'Export to PHP' feature. ([#](https://wordpress.org/support/topic/plugin-code-snippets-suggestion-bulk-export-to-php)) +* Added 'Export to PHP' feature. + ([#](https://wordpress.org/support/topic/plugin-code-snippets-suggestion-bulk-export-to-php)) * Added i18n. ### Changed * Updated CodeMirror to version 2.33. * Updated the 'Manage Snippets' page to use the WP_List_Table class: - - Added 'Screen Options' tab to 'Manage Snippets' page. - - Added search capability to 'Manage Snippets' page. - - Added views to easily filter activated, deactivated and recently activated snippets. - - Added ID column to 'Manage Snippets' page. - - Added sortable name and ID column on 'Manage Snippets' page ([#](https://wordpress.org/support/topic/plugin-code-snippets-suggestion-sort-by-snippet-name)) + - Added 'Screen Options' tab to 'Manage Snippets' page. + - Added search capability to 'Manage Snippets' page. + - Added views to easily filter activated, deactivated and recently activated snippets. + - Added ID column to 'Manage Snippets' page. + - Added sortable name and ID column on 'Manage Snippets' page + ([#](https://wordpress.org/support/topic/plugin-code-snippets-suggestion-sort-by-snippet-name)) * Improved API. -* Lengthened snippet name field to 64 characters. ([#](https://wordpress.org/support/topic/plugin-code-snippets-snippet-title-limited-to-36-characters)) +* Lengthened snippet name field to 64 characters. + ([#](https://wordpress.org/support/topic/plugin-code-snippets-snippet-title-limited-to-36-characters)) ## [1.4.0] (2012-08-20) @@ -1426,9 +1564,11 @@ ## [1.1.0] (2012-06-24) ### Fixed -* Fixed a permissions bug with `DISALLOW_FILE_EDIT` being set to true. ([#](https://wordpress.org/support/topic/plugin-code-snippets-cant-add-new)) +* Fixed a permissions bug with `DISALLOW_FILE_EDIT` being set to true. + ([#](https://wordpress.org/support/topic/plugin-code-snippets-cant-add-new)) * Fixed a bug with the page title reading 'Add New Snippet' on the 'Edit Snippets' page. -* Fixed a bug not allowing the plugin to be Network Activated. ([#](https://wordpress.org/support/topic/plugin-code-snippets-network-activate-does-not-create-snippets-tables)) +* Fixed a bug not allowing the plugin to be Network Activated. + ([#](https://wordpress.org/support/topic/plugin-code-snippets-network-activate-does-not-create-snippets-tables)) ## [1.0.0] (2012-06-13) @@ -1438,22 +1578,6 @@ [brandonjp]: https://github.com/brandonjp [unreleased]: https://github.com/codesnippetspro/code-snippets/tree/core -[3.10.0]: https://github.com/codesnippetspro/code-snippets/releases/tag/v3.10.0 -[3.9.6]: https://github.com/codesnippetspro/code-snippets/releases/tag/v3.9.6 -[3.9.5]: https://github.com/codesnippetspro/code-snippets/releases/tag/v3.9.5 -[3.9.4]: https://github.com/codesnippetspro/code-snippets/releases/tag/v3.9.4 -[3.9.3]: https://github.com/codesnippetspro/code-snippets/releases/tag/v3.9.3 -[3.9.2]: https://github.com/codesnippetspro/code-snippets/releases/tag/v3.9.2 -[3.9.1]: https://github.com/codesnippetspro/code-snippets/releases/tag/v3.9.1 -[3.9.0]: https://github.com/codesnippetspro/code-snippets/releases/tag/v3.9.0 -[3.9.0-beta.2]: https://github.com/codesnippetspro/code-snippets/releases/tag/v3.9.0-beta.2 -[3.9.0-beta.1]: https://github.com/codesnippetspro/code-snippets/releases/tag/v3.9.0-beta.1 -[3.8.2]: https://github.com/codesnippetspro/code-snippets/releases/tag/v3.8.2 -[3.8.1]: https://github.com/codesnippetspro/code-snippets/releases/tag/v3.8.1 -[3.8.0]: https://github.com/codesnippetspro/code-snippets/releases/tag/v3.8.0 -[3.7.1-beta.3]: https://github.com/codesnippetspro/code-snippets/releases/tag/v3.7.1-beta.3 -[3.7.1-beta.2]: https://github.com/codesnippetspro/code-snippets/releases/tag/v3.7.1-beta.2 -[3.7.1-beta.1]: https://github.com/codesnippetspro/code-snippets/releases/tag/v3.7.1-beta.1 [3.7.0]: https://github.com/codesnippetspro/code-snippets/releases/tag/v3.7.0 [3.6.7]: https://github.com/codesnippetspro/code-snippets/releases/tag/v3.6.7 [3.6.6.1]: https://github.com/codesnippetspro/code-snippets/releases/tag/v3.6.6.1 diff --git a/config/playwright/playwright.config.ts b/config/playwright/playwright.config.ts index 68b6ac87d..c0fac8c8e 100644 --- a/config/playwright/playwright.config.ts +++ b/config/playwright/playwright.config.ts @@ -12,8 +12,17 @@ const ASSERT_TIMEOUT_SECONDS = 30 const MILLISECONDS_IN_SECOND = 1000 +// Overridable so the suite can be pointed at a second environment, which wp-env gives a +// different port when one is already running. An empty value is treated as unset: Playwright +// resolves relative paths against baseURL, and an empty base cannot be resolved against. +const DEFAULT_BASE_URL = 'http://localhost:8888' +const configuredBaseUrl = process.env.WP_E2E_BASE_URL?.trim() ?? '' +const baseURL = '' === configuredBaseUrl ? DEFAULT_BASE_URL : configuredBaseUrl + const baseTestsDir = join(__dirname, '..', '..', 'tests') const storageState = join(baseTestsDir, 'e2e/.auth/user.json') +const rtlSpecs = /rtl-layout\.spec\.ts/ +const rtlStorageState = join(baseTestsDir, 'e2e/.auth/rtl-user.json') /** * @see https://playwright.dev/docs/test-configuration @@ -38,7 +47,7 @@ export default defineConfig({ ['junit', { outputFile: join(process.cwd(), 'test-results', 'results.xml') }] ], use: { - baseURL: 'http://localhost:8888', + baseURL, trace: 'retain-on-failure', screenshot: 'only-on-failure', video: 'retain-on-failure' @@ -66,7 +75,7 @@ export default defineConfig({ storageState }, dependencies: ['setup'], - testIgnore: /.*\.setup\.ts/ + testIgnore: [/.*\.setup\.ts/, /.*\.teardown\.ts/, rtlSpecs] }, { @@ -76,7 +85,31 @@ export default defineConfig({ storageState }, dependencies: ['setup', 'flat-files-setup'], - testIgnore: /.*\.setup\.ts/ + testIgnore: [/.*\.setup\.ts/, /.*\.teardown\.ts/, rtlSpecs] + }, + + // The RTL specs sign in as a user of their own on a right-to-left locale + // and keep that session apart, so the other projects never see the site + // mirrored whatever order Playwright runs them in. + { + name: 'rtl-setup', + testMatch: /rtl\.setup\.ts/, + use: { ...devices['Desktop Chrome'] }, + dependencies: ['setup'] + }, + { + name: 'rtl-teardown', + testMatch: /rtl\.teardown\.ts/ + }, + { + name: 'chromium-rtl', + testMatch: rtlSpecs, + use: { + ...devices['Desktop Chrome'], + storageState: rtlStorageState + }, + dependencies: ['rtl-setup'], + teardown: 'rtl-teardown' } ], diff --git a/config/webpack/webpack-js.ts b/config/webpack/webpack-js.ts index 5c88861e8..164d01dd0 100644 --- a/config/webpack/webpack-js.ts +++ b/config/webpack/webpack-js.ts @@ -24,14 +24,17 @@ const babelConfig = { export const jsWebpackConfig: Configuration = { entry: { - 'admin-bar': `${SOURCE_DIR}/admin-bar.ts`, 'edit': { import: `${SOURCE_DIR}/edit.ts`, dependOn: 'editor' }, 'editor': `${SOURCE_DIR}/editor.ts`, + 'feedback': `${SOURCE_DIR}/feedback.ts`, + 'feedback-capture': `${SOURCE_DIR}/feedback-capture.ts`, 'import': `${SOURCE_DIR}/import.ts`, + 'stats': `${SOURCE_DIR}/stats.ts`, 'manage': `${SOURCE_DIR}/manage.ts`, 'mce': `${SOURCE_DIR}/mce.ts`, 'prism': `${SOURCE_DIR}/prism.ts`, 'settings': { import: `${SOURCE_DIR}/settings.ts`, dependOn: 'editor' }, + 'admin-bar': `${SOURCE_DIR}/admin-bar.ts`, 'welcome': `${SOURCE_DIR}/welcome.ts` }, output: { diff --git a/package-lock.json b/package-lock.json index e903c1676..29dbebaca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "code-snippets", - "version": "3.10.2", + "version": "4.0.0-beta.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "code-snippets", - "version": "3.10.2", + "version": "4.0.0-beta.3", "license": "GPL-2.0-or-later", "dependencies": { "@codemirror/fold": "^0.19.4", @@ -23,7 +23,8 @@ "prismjs": "^1.29.0", "react": "^18.3.1", "react-dom": "^18.3.1", - "react-select": "^5.10.0" + "react-select": "^5.10.0", + "uuid": "^11.1.0" }, "devDependencies": { "@axe-core/playwright": "^4.11.2", @@ -99,15 +100,23 @@ "node": ">=6.0.0" } }, - "node_modules/@ariakit/core": { - "version": "0.4.14", - "license": "MIT" + "node_modules/@ariakit/components": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/@ariakit/components/-/components-0.1.9.tgz", + "integrity": "sha512-Rmj5gcdfNQ4r4z5FzHHeC9OFRu9HSCVXDCDz8ak/vNHBrGmjeZ6Q3LOup61Eh+/GsT6cc2TDIQ6i8X1aB6y0BA==", + "license": "MIT", + "dependencies": { + "@ariakit/store": "0.1.7", + "@ariakit/utils": "0.1.5" + } }, "node_modules/@ariakit/react": { - "version": "0.4.15", + "version": "0.4.36", + "resolved": "https://registry.npmjs.org/@ariakit/react/-/react-0.4.36.tgz", + "integrity": "sha512-QKxSc6KvTHObB9khmaQlr6XOvjKyZSHr4JWnZTDwRFDgUAzz79a+p2vApCHFlqadqV1QeGezap5cxHrOysoNOg==", "license": "MIT", "dependencies": { - "@ariakit/react-core": "0.4.15" + "@ariakit/react-components": "0.4.0" }, "funding": { "type": "opencollective", @@ -118,19 +127,67 @@ "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" } }, - "node_modules/@ariakit/react-core": { - "version": "0.4.15", + "node_modules/@ariakit/react-components": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@ariakit/react-components/-/react-components-0.4.0.tgz", + "integrity": "sha512-dYymiYvAbyu6a/ehqYN2ZeieiMSYt0CMAj2BnDRUI/dZ9vLF32cv9PQMcgXXyqn0ILpqu79PQs53vVEZ+Z+0rQ==", "license": "MIT", "dependencies": { - "@ariakit/core": "0.4.14", - "@floating-ui/dom": "^1.0.0", - "use-sync-external-store": "^1.2.0" + "@ariakit/components": "0.1.9", + "@ariakit/react-store": "0.1.8", + "@ariakit/react-utils": "0.2.3", + "@ariakit/store": "0.1.7", + "@ariakit/utils": "0.1.5", + "@floating-ui/dom": "^1.0.0" }, "peerDependencies": { "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/@ariakit/react-store": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/@ariakit/react-store/-/react-store-0.1.8.tgz", + "integrity": "sha512-VYZ1LTUVMrNUi4jP37Npvhe3mcAzKdznqnBSVeh1Jjsbcgw0JlN88oy6UpdoJPvLKWOZHVYr62Sqn7xE0GrsqQ==", + "license": "MIT", + "dependencies": { + "@ariakit/react-utils": "0.2.3", + "@ariakit/store": "0.1.7", + "@ariakit/utils": "0.1.5", + "use-sync-external-store": "^1.6.0" + }, + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@ariakit/react-utils": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@ariakit/react-utils/-/react-utils-0.2.3.tgz", + "integrity": "sha512-fDaheb/7QEusanZb2oRT7mO55GTpQUyBOdjvQF5RPh3/CM15lm0TejLKN5bl1obmU5HRuByvckQmQvSyr8n/dw==", + "license": "MIT", + "dependencies": { + "@ariakit/store": "0.1.7", + "@ariakit/utils": "0.1.5" + }, + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@ariakit/store": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/@ariakit/store/-/store-0.1.7.tgz", + "integrity": "sha512-/GcxscA9QTo2F+IFbFPvoyj1N8hzXBnaYsQt9UxRiJgCFPQ2jIe4i6QgPXdOZEZUuqYdyuvjcQrg7MDm9vpvCA==", + "license": "MIT", + "dependencies": { + "@ariakit/utils": "0.1.5" + } + }, + "node_modules/@ariakit/utils": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@ariakit/utils/-/utils-0.1.5.tgz", + "integrity": "sha512-BQebYH9nV1VZttZwoq/fsxcxIJjc8oW2bNV6yJDHLZ8OF8UtM15drFN0JOL8Jwn7jeeD7Ev+tIC8pUijJEditQ==", + "license": "MIT" + }, "node_modules/@axe-core/playwright": { "version": "4.11.3", "resolved": "https://registry.npmjs.org/@axe-core/playwright/-/playwright-4.11.3.tgz", @@ -1994,14 +2051,16 @@ } }, "node_modules/@emotion/css": { - "version": "11.11.2", + "version": "11.13.5", + "resolved": "https://registry.npmjs.org/@emotion/css/-/css-11.13.5.tgz", + "integrity": "sha512-wQdD0Xhkn3Qy2VNcIzbLP9MR8TafI0MJb7BEAXKp+w4+XqErksWR4OXomuDzPsN4InLdGhVe6EYcn2ZIUCpB8w==", "license": "MIT", "dependencies": { - "@emotion/babel-plugin": "^11.11.0", - "@emotion/cache": "^11.11.0", - "@emotion/serialize": "^1.1.2", - "@emotion/sheet": "^1.2.2", - "@emotion/utils": "^1.2.1" + "@emotion/babel-plugin": "^11.13.5", + "@emotion/cache": "^11.13.5", + "@emotion/serialize": "^1.3.3", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.2" } }, "node_modules/@emotion/hash": { @@ -2057,7 +2116,9 @@ "license": "MIT" }, "node_modules/@emotion/styled": { - "version": "11.14.0", + "version": "11.14.1", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.1.tgz", + "integrity": "sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.18.3", @@ -2263,25 +2324,31 @@ } }, "node_modules/@floating-ui/core": { - "version": "1.5.0", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.8.0.tgz", + "integrity": "sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==", "license": "MIT", "dependencies": { - "@floating-ui/utils": "^0.1.3" + "@floating-ui/utils": "^0.2.12" } }, "node_modules/@floating-ui/dom": { - "version": "1.5.3", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.8.0.tgz", + "integrity": "sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==", "license": "MIT", "dependencies": { - "@floating-ui/core": "^1.4.2", - "@floating-ui/utils": "^0.1.3" + "@floating-ui/core": "^1.8.0", + "@floating-ui/utils": "^0.2.12" } }, "node_modules/@floating-ui/react-dom": { - "version": "2.1.1", + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.9.tgz", + "integrity": "sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==", "license": "MIT", "dependencies": { - "@floating-ui/dom": "^1.0.0" + "@floating-ui/dom": "^1.8.0" }, "peerDependencies": { "react": ">=16.8.0", @@ -2289,7 +2356,9 @@ } }, "node_modules/@floating-ui/utils": { - "version": "0.1.6", + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.12.tgz", + "integrity": "sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==", "license": "MIT" }, "node_modules/@humanfs/core": { @@ -4146,13 +4215,13 @@ } }, "node_modules/@wordpress/a11y": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@wordpress/a11y/-/a11y-4.40.0.tgz", - "integrity": "sha512-WhBuBgJTvanbBMNeflgCvwQLOU9ToITdYSzOvWg0kzz1i/e138NlCxrVpcXGUc6MQulduKhOWOtjizSdotaQRA==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@wordpress/a11y/-/a11y-4.52.0.tgz", + "integrity": "sha512-0KlDa/vSASriu84+z+a/XA2teaau6t6rCH6PEqMXoW5a6EbP2e/REpZRumbu0VzV5MRO5kpxfD+fVYNYz9BdlA==", "license": "GPL-2.0-or-later", "dependencies": { - "@wordpress/dom-ready": "^4.40.0", - "@wordpress/i18n": "^6.13.0" + "@wordpress/dom-ready": "^4.52.0", + "@wordpress/i18n": "^6.25.0" }, "engines": { "node": ">=18.12.0", @@ -4160,15 +4229,14 @@ } }, "node_modules/@wordpress/a11y/node_modules/@wordpress/i18n": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/@wordpress/i18n/-/i18n-6.13.0.tgz", - "integrity": "sha512-Yx882uFxcg6QpB13fv8UhvM6k5NwMQGfNXKB9SVSNL/APvDWn2m/n4n+5GZYi+wOV+KJLojQZbdRpHWCnX/jFg==", + "version": "6.25.0", + "resolved": "https://registry.npmjs.org/@wordpress/i18n/-/i18n-6.25.0.tgz", + "integrity": "sha512-UcyUT8CJgEkhjzEGTVV6kw0Qi4OraF0I9J9FBLmTda/1Wi1o6rLAXZBm2aJrP740ezFxPneHH92aQF4Z5xZqcQ==", "license": "GPL-2.0-or-later", "dependencies": { "@tannin/sprintf": "^1.3.2", - "@wordpress/hooks": "^4.40.0", + "@wordpress/hooks": "^4.52.0", "gettext-parser": "^1.3.1", - "memize": "^2.1.0", "tannin": "^1.2.0" }, "bin": { @@ -4275,6 +4343,20 @@ "react-dom": "^18.0.0" } }, + "node_modules/@wordpress/components/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@wordpress/compose": { "version": "7.40.0", "resolved": "https://registry.npmjs.org/@wordpress/compose/-/compose-7.40.0.tgz", @@ -4303,19 +4385,19 @@ } }, "node_modules/@wordpress/data": { - "version": "10.40.0", - "resolved": "https://registry.npmjs.org/@wordpress/data/-/data-10.40.0.tgz", - "integrity": "sha512-wwqkMc9iLteRO1zNxL/R3COWnijsdC5TIjenmd2JivReUmdA4ulAN3Tq7QiHkhwOV4jzZkuWW7DgR2ynxf55lw==", + "version": "10.52.0", + "resolved": "https://registry.npmjs.org/@wordpress/data/-/data-10.52.0.tgz", + "integrity": "sha512-7Nx66TfUkYZDdXL4wn2/DjIJOYRsKdi8Gvv24Warv9rZ1pwTNoFXFDagvc3UoCOjn0pM8+pFNbd7a9TJH8oQzg==", "license": "GPL-2.0-or-later", "dependencies": { - "@wordpress/compose": "^7.40.0", - "@wordpress/deprecated": "^4.40.0", - "@wordpress/element": "^6.40.0", - "@wordpress/is-shallow-equal": "^5.40.0", - "@wordpress/priority-queue": "^3.40.0", - "@wordpress/private-apis": "^1.40.0", - "@wordpress/redux-routine": "^5.40.0", - "deepmerge": "^4.3.0", + "@wordpress/compose": "^8.5.0", + "@wordpress/deprecated": "^4.52.0", + "@wordpress/element": "^8.4.0", + "@wordpress/is-shallow-equal": "^5.52.0", + "@wordpress/priority-queue": "^3.52.0", + "@wordpress/private-apis": "^1.52.0", + "@wordpress/redux-routine": "^5.52.0", + "deepmerge": "^4.3.1", "equivalent-key-map": "^0.2.2", "is-plain-object": "^5.0.0", "is-promise": "^4.0.0", @@ -4328,16 +4410,75 @@ "npm": ">=8.19.2" }, "peerDependencies": { - "react": "^18.0.0" + "@types/react": "^18 || ^19", + "react": "^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@wordpress/data/node_modules/@wordpress/compose": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@wordpress/compose/-/compose-8.5.0.tgz", + "integrity": "sha512-giYS22Tbhtr3Lj4lK6lxzGqV6EkM2QeVBiP7+c9r2F9rnQN3F6A8cN4gQM5sVGdgIOlps9tw+dsn9OHzIFVwIg==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@types/mousetrap": "^1.6.8", + "@wordpress/deprecated": "^4.52.0", + "@wordpress/dom": "^4.52.0", + "@wordpress/element": "^8.4.0", + "@wordpress/is-shallow-equal": "^5.52.0", + "@wordpress/keycodes": "^4.52.0", + "@wordpress/priority-queue": "^3.52.0", + "@wordpress/private-apis": "^1.52.0", + "@wordpress/undo-manager": "^1.52.0", + "change-case": "^4.1.2", + "mousetrap": "^1.6.5", + "use-memo-one": "^1.1.1" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + }, + "peerDependencies": { + "@types/react": "^18 || ^19", + "react": "^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@wordpress/data/node_modules/@wordpress/element": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@wordpress/element/-/element-8.4.0.tgz", + "integrity": "sha512-/LzW+0MpSmKfYiESoXUQtjBPqOPyvqUm17GigQR5c0Z8Wj2NiklP72x4XaF02uSkau1ru1Z9lP8NWEGjwBwLsA==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@types/react": "^18.3.27", + "@types/react-dom": "^18.3.1", + "@wordpress/deprecated": "^4.52.0", + "@wordpress/escape-html": "^3.52.0", + "change-case": "^4.1.2", + "is-plain-object": "^5.0.0", + "react": "^18.3.1", + "react-dom": "^18.3.1" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" } }, "node_modules/@wordpress/date": { - "version": "5.43.0", - "resolved": "https://registry.npmjs.org/@wordpress/date/-/date-5.43.0.tgz", - "integrity": "sha512-8DiFlE7YzP7F/P59Hr6h5fWJxJlvt6eZgU1C7huM9XhANh8Y3dZfepsySL6K7h1yE66SQDSq07cEefFQgJW31g==", + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@wordpress/date/-/date-5.52.0.tgz", + "integrity": "sha512-JRAXv2CQwUiJq9k2n/iUqblo9rX3LUFJ03lA4zRy4+bxcSiMdpXvYrpZfUTp3W4I1Oc/fPZIYQY0HB3XNyjUWw==", "license": "GPL-2.0-or-later", "dependencies": { - "@wordpress/deprecated": "^4.43.0", + "@wordpress/deprecated": "^4.52.0", "moment": "^2.29.4", "moment-timezone": "^0.5.40" }, @@ -4347,12 +4488,12 @@ } }, "node_modules/@wordpress/deprecated": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@wordpress/deprecated/-/deprecated-4.43.0.tgz", - "integrity": "sha512-Pxn+nUmCVAaKBiZun2tEVweVdevMvWFWyCRqIqsAKdWCLsD8Uk6o27EwXc1u8BlO65VmK8D2zF9uWKGKfdZbCw==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@wordpress/deprecated/-/deprecated-4.52.0.tgz", + "integrity": "sha512-94oHBKPty4pp6L5510LWDwJwNqFhikxLfwN6VUcDOQzj3itkc0MQ4ZQhmsfBy3Y6IRxSGx+p2bjSQvA4D+M96A==", "license": "GPL-2.0-or-later", "dependencies": { - "@wordpress/hooks": "^4.43.0" + "@wordpress/hooks": "^4.52.0" }, "engines": { "node": ">=18.12.0", @@ -4360,12 +4501,12 @@ } }, "node_modules/@wordpress/dom": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@wordpress/dom/-/dom-4.40.0.tgz", - "integrity": "sha512-JBF1sRjJMFgLn0pet0tmPzO1kNaa35/DwAAtG81zzjikctR1PzE3EK8o6ZGPtUY1sTa9l7aB1Lxfcum/eroyRg==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@wordpress/dom/-/dom-4.52.0.tgz", + "integrity": "sha512-LUY9nI9h6blk4xBuG83KgyfTnx7PMs/g6ZVzY/SOaCcOc2P3zOfzacADq/vxQydbZpcwtLM6juzgnNta6AX95w==", "license": "GPL-2.0-or-later", "dependencies": { - "@wordpress/deprecated": "^4.40.0" + "@wordpress/deprecated": "^4.52.0" }, "engines": { "node": ">=18.12.0", @@ -4373,9 +4514,9 @@ } }, "node_modules/@wordpress/dom-ready": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@wordpress/dom-ready/-/dom-ready-4.40.0.tgz", - "integrity": "sha512-mHVy4P6yc0XLmGgnccxptMKg83TwcbYKfYrQH8pTcIu43P24zONTd44eZFjkfz7c/b+RLJg1Kj+d5mKh1xqH1A==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@wordpress/dom-ready/-/dom-ready-4.52.0.tgz", + "integrity": "sha512-dAsch1oeyRV4kwoQuqdr6dXisGAs09OtvGOk09Ke+QC0fTTPzxdKUw+c4M1nk0AmCeyZfqAqYYGCMm4n241QeQ==", "license": "GPL-2.0-or-later", "engines": { "node": ">=18.12.0", @@ -4486,9 +4627,9 @@ } }, "node_modules/@wordpress/escape-html": { - "version": "3.40.0", - "resolved": "https://registry.npmjs.org/@wordpress/escape-html/-/escape-html-3.40.0.tgz", - "integrity": "sha512-DD6xWVbnw4fGGgO6DFDTJiLj52om0OG4cYHLz7ZhuipmOlEUGljPYOcrj8uxtlh5EFrqHCIPkOya+qQXUHUSBw==", + "version": "3.52.0", + "resolved": "https://registry.npmjs.org/@wordpress/escape-html/-/escape-html-3.52.0.tgz", + "integrity": "sha512-sW8X839Xu8aiJlCGF48MM3iYrcXspuiY0By8iTpXKpnUdd2u0SL9HRR0ALSAsOf2ujOeWm/x58ODMSchovRWGw==", "license": "GPL-2.0-or-later", "engines": { "node": ">=18.12.0", @@ -4496,9 +4637,9 @@ } }, "node_modules/@wordpress/hooks": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@wordpress/hooks/-/hooks-4.43.0.tgz", - "integrity": "sha512-BY7GPjEwhOlgkavVak40E3RtA8Z9ehydqTZckRoesMRjXYfxKSzr1C1FT4wAPS5uXM1pNlWivfofMaJjVNQu5w==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@wordpress/hooks/-/hooks-4.52.0.tgz", + "integrity": "sha512-EbV/nJTerhqwNW3DLvvGutJfNyXcmBHXuWyJpv1NypzT80k21jPGP79HBE5Z0A2oAI2kIBp6Klaa4O8uEjq/sw==", "license": "GPL-2.0-or-later", "engines": { "node": ">=18.12.0", @@ -4506,9 +4647,9 @@ } }, "node_modules/@wordpress/html-entities": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@wordpress/html-entities/-/html-entities-4.40.0.tgz", - "integrity": "sha512-bsJrwZk22On8gNhUd84yyWKt/nrNZtACNZpXmkpyue/oTlFqNenLfhqRkvTKJzjbLxrrcUPsXlskbPcS7mxwTQ==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@wordpress/html-entities/-/html-entities-4.52.0.tgz", + "integrity": "sha512-SM0XH+EgCi20Ptqv5WGkM8d2CAlThWRO/BVMeXzu6RLlcHKLMXNQZvW2waXCZtUpto8hlVZT9Dzg8z37sj4OiQ==", "license": "GPL-2.0-or-later", "engines": { "node": ">=18.12.0", @@ -4552,9 +4693,9 @@ } }, "node_modules/@wordpress/is-shallow-equal": { - "version": "5.40.0", - "resolved": "https://registry.npmjs.org/@wordpress/is-shallow-equal/-/is-shallow-equal-5.40.0.tgz", - "integrity": "sha512-IU11xOcHIGqDLxx9X+8RIk4WFo0qqba0bpeLqrVKsQXNGjP7tXSo2ufylxE9K9CEYXFMF0C65k83XpRZtEkA8g==", + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@wordpress/is-shallow-equal/-/is-shallow-equal-5.52.0.tgz", + "integrity": "sha512-LMIBLLTZ+vvq0DlYebL4d9XaCu16PNnhPGSn8QqsMPmBzwBYOjO0/btMLpYbs9rRc8k1QKBDWvw8eddvRVsYxA==", "license": "GPL-2.0-or-later", "engines": { "node": ">=18.12.0", @@ -4562,28 +4703,35 @@ } }, "node_modules/@wordpress/keycodes": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@wordpress/keycodes/-/keycodes-4.40.0.tgz", - "integrity": "sha512-laLkfjwkhMdreCl/KQdHucBIQAYwSjkyk3BToq/PCrcxFJBwWK2NgEtSl/t1CEw2HJwe0H2ne3FEWtipY4iDrA==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@wordpress/keycodes/-/keycodes-4.52.0.tgz", + "integrity": "sha512-KRvwViTTdxUDgikYaCm+qnXSH2UlFuCDjAIvtjjcen8oRviSkeMv6bPn1T2vqFRKcVgt88xTtKlvI8l5U99o2Q==", "license": "GPL-2.0-or-later", "dependencies": { - "@wordpress/i18n": "^6.13.0" + "@wordpress/i18n": "^6.25.0" }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" + }, + "peerDependencies": { + "@types/react": "^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, "node_modules/@wordpress/keycodes/node_modules/@wordpress/i18n": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/@wordpress/i18n/-/i18n-6.13.0.tgz", - "integrity": "sha512-Yx882uFxcg6QpB13fv8UhvM6k5NwMQGfNXKB9SVSNL/APvDWn2m/n4n+5GZYi+wOV+KJLojQZbdRpHWCnX/jFg==", + "version": "6.25.0", + "resolved": "https://registry.npmjs.org/@wordpress/i18n/-/i18n-6.25.0.tgz", + "integrity": "sha512-UcyUT8CJgEkhjzEGTVV6kw0Qi4OraF0I9J9FBLmTda/1Wi1o6rLAXZBm2aJrP740ezFxPneHH92aQF4Z5xZqcQ==", "license": "GPL-2.0-or-later", "dependencies": { "@tannin/sprintf": "^1.3.2", - "@wordpress/hooks": "^4.40.0", + "@wordpress/hooks": "^4.52.0", "gettext-parser": "^1.3.1", - "memize": "^2.1.0", "tannin": "^1.2.0" }, "bin": { @@ -4595,12 +4743,12 @@ } }, "node_modules/@wordpress/primitives": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@wordpress/primitives/-/primitives-4.40.0.tgz", - "integrity": "sha512-0gOw3n3kSUsAPo91xNDS9J4GGTrNXU90XmuWn7mNfXAl5uRAMRnxgkfL+pwd0ng0rmdPtjPqrJpljnP2oy3K2w==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@wordpress/primitives/-/primitives-4.52.0.tgz", + "integrity": "sha512-rgB+Q1i97AY8AECLAEhvV/bGw9sOV6CKtUaY90t7nuv6M+ZgXqwQLRRWVaXWFUsthcZ2y4sTIbKfxwVJV+JQ6g==", "license": "GPL-2.0-or-later", "dependencies": { - "@wordpress/element": "^6.40.0", + "@wordpress/element": "^8.4.0", "clsx": "^2.1.1" }, "engines": { @@ -4608,13 +4756,39 @@ "npm": ">=8.19.2" }, "peerDependencies": { - "react": "^18.0.0" + "@types/react": "^18 || ^19", + "react": "^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@wordpress/primitives/node_modules/@wordpress/element": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@wordpress/element/-/element-8.4.0.tgz", + "integrity": "sha512-/LzW+0MpSmKfYiESoXUQtjBPqOPyvqUm17GigQR5c0Z8Wj2NiklP72x4XaF02uSkau1ru1Z9lP8NWEGjwBwLsA==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@types/react": "^18.3.27", + "@types/react-dom": "^18.3.1", + "@wordpress/deprecated": "^4.52.0", + "@wordpress/escape-html": "^3.52.0", + "change-case": "^4.1.2", + "is-plain-object": "^5.0.0", + "react": "^18.3.1", + "react-dom": "^18.3.1" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" } }, "node_modules/@wordpress/priority-queue": { - "version": "3.40.0", - "resolved": "https://registry.npmjs.org/@wordpress/priority-queue/-/priority-queue-3.40.0.tgz", - "integrity": "sha512-85km9+I7RWi7P73BU/yom41gpdu0watdQ1GscQhQBel6BjHOXO5qWG6P9i3sEH47bz7EyO248l4LC/h8oHqpfQ==", + "version": "3.52.0", + "resolved": "https://registry.npmjs.org/@wordpress/priority-queue/-/priority-queue-3.52.0.tgz", + "integrity": "sha512-aYnVXxV5j4D73tRld2n1D3G6cKLfSwKWcTnktJ7XlmiswW7xlaAqvn4FyBjwmIe20pZw0A7xLH5xCuQcqOM7nw==", "license": "GPL-2.0-or-later", "dependencies": { "requestidlecallback": "^0.3.0" @@ -4625,9 +4799,9 @@ } }, "node_modules/@wordpress/private-apis": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/@wordpress/private-apis/-/private-apis-1.40.0.tgz", - "integrity": "sha512-68cwZKVq8Xy8GBzKoDRuV4b3pQ4nJFItY689HXp+poc0XXrnAeC4ZhjeSgS1qGRpFo6RVvLjjcaZsN2OrSSMvQ==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/@wordpress/private-apis/-/private-apis-1.52.0.tgz", + "integrity": "sha512-gFcmBXSti73Y4uEx++5qUNDaH/o0/2ijrHEJkY5e/df4RtmxVSQOIVxftRiI4m9thCWfJMoehMMreJxhwx6Qtg==", "license": "GPL-2.0-or-later", "engines": { "node": ">=18.12.0", @@ -4635,9 +4809,9 @@ } }, "node_modules/@wordpress/redux-routine": { - "version": "5.40.0", - "resolved": "https://registry.npmjs.org/@wordpress/redux-routine/-/redux-routine-5.40.0.tgz", - "integrity": "sha512-V+c1yCBl4i7qvRsWtQpGevbFCGtrRlzDe++4bwnrYJUiu79wbSXWRrmiSFr/EQie2KNM680t2MeFcfO7nsDVoA==", + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@wordpress/redux-routine/-/redux-routine-5.52.0.tgz", + "integrity": "sha512-MpyNAKpfQAk8IWJ3Cwzc/p1dVEUl/iYxL6GCTX/y7K3c8qYd7csU4o5kuK9lfeqgVVI+d0y6rzBZiTaL2Z6Ojw==", "license": "GPL-2.0-or-later", "dependencies": { "is-plain-object": "^5.0.0", @@ -4653,42 +4827,99 @@ } }, "node_modules/@wordpress/rich-text": { - "version": "7.40.0", - "resolved": "https://registry.npmjs.org/@wordpress/rich-text/-/rich-text-7.40.0.tgz", - "integrity": "sha512-eHImTvzPEg4GWAuzcagyc2tArc6neA2sbqvybpd5JzhEpgv/Q0zcKwLfUKI05kYaaPI/Rg5WXgeXDxjGYpq5hA==", + "version": "7.52.0", + "resolved": "https://registry.npmjs.org/@wordpress/rich-text/-/rich-text-7.52.0.tgz", + "integrity": "sha512-zyd7w4hs8TV+nWrC/hbULg4lKJQyxpd5AolFfOHAJmKM2LrU/Lvlobz9oyGuTrZ/HXdkFs3wzPMwS/VGydSjHw==", "license": "GPL-2.0-or-later", "dependencies": { - "@wordpress/a11y": "^4.40.0", - "@wordpress/compose": "^7.40.0", - "@wordpress/data": "^10.40.0", - "@wordpress/deprecated": "^4.40.0", - "@wordpress/dom": "^4.40.0", - "@wordpress/element": "^6.40.0", - "@wordpress/escape-html": "^3.40.0", - "@wordpress/i18n": "^6.13.0", - "@wordpress/keycodes": "^4.40.0", - "@wordpress/private-apis": "^1.40.0", - "colord": "2.9.3", - "memize": "^2.1.0" + "@wordpress/a11y": "^4.52.0", + "@wordpress/compose": "^8.5.0", + "@wordpress/data": "^10.52.0", + "@wordpress/deprecated": "^4.52.0", + "@wordpress/dom": "^4.52.0", + "@wordpress/element": "^8.4.0", + "@wordpress/escape-html": "^3.52.0", + "@wordpress/i18n": "^6.25.0", + "@wordpress/keycodes": "^4.52.0", + "@wordpress/private-apis": "^1.52.0", + "colord": "^2.9.3" }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" }, "peerDependencies": { - "react": "^18.0.0" + "@types/react": "^18 || ^19", + "react": "^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@wordpress/rich-text/node_modules/@wordpress/compose": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@wordpress/compose/-/compose-8.5.0.tgz", + "integrity": "sha512-giYS22Tbhtr3Lj4lK6lxzGqV6EkM2QeVBiP7+c9r2F9rnQN3F6A8cN4gQM5sVGdgIOlps9tw+dsn9OHzIFVwIg==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@types/mousetrap": "^1.6.8", + "@wordpress/deprecated": "^4.52.0", + "@wordpress/dom": "^4.52.0", + "@wordpress/element": "^8.4.0", + "@wordpress/is-shallow-equal": "^5.52.0", + "@wordpress/keycodes": "^4.52.0", + "@wordpress/priority-queue": "^3.52.0", + "@wordpress/private-apis": "^1.52.0", + "@wordpress/undo-manager": "^1.52.0", + "change-case": "^4.1.2", + "mousetrap": "^1.6.5", + "use-memo-one": "^1.1.1" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + }, + "peerDependencies": { + "@types/react": "^18 || ^19", + "react": "^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@wordpress/rich-text/node_modules/@wordpress/element": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@wordpress/element/-/element-8.4.0.tgz", + "integrity": "sha512-/LzW+0MpSmKfYiESoXUQtjBPqOPyvqUm17GigQR5c0Z8Wj2NiklP72x4XaF02uSkau1ru1Z9lP8NWEGjwBwLsA==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@types/react": "^18.3.27", + "@types/react-dom": "^18.3.1", + "@wordpress/deprecated": "^4.52.0", + "@wordpress/escape-html": "^3.52.0", + "change-case": "^4.1.2", + "is-plain-object": "^5.0.0", + "react": "^18.3.1", + "react-dom": "^18.3.1" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" } }, "node_modules/@wordpress/rich-text/node_modules/@wordpress/i18n": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/@wordpress/i18n/-/i18n-6.13.0.tgz", - "integrity": "sha512-Yx882uFxcg6QpB13fv8UhvM6k5NwMQGfNXKB9SVSNL/APvDWn2m/n4n+5GZYi+wOV+KJLojQZbdRpHWCnX/jFg==", + "version": "6.25.0", + "resolved": "https://registry.npmjs.org/@wordpress/i18n/-/i18n-6.25.0.tgz", + "integrity": "sha512-UcyUT8CJgEkhjzEGTVV6kw0Qi4OraF0I9J9FBLmTda/1Wi1o6rLAXZBm2aJrP740ezFxPneHH92aQF4Z5xZqcQ==", "license": "GPL-2.0-or-later", "dependencies": { "@tannin/sprintf": "^1.3.2", - "@wordpress/hooks": "^4.40.0", + "@wordpress/hooks": "^4.52.0", "gettext-parser": "^1.3.1", - "memize": "^2.1.0", "tannin": "^1.2.0" }, "bin": { @@ -4700,12 +4931,12 @@ } }, "node_modules/@wordpress/undo-manager": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/@wordpress/undo-manager/-/undo-manager-1.40.0.tgz", - "integrity": "sha512-QvhHke/bVaOSPeaV5mNvsuIQpc2dJFDhXZ7gUnpuzyuNHh74Xk6Ar0vvYcfXiALst4ejKqWCoKOBi7ve1h2ppg==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/@wordpress/undo-manager/-/undo-manager-1.52.0.tgz", + "integrity": "sha512-6hI0WWDOtLLVEkWqQokCoQRz5Pba9UNtgt5MV4hHGe5x0mzsowj+GWHedppf8UCVZ2ex3cde7fThaRKMCackrQ==", "license": "GPL-2.0-or-later", "dependencies": { - "@wordpress/is-shallow-equal": "^5.40.0" + "@wordpress/is-shallow-equal": "^5.52.0" }, "engines": { "node": ">=18.12.0", @@ -4713,9 +4944,9 @@ } }, "node_modules/@wordpress/url": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@wordpress/url/-/url-4.40.0.tgz", - "integrity": "sha512-DVAJlW7bdocKfQp8G7tS73vnobAC8TBbIHHdxeLQKwzT8mOkG4W/rpzN2KTxkiJKFXUu5in4F8a6T+Cy/Lt1eQ==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@wordpress/url/-/url-4.52.0.tgz", + "integrity": "sha512-4cSo0dUHiOQB2DiiPkoVIqP5aoGJdIj32LD9XgG41yeh7xG6ALdDA1I/883WFRoX2JIYySFj208MzopURDH7Yg==", "license": "GPL-2.0-or-later", "dependencies": { "remove-accents": "^0.5.0" @@ -4726,9 +4957,9 @@ } }, "node_modules/@wordpress/warning": { - "version": "3.40.0", - "resolved": "https://registry.npmjs.org/@wordpress/warning/-/warning-3.40.0.tgz", - "integrity": "sha512-0l3OFa1Z+UdhWRRHX9JWWKofo7Lbi2MqOFzzzn0MC26HOyfieQycjLVLNVNXaaodIKUhap6uDQq+JXbbHm881A==", + "version": "3.52.0", + "resolved": "https://registry.npmjs.org/@wordpress/warning/-/warning-3.52.0.tgz", + "integrity": "sha512-BjJ+Jte1g2nMITI1IHq0NwMpm/qlFOV1L+gNe0vFQmGKEcXiQ0DjrHQNtA8hV+Mgt9zSWRNS71Fgonht3r72Ew==", "license": "GPL-2.0-or-later", "engines": { "node": ">=18.12.0", @@ -8036,15 +8267,19 @@ } }, "node_modules/framer-motion": { - "version": "11.3.28", + "version": "11.18.2", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.18.2.tgz", + "integrity": "sha512-5F5Och7wrvtLVElIpclDT0CBzMVg3dL22B64aZwHtsIY8RB4mXICLrkajK4G9R+ieSAGcgrLeae2SeUTg2pr6w==", "license": "MIT", "dependencies": { + "motion-dom": "^11.18.1", + "motion-utils": "^11.18.1", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", - "react": "^18.0.0", - "react-dom": "^18.0.0" + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@emotion/is-prop-valid": { @@ -9873,7 +10108,9 @@ "license": "CC0-1.0" }, "node_modules/memize": { - "version": "2.1.0", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/memize/-/memize-2.1.1.tgz", + "integrity": "sha512-8Nl+i9S5D6KXnruM03Jgjb+LwSupvR13WBr4hJegaaEyobvowCVupi79y2WSiWvO1mzBWxPwEYE5feCe8vyA5w==", "license": "MIT" }, "node_modules/memoize-one": { @@ -10032,6 +10269,21 @@ "node": "*" } }, + "node_modules/motion-dom": { + "version": "11.18.1", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-11.18.1.tgz", + "integrity": "sha512-g76KvA001z+atjfxczdRtw/RXOM3OMSdd1f4DL77qCTF/+avrRJiawSG4yDibEQ215sr9kpinSlX2pCTJ9zbhw==", + "license": "MIT", + "dependencies": { + "motion-utils": "^11.18.1" + } + }, + "node_modules/motion-utils": { + "version": "11.18.1", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-11.18.1.tgz", + "integrity": "sha512-49Kt+HKjtbJKLtgO/LKj9Ld+6vw9BjH5d9sc40R/kVyH8GLAXgT42M2NnuPcJNuA3s9ZfZBUcwIgpmZWGEE+hA==", + "license": "MIT" + }, "node_modules/mousetrap": { "version": "1.6.5", "resolved": "https://registry.npmjs.org/mousetrap/-/mousetrap-1.6.5.tgz", @@ -13713,7 +13965,9 @@ } }, "node_modules/use-sync-external-store": { - "version": "1.4.0", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", "license": "MIT", "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" @@ -13725,14 +13979,16 @@ "license": "MIT" }, "node_modules/uuid": { - "version": "9.0.1", + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.1.tgz", + "integrity": "sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], "license": "MIT", "bin": { - "uuid": "dist/bin/uuid" + "uuid": "dist/esm/bin/uuid" } }, "node_modules/v8-compile-cache-lib": { diff --git a/package.json b/package.json index ee8f2bb5f..f20cc6920 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "code-snippets", "description": "Manage code snippets running on a WordPress-powered site through a graphical interface.", "homepage": "https://codesnippets.pro", - "version": "3.10.2", + "version": "4.0.0-beta.3", "main": "src/dist/edit.js", "directories": { "test": "tests" @@ -63,7 +63,8 @@ "prismjs": "^1.29.0", "react": "^18.3.1", "react-dom": "^18.3.1", - "react-select": "^5.10.0" + "react-select": "^5.10.0", + "uuid": "^11.1.0" }, "devDependencies": { "@axe-core/playwright": "^4.11.2", diff --git a/scripts/install-wp-tests.sh b/scripts/install-wp-tests.sh index ad277eb77..17dcf0f59 100755 --- a/scripts/install-wp-tests.sh +++ b/scripts/install-wp-tests.sh @@ -13,7 +13,7 @@ WP_VERSION=${5-latest} SKIP_DB_CREATE=${6-false} TMPDIR=${TMPDIR-/tmp} -TMPDIR=$(echo $TMPDIR | sed -e "s/\/$//") +TMPDIR=$(echo "$TMPDIR" | sed -e "s/\/$//") WP_TESTS_DIR=${WP_TESTS_DIR-$TMPDIR/wordpress-tests-lib} WP_CORE_DIR=${WP_CORE_DIR-$TMPDIR/wordpress/} @@ -42,33 +42,33 @@ set -ex install_wp() { - if [ -d $WP_CORE_DIR ]; then + if [ -d "$WP_CORE_DIR" ]; then return; fi - mkdir -p $WP_CORE_DIR + mkdir -p "$WP_CORE_DIR" if [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then - mkdir -p $TMPDIR/wordpress-trunk - rm -rf $TMPDIR/wordpress-trunk/* - svn export --quiet https://core.svn.wordpress.org/trunk $TMPDIR/wordpress-trunk/wordpress - mv $TMPDIR/wordpress-trunk/wordpress/* $WP_CORE_DIR + mkdir -p "$TMPDIR/wordpress-trunk" + rm -rf "$TMPDIR"/wordpress-trunk/* + svn export --quiet https://core.svn.wordpress.org/trunk "$TMPDIR/wordpress-trunk/wordpress" + mv "$TMPDIR"/wordpress-trunk/wordpress/* "$WP_CORE_DIR" else if [ $WP_VERSION == 'latest' ]; then local ARCHIVE_NAME='latest' elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+ ]]; then # https serves multiple offers, whereas http serves single. - download https://wordpress.org/wordpress-$WP_VERSION.tar.gz $TMPDIR/wordpress.tar.gz + download "https://wordpress.org/wordpress-$WP_VERSION.tar.gz" "$TMPDIR/wordpress.tar.gz" ARCHIVE_NAME="wordpress-$WP_VERSION" fi - if [ ! -f $TMPDIR/wordpress.tar.gz ]; then - download https://wordpress.org/${ARCHIVE_NAME}.tar.gz $TMPDIR/wordpress.tar.gz + if [ ! -f "$TMPDIR/wordpress.tar.gz" ]; then + download "https://wordpress.org/${ARCHIVE_NAME}.tar.gz" "$TMPDIR/wordpress.tar.gz" fi - tar --strip-components=1 -zxmf $TMPDIR/wordpress.tar.gz -C $WP_CORE_DIR + tar --strip-components=1 -zxmf "$TMPDIR/wordpress.tar.gz" -C "$WP_CORE_DIR" fi - download https://raw.githubusercontent.com/markoheijnen/wp-mysqli/master/db.php $WP_CORE_DIR/wp-content/db.php + download https://raw.githubusercontent.com/markoheijnen/wp-mysqli/master/db.php "$WP_CORE_DIR/wp-content/db.php" } install_test_suite() { @@ -80,11 +80,11 @@ install_test_suite() { fi # set up testing suite if it doesn't yet exist - if [ ! -d $WP_TESTS_DIR ]; then + if [ ! -d "$WP_TESTS_DIR" ]; then local WP_TESTS_VERSION="${WP_TESTS_TAG#tags/}" case "$WP_TESTS_VERSION" in *.*.*) ;; *.*) WP_TESTS_VERSION="$WP_TESTS_VERSION.0" ;; esac - mkdir -p $WP_TESTS_DIR - rm -rf $WP_TESTS_DIR/{includes,data} + mkdir -p "$WP_TESTS_DIR" + rm -rf "$WP_TESTS_DIR"/{includes,data} download "https://github.com/WordPress/wordpress-develop/archive/refs/tags/${WP_TESTS_VERSION}.tar.gz" "$TMPDIR/wp-develop.tar.gz" rm -rf "$TMPDIR/wp-develop" mkdir -p "$TMPDIR/wp-develop" @@ -94,12 +94,15 @@ install_test_suite() { fi if [ ! -f "$WP_TESTS_DIR/wp-tests-config.php" ]; then - download https://develop.svn.wordpress.org/${WP_TESTS_TAG}/wp-tests-config-sample.php "$WP_TESTS_DIR"/wp-tests-config.php + download "https://develop.svn.wordpress.org/${WP_TESTS_TAG}/wp-tests-config-sample.php" "$WP_TESTS_DIR"/wp-tests-config.php # remove all forward slashes in the end - WP_CORE_DIR=$(echo $WP_CORE_DIR | sed "s:/\+$::") + WP_CORE_DIR=$(echo "$WP_CORE_DIR" | sed "s:/\+$::") + # sed reads '&' and '\' in the replacement as syntax, so a path containing either + # would be written to the config mangled. + WP_CORE_DIR_ESCAPED=$(printf '%s' "$WP_CORE_DIR" | sed -e 's:[\\&]:\\&:g') # Support both older (/src/) and current (/wordpress/) sample config templates. - sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR/':" "$WP_TESTS_DIR"/wp-tests-config.php - sed $ioption "s:dirname( __FILE__ ) . '/wordpress/':'$WP_CORE_DIR/':" "$WP_TESTS_DIR"/wp-tests-config.php + sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR_ESCAPED/':" "$WP_TESTS_DIR"/wp-tests-config.php + sed $ioption "s:dirname( __FILE__ ) . '/wordpress/':'$WP_CORE_DIR_ESCAPED/':" "$WP_TESTS_DIR"/wp-tests-config.php sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" "$WP_TESTS_DIR"/wp-tests-config.php sed $ioption "s/yourusernamehere/$DB_USER/" "$WP_TESTS_DIR"/wp-tests-config.php sed $ioption "s/yourpasswordhere/$DB_PASS/" "$WP_TESTS_DIR"/wp-tests-config.php diff --git a/scripts/linters/lint-changelog.ts b/scripts/linters/lint-changelog.ts index 4acaf4741..0fc6aaf81 100644 --- a/scripts/linters/lint-changelog.ts +++ b/scripts/linters/lint-changelog.ts @@ -60,17 +60,12 @@ const collapseBlankLines = (lines: string[]): string[] => { return out } -/** Remove blank lines before the first content line. */ const stripLeadingBlanks = (lines: string[]): string[] => { let i = 0 while (i < lines.length && '' === lines[i].trim()) {i += 1} return lines.slice(i) } -/** - * Drop blank lines that sit between two list items. Bullets accumulate stray - * blank lines over successive edits; consecutive items should be contiguous. - */ const removeBlanksBetweenListItems = (lines: string[]): string[] => { const isItem = (l: string): boolean => /^\s*[*-] /.test(l) const out: string[] = [] diff --git a/scripts/linters/lint-readme.ts b/scripts/linters/lint-readme.ts index 9d10fa6a4..d24f1a9be 100644 --- a/scripts/linters/lint-readme.ts +++ b/scripts/linters/lint-readme.ts @@ -100,7 +100,6 @@ const collapseBlankLines = (lines: string[]): string[] => { return out } -/** Remove blank lines before the first content line. */ const stripLeadingBlanks = (lines: string[]): string[] => { let i = 0 while (i < lines.length && '' === lines[i].trim()) { @@ -109,10 +108,6 @@ const stripLeadingBlanks = (lines: string[]): string[] => { return lines.slice(i) } -/** - * Drop blank lines that sit between two list items. Bullets accumulate stray - * blank lines over successive edits; consecutive items should be contiguous. - */ const removeBlanksBetweenListItems = (lines: string[]): string[] => { const isItem = (l: string): boolean => /^\s*[*-] /.test(l) const out: string[] = [] diff --git a/scripts/test-setup-phpunit.ts b/scripts/test-setup-phpunit.ts index 7bd57f27b..94ef6dc0a 100644 --- a/scripts/test-setup-phpunit.ts +++ b/scripts/test-setup-phpunit.ts @@ -1,80 +1,77 @@ #!/usr/bin/env ts-node import { execFileSync } from 'node:child_process' +import { existsSync } from 'node:fs' import { resolve } from 'node:path' - -const getEnv = (key: string, fallback: string): string => process.env[key] ?? fallback - -const run = (cmd: string, args: readonly string[], options: { env?: NodeJS.ProcessEnv } = {}) => { - const extraEnv = options.env ?? {} - execFileSync(cmd, args, { stdio: 'inherit', env: { ...process.env, ...extraEnv } }) +import process, { loadEnvFile } from 'node:process' + +interface DatabaseOptions { + binary: string + schema: string + user: string + password: string + host: string } -const buildMysqlArgs = (options: { user: string; password: string; host: string }) => { - const args = ['-u', options.user] +const run = (cmd: string, args: readonly string[], options?: { env?: NodeJS.ProcessEnv }) => { + execFileSync(cmd, args, { stdio: 'inherit', env: { ...process.env, ...options?.env } }) +} - if (options.password) { - args.push(`--password=${options.password}`) - } +const buildMysqlArgs = (options: DatabaseOptions): string[] => [ + ...['-u', options.user], + ...options.password ? [`--password=${options.password}`] : [], + ...options.host ? ['-h', options.host] : [] +] - if (options.host) { - args.push('-h', options.host) +const assertSafeIdentifier = (text: string, name: string) => { + if (!/^[A-Za-z0-9_]+$/.test(text)) { + throw new Error(`Invalid ${name} "${text}". Use only letters, numbers, and underscore.`) } - - return args } -const assertSafeDbName = (dbName: string) => { - if (!/^[A-Za-z0-9_]+$/.test(dbName)) { - throw new Error(`Invalid DB name "${dbName}". Use only letters, numbers, and underscore.`) +const assertSimpleString = (text: string, name: string) => { + if (!/^[A-Za-z0-9_.-]+$/.test(text)) { + throw new Error(`Invalid ${name} "${text}". Use only letters, numbers, underscore, dot, and hyphen.`) } } -const main = () => { - const dbName = getEnv('WP_PHPUNIT_DB_NAME', 'code_snippets_phpunit') - const dbUser = getEnv('WP_PHPUNIT_DB_USER', 'root') - const dbPass = getEnv('WP_PHPUNIT_DB_PASS', '') - const dbHost = getEnv('WP_PHPUNIT_DB_HOST', '127.0.0.1') - const wpVersion = getEnv('WP_PHPUNIT_WP_VERSION', 'latest') +const initialiseDatabase = (): DatabaseOptions => { + const db: DatabaseOptions = { + binary: process.env.WP_PHPUNIT_DB_BINARY ?? 'mysql', + schema: process.env.WP_PHPUNIT_DB_NAME ?? 'code_snippets_phpunit', + user: process.env.WP_PHPUNIT_DB_USER ?? 'root', + password: process.env.WP_PHPUNIT_DB_PASS ?? '', + host: process.env.WP_PHPUNIT_DB_HOST ?? '127.0.0.1', + } - assertSafeDbName(dbName) + assertSafeIdentifier(db.schema, 'WP_PHPUNIT_DB_NAME') + assertSafeIdentifier(db.user, 'WP_PHPUNIT_DB_USER') + assertSimpleString(db.host, 'WP_PHPUNIT_DB_HOST') + if ('' !== db.password) { + assertSimpleString(db.password, 'WP_PHPUNIT_DB_PASS') + } - const wpTestsDir = resolve(process.cwd(), '.wp-tests-lib') - const wpCoreDir = resolve(process.cwd(), '.wp-core') - const wpTestsConfig = resolve(wpTestsDir, 'wp-tests-config.php') - const installScript = resolve(process.cwd(), 'scripts', 'install-wp-tests.sh') + const useDbSocket = 'true' === (process.env.WP_PHPUNIT_DB_USE_SOCKET ?? 'false').toLowerCase() // Create the database if needed (avoid install-wp-tests.sh prompt / destructive behavior). - const mysqlArgs = buildMysqlArgs({ user: dbUser, password: dbPass, host: dbHost }) - run('mysql', [...mysqlArgs, '-e', `CREATE DATABASE IF NOT EXISTS \`${dbName}\`;`]) + const mysqlArgs = useDbSocket ? [] : buildMysqlArgs(db) + run(db.binary, [...mysqlArgs, '-e', `CREATE DATABASE IF NOT EXISTS \`${db.schema}\`;`]) - // Ensure config is regenerated with current DB settings. - run('rm', ['-f', wpTestsConfig, `${wpTestsConfig}.bak`]) - - run('bash', [ - installScript, - dbName, - dbUser, - dbPass, - dbHost, - wpVersion, - 'true' - ], { - env: { - WP_TESTS_DIR: wpTestsDir, - WP_CORE_DIR: wpCoreDir - } - }) + if (useDbSocket && 'root' !== db.user) { + run(db.binary, ['-e', `CREATE USER IF NOT EXISTS '${db.user}'@'${db.host}' IDENTIFIED BY '${db.password}';`]) + run(db.binary, ['-e', `GRANT ALL PRIVILEGES ON \`${db.schema}\`.* TO '${db.user}'@'${db.host}';`]) + run(db.binary, ['-e', 'FLUSH PRIVILEGES;']) + } // Ensure a clean test schema before WordPress bootstraps installation. - run('mysql', [ + run(db.binary, [ ...mysqlArgs, - dbName, + db.schema, '-e', [ 'SET FOREIGN_KEY_CHECKS = 0', 'SET @tables = (SELECT GROUP_CONCAT(table_name)' + - ` FROM information_schema.tables WHERE table_schema = '${dbName}' AND table_name LIKE 'wptests\\_%')`, + ` FROM information_schema.tables WHERE table_schema = '${db.schema}' AND table_name LIKE 'wptests\\_%')`, "SET @drop = IF(@tables IS NULL, 'SELECT 1', CONCAT('DROP TABLE ', @tables))", 'PREPARE stmt FROM @drop', 'EXECUTE stmt', @@ -83,6 +80,33 @@ const main = () => { ].join('; ') ]) + return db +} + +const main = () => { + const envFile = resolve(process.cwd(), '.env') + + if (existsSync(envFile)) { + loadEnvFile(envFile) + } + + const wpVersion = process.env.WP_PHPUNIT_WP_VERSION ?? 'latest' + const db = initialiseDatabase() + + const wpTestsDir = resolve(process.cwd(), '.wp-tests-lib') + const wpCoreDir = resolve(process.cwd(), '.wp-core') + const wpTestsConfig = resolve(wpTestsDir, 'wp-tests-config.php') + const installScript = resolve(process.cwd(), 'scripts', 'install-wp-tests.sh') + + // Ensure config is regenerated with current DB settings. + run('rm', ['-f', wpTestsConfig, `${wpTestsConfig}.bak`]) + + run( + 'bash', + [installScript, db.schema, db.user, db.password, db.host, wpVersion, 'true'], + { env: { WP_TESTS_DIR: wpTestsDir, WP_CORE_DIR: wpCoreDir } } + ) + // Initialize WordPress test tables so `npm run test:php` works on a fresh DB. run('php', [resolve(wpTestsDir, 'includes', 'install.php'), wpTestsConfig]) } diff --git a/scripts/test-setup-playwright.ts b/scripts/test-setup-playwright.ts index 7ebdcd3b4..18d88b869 100644 --- a/scripts/test-setup-playwright.ts +++ b/scripts/test-setup-playwright.ts @@ -1,7 +1,7 @@ #!/usr/bin/env ts-node import { execFileSync } from 'node:child_process' -import { readFileSync } from 'node:fs' +import { existsSync, readFileSync } from 'node:fs' import { resolve } from 'node:path' const run = (cmd: string, args: readonly string[]) => { @@ -10,6 +10,22 @@ const run = (cmd: string, args: readonly string[]) => { const runWpEnvCli = (args: readonly string[]) => run('npx', ['wp-env', 'run', 'cli', ...args]) +const loadEnvFile = (): void => { + const envPath = resolve(process.cwd(), '.env') + + if (!existsSync(envPath)) { + return + } + + for (const line of readFileSync(envPath, 'utf8').split('\n')) { + const match = /^\s*(?[\w.-]+)\s*=\s*(?.*?)\s*$/u.exec(line) + + if (match?.groups && !(match.groups.key in process.env)) { + process.env[match.groups.key] = match.groups.value.replace(/^["']|["']$/u, '') + } + } +} + const getPluginSlug = (): string => { const prefix = 'wp-content/plugins/' const config = <{ mappings?: Record }>JSON.parse(readFileSync(resolve(process.cwd(), '.wp-env.json'), 'utf8')) @@ -29,6 +45,8 @@ const main = () => { // - force enable_flat_files=false so the Playwright setup test can flip it to true // - delete all DB snippets with an E2E prefix (keeps list clean across runs) + loadEnvFile() + runWpEnvCli(['sh', '-lc', 'rm -rf wp-content/code-snippets']) runWpEnvCli(['wp', 'plugin', 'activate', getPluginSlug()]) diff --git a/src/code-snippets.php b/src/code-snippets.php index 3391a3533..d4e59c8c9 100644 --- a/src/code-snippets.php +++ b/src/code-snippets.php @@ -8,11 +8,11 @@ * License: GPL-2.0-or-later * License URI: license.txt * Text Domain: code-snippets - * Version: 3.10.2 + * Version: 4.0.0-beta.3 * Requires PHP: 7.4 * Requires at least: 5.5 * - * @version 3.10.2 + * @version 4.0.0-beta.3 * @package Code_Snippets * @author Shea Bunge * @copyright 2012-2026 Code Snippets Pro @@ -37,7 +37,7 @@ * * @const string */ - define( 'CODE_SNIPPETS_VERSION', '3.10.2' ); + define( 'CODE_SNIPPETS_VERSION', '4.0.0-beta.3' ); /** * The full path to the main file of this plugin. diff --git a/src/css/admin-bar.scss b/src/css/admin-bar.scss index 8da2b46fc..1ef7ceec1 100644 --- a/src/css/admin-bar.scss +++ b/src/css/admin-bar.scss @@ -27,7 +27,7 @@ .code-snippets-safe-mode-active { > .ab-item, &.ab-item { - background: #b32d2e; + background: var(--cs-color-error); color: #fff; } } @@ -84,7 +84,7 @@ .code-snippets-safe-mode-active:hover > .ab-item, .code-snippets-safe-mode-active.hover > .ab-item { - background: #d63638; + background: var(--cs-color-error); color: #fff; } @@ -112,8 +112,22 @@ opacity: 1; } + #wp-admin-bar-code-snippets-ran-on-this-page > .ab-sub-wrapper > .ab-submenu, #wp-admin-bar-code-snippets-active-snippets > .ab-sub-wrapper > .ab-submenu, #wp-admin-bar-code-snippets-inactive-snippets > .ab-sub-wrapper > .ab-submenu { padding-block-start: 0; } + + .code-snippets-kind-badge { + display: inline-block; + min-inline-size: 30px; + padding-inline: 5px; + border-radius: 3px; + color: #fff; + font-size: 10px; + font-weight: 600; + line-height: 1.7; + text-align: center; + text-transform: uppercase; + } } diff --git a/src/css/common/_badges.scss b/src/css/common/_badges.scss index b00ea2f6c..cd0849693 100644 --- a/src/css/common/_badges.scss +++ b/src/css/common/_badges.scss @@ -1,28 +1,12 @@ @use 'sass:map'; @use 'sass:list'; -@use 'sass:color'; @use 'theme'; -// Badges are wrapped in links that filter by type. The white ring separates the -// focus indicator from the badge, matching the activation switch. `:focus` is -// included because a click otherwise leaves the WordPress ring, which hugs the -// badge. -@mixin badge-link { - display: inline-flex; - border-radius: 3px; - - &:focus, - &:focus-visible { - outline: none; - box-shadow: 0 0 0 2px #fff, 0 0 0 4px theme.$accent; - } -} - .badge { font-size: 12px; font-weight: 700; text-transform: uppercase; - border-radius: 3px; + border-radius: 5px; text-align: center; min-inline-size: 43px; min-block-size: 24px; @@ -50,7 +34,7 @@ } .network-shared { - color: theme.$accent; + color: var(--cs-color-accent); font-size: 22px; inline-size: 100%; cursor: help; @@ -67,9 +51,9 @@ .wp-core-ui .button.nav-tab-button { margin-inline-start: 0.5em; float: inline-end; - color: #a7aaad; - background: #f6f7f7; - border-color: #f6f7f7; + color: var(--cs-color-text-disabled); + background: var(--cs-color-surface-subtle); + border-color: var(--cs-color-surface-subtle); align-self: center; display: flex; justify-content: center; @@ -82,7 +66,32 @@ color: #666; } -@each $name, $colors in theme.$badges { +/* format: background-color [color] */ +$badges: ( + // php/html/cond darkened for WCAG AA — at least 4.5:1 against white 12px + // bold text; php reuses the accent token, html/cond keep hue at reduced lightness. + php: #2271b1, + html: #cd4510, + css: #9b59b6, + js: #f7d67a #1c1f20, + cond: #22826f, + core: #0ca0a9, + pro: #f7e8e3 #df9279, + revisions: #50575e, + cloud: #009fb4, + bundles: #50575e, + cloud_search: #d27c00, + private: #f7e6be #a27813, + public: #dbebf7 #2271b1, + success: #d3e8d1 #447340, + failure: #fad7c1 #a24b16, + info: #d2e6f4 #2b71a3, + neutral: #f6f7f7 #646970, + neutralDark: #e2e5e5 #6c7e7e, + special: #dfc5ef #6e249c +); + +@each $name, $colors in $badges { $text-color: #fff; $background-color: list.nth($colors, 1); @@ -100,7 +109,7 @@ a.badge.#{$name}-badge:hover, button.badge.#{$name}-badge:hover { color: $text-color; - background-color: color.adjust($background-color, $lightness: -5%); + background-color: color-mix(in srgb, $background-color 95%, #000 5%); } } @@ -118,7 +127,7 @@ } .nav-tab-inactive { - $colors: map.get(theme.$badges, 'pro'); + $colors: map.get($badges, 'pro'); $text-color: list.nth($colors, 2); $background-color: list.nth($colors, 1); @@ -158,8 +167,8 @@ .badge.pro-badge, .inverted-badges .badge.pro-badge, .nav-tab-inactive .badge.pro-badge { - color: theme.$accent; - background-color: #eff5f9; + color: var(--cs-color-accent); + background-color: var(--cs-color-surface-info); border: 1px solid rgb(34 113 177 / 10%) !important; border-radius: 999px; padding-block: 3px; diff --git a/src/css/common/_banners.scss b/src/css/common/_banners.scss deleted file mode 100644 index 8769e0ac7..000000000 --- a/src/css/common/_banners.scss +++ /dev/null @@ -1,43 +0,0 @@ -@use '../common/theme'; -@use 'sass:map'; -@use 'sass:list'; - -@mixin banners { - .banner { - border: 0; - border-radius: 5px; - display: flex; - align-items: center; - padding: 6px 10px; - gap: 8px; - margin: 0; - - .banner-dismiss { - position: unset; - margin-inline-start: auto; - padding: 0; - - &, &::before { - color: inherit; - } - } - - .wp-core-ui &.is-dismissible { - position: unset; - padding-inline-end: 10px; - } - } - - @each $name, $colors in theme.$notices { - .banner-#{$name} { - color: list.nth($colors, 2); - background-color: list.nth($colors, 1); - } - } - - .banner-success::before { - content: '✓'; - font-weight: bold; - font-size: 16px; - } -} diff --git a/src/css/common/_buttons.scss b/src/css/common/_buttons.scss new file mode 100644 index 000000000..cb315d231 --- /dev/null +++ b/src/css/common/_buttons.scss @@ -0,0 +1,32 @@ +// A ` diff --git a/src/js/components/EditMenu/EditorSidebar/actions/ExportButtons.tsx b/src/js/components/EditMenu/EditorSidebar/actions/ExportButtons.tsx index 82d84aa71..a7b167465 100644 --- a/src/js/components/EditMenu/EditorSidebar/actions/ExportButtons.tsx +++ b/src/js/components/EditMenu/EditorSidebar/actions/ExportButtons.tsx @@ -11,10 +11,12 @@ import type { Snippet } from '../../../../types/Snippet' interface ExportButtonProps { name: string label: string + icon: string + title?: string makeRequest: (snippet: Snippet) => Promise } -const ExportButton: React.FC = ({ name, label, makeRequest }) => { +const ExportButton: React.FC = ({ name, label, icon, title, makeRequest }) => { const { snippet, isWorking, setIsWorking, handleRequestError } = useSnippetForm() const handleClick = () => { @@ -28,7 +30,8 @@ const ExportButton: React.FC = ({ name, label, makeRequest }) } return ( - ) @@ -43,13 +46,16 @@ export const ExportButtons: React.FC = () => { {window.CODE_SNIPPETS_EDIT?.enableDownloads && 'cond' !== getSnippetType(snippet) && ( )} diff --git a/src/js/components/EditMenu/EditorSidebar/controls/LockControl.tsx b/src/js/components/EditMenu/EditorSidebar/controls/LockControl.tsx index 84df9dcf1..6ad5800ca 100644 --- a/src/js/components/EditMenu/EditorSidebar/controls/LockControl.tsx +++ b/src/js/components/EditMenu/EditorSidebar/controls/LockControl.tsx @@ -41,6 +41,7 @@ export const LockControl: React.FC = () => { {snippet.locked ?