From da1b96eed80dfc755d2d34fe494d7062473a5fa2 Mon Sep 17 00:00:00 2001 From: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Date: Tue, 4 Aug 2026 16:49:43 +0000 Subject: [PATCH 01/49] feat: apply initial UI review tokens --- src/css/common/_subnav.scss | 9 ++-- src/css/common/_theme.scss | 17 ++++++ src/css/common/_wp-admin.scss | 54 +++++++++---------- src/css/manage/_snippets-table.scss | 5 +- .../SnippetsTable/ManageSnippetCard.tsx | 2 +- .../SnippetsTable/SnippetsTable.tsx | 2 +- 6 files changed, 55 insertions(+), 34 deletions(-) diff --git a/src/css/common/_subnav.scss b/src/css/common/_subnav.scss index 4b5ecf5fa..d9d4bf03c 100644 --- a/src/css/common/_subnav.scss +++ b/src/css/common/_subnav.scss @@ -196,17 +196,18 @@ } } -// Item count shown after a subnav tab's label, as muted text. +// Item count shown after a subnav tab's label, using the label colour at reduced opacity. .snippet-type-nav .subnav-count { font-size: 14px; font-weight: 600; line-height: 1.5; - color: #646970; + color: currentcolor; + opacity: 0.65; } -// The active tab's count adopts the accent alongside its label. +// Keep the active count tied to its active label rather than assigning a separate colour. .snippet-type-nav .snippet-type-link.active-type .subnav-count { - color: theme.$accent; + color: currentcolor; } // The short "All" form of the all-snippets label only replaces "All Snippets" diff --git a/src/css/common/_theme.scss b/src/css/common/_theme.scss index a836dce51..f7d183526 100644 --- a/src/css/common/_theme.scss +++ b/src/css/common/_theme.scss @@ -17,6 +17,23 @@ $control-text: #2c3338; $control-height: 38px; $control-radius: 5px; +// Runtime design tokens. Sass variables keep compile-time helpers available; +// custom properties expose the approved system to every rendered component. +:root { + --cs-color-accent: #2271b1; + --cs-color-accent-hover: #0a4b78; + --cs-color-text: #2c3338; + --cs-color-text-muted: #646970; + --cs-color-surface: #fff; + --cs-color-surface-subtle: #f6f7f7; + --cs-color-border: #c3c4c7; + --cs-color-border-subtle: #dcdcde; + --cs-control-height: 38px; + --cs-control-radius: 5px; + --cs-font-size-body: 14px; + --cs-line-height-body: 1.5; +} + /* format: background-color [color] */ $badges: ( // php/html/cond darkened for WCAG AA — at least 4.5:1 against white 12px diff --git a/src/css/common/_wp-admin.scss b/src/css/common/_wp-admin.scss index e0b9b839d..babb82a95 100644 --- a/src/css/common/_wp-admin.scss +++ b/src/css/common/_wp-admin.scss @@ -38,18 +38,18 @@ input[type='tel']:not([class*='components-']) { // `min-block-size` (not a fixed `block-size`) so controls floor at the token height but // can still flex-stretch where the layout calls for it (e.g. the 54px cloud search bar). - min-block-size: theme.$control-height; + min-block-size: var(--cs-control-height); padding-block: 0; padding-inline: 8px; - border: 1px solid theme.$control-border; - border-radius: theme.$control-radius; + border: 1px solid var(--cs-color-border); + border-radius: var(--cs-control-radius); line-height: 2; - color: theme.$control-text; + color: var(--cs-color-text); font-size: 14px; &:focus { - border-color: theme.$accent; - box-shadow: 0 0 0 1px theme.$accent; + border-color: var(--cs-color-accent); + box-shadow: 0 0 0 1px var(--cs-color-accent); outline: 2px solid transparent; } } @@ -58,21 +58,21 @@ // side. WP 7.0 keeps the chevron as a background image; clamping both paddings to 8px (as // for text inputs) would let the arrow overlap the text, so the inline-end padding is wider. select:not([class*='components-']) { - min-block-size: theme.$control-height; + min-block-size: var(--cs-control-height); padding-block: 0; padding-inline: 8px 24px; - border: 1px solid theme.$control-border; - border-radius: theme.$control-radius; + border: 1px solid var(--cs-color-border); + border-radius: var(--cs-control-radius); // WP 7.0 leaves a tall (≈38px) line-height on selects, pushing the value off-centre. // Pin it so the text stays vertically centred within the token height. line-height: 2; - color: theme.$control-text; + color: var(--cs-color-text); font-size: 14px; &:focus { - border-color: theme.$accent; - box-shadow: 0 0 0 1px theme.$accent; + border-color: var(--cs-color-accent); + box-shadow: 0 0 0 1px var(--cs-color-accent); outline: 2px solid transparent; } } @@ -84,21 +84,21 @@ .button:not(.button-primary, .button-link), .button-secondary, .page-title-action { - min-block-size: theme.$control-height; + min-block-size: var(--cs-control-height); padding-block: 0; padding-inline: 12px; - border-radius: theme.$control-radius; + border-radius: var(--cs-control-radius); line-height: 2.5715; font-size: 14px; font-weight: 400; background: #f6f7f7; - border-color: theme.$accent; - color: theme.$accent; + border-color: var(--cs-color-accent); + color: var(--cs-color-accent); &:hover:not(:disabled) { background: #f0f0f1; - border-color: theme.$accent-hover; - color: theme.$accent-hover; + border-color: var(--cs-color-accent-hover); + color: var(--cs-color-accent-hover); } &:focus:not(:disabled) { @@ -113,27 +113,27 @@ // matches the secondary rule's specificity so a `.button-small.button-primary` // keeps the token height instead of collapsing to the 26px small size. .button-primary:not(.button-link) { - min-block-size: theme.$control-height; + min-block-size: var(--cs-control-height); padding-block: 0; padding-inline: 12px; - border-radius: theme.$control-radius; + border-radius: var(--cs-control-radius); line-height: 2.5715; font-size: 14px; font-weight: 700; - background: theme.$accent; - border-color: theme.$accent; + background: var(--cs-color-accent); + border-color: var(--cs-color-accent); color: #fff; &:hover:not(:disabled) { - background: theme.$accent-hover; - border-color: theme.$accent-hover; + background: var(--cs-color-accent-hover); + border-color: var(--cs-color-accent-hover); color: #fff; } &:focus:not(:disabled) { - background: theme.$accent-hover; - border-color: theme.$accent-hover; - box-shadow: 0 0 0 1px #fff, 0 0 0 3px theme.$accent; + background: var(--cs-color-accent-hover); + border-color: var(--cs-color-accent-hover); + box-shadow: 0 0 0 1px #fff, 0 0 0 3px var(--cs-color-accent); outline: 2px solid transparent; } } diff --git a/src/css/manage/_snippets-table.scss b/src/css/manage/_snippets-table.scss index d4a3de071..fa23d4a2f 100644 --- a/src/css/manage/_snippets-table.scss +++ b/src/css/manage/_snippets-table.scss @@ -173,7 +173,10 @@ } .snippet-card-modified { - color: #646970; + color: var(--cs-color-text-muted); + } + + &.has-tags .snippet-card-modified { margin-inline-start: auto; } } diff --git a/src/js/components/ManageMenu/SnippetsTable/ManageSnippetCard.tsx b/src/js/components/ManageMenu/SnippetsTable/ManageSnippetCard.tsx index 9c36fd6a1..592af284e 100644 --- a/src/js/components/ManageMenu/SnippetsTable/ManageSnippetCard.tsx +++ b/src/js/components/ManageMenu/SnippetsTable/ManageSnippetCard.tsx @@ -211,7 +211,7 @@ export const ManageSnippetCard: React.FC = ({ {0 < snippet.tags.length || snippet.modified - ?
+ ?
{0 < snippet.tags.length ? {__('Tags:', 'code-snippets')} diff --git a/src/js/components/ManageMenu/SnippetsTable/SnippetsTable.tsx b/src/js/components/ManageMenu/SnippetsTable/SnippetsTable.tsx index 8bcc05701..b9392daa8 100644 --- a/src/js/components/ManageMenu/SnippetsTable/SnippetsTable.tsx +++ b/src/js/components/ManageMenu/SnippetsTable/SnippetsTable.tsx @@ -136,7 +136,7 @@ const SnippetsTableInner = () => { currentType ? SNIPPET_TYPE_LABELS[currentType] : __('All Snippets', 'code-snippets') )} - {__('Create new Snippet', 'code-snippets')} + {__('Create New', 'code-snippets')}
From 211d1c69aee6b1efe746331e139ef07240bc3256 Mon Sep 17 00:00:00 2001 From: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Date: Wed, 5 Aug 2026 06:53:41 +0000 Subject: [PATCH 02/49] feat: refine snippets navigation --- src/css/common/_kebab-menu.scss | 1 + src/css/common/_subnav.scss | 73 ++++++++++++------- src/css/common/_toolbar.scss | 23 +++--- .../SnippetsTable/SnippetsTable.tsx | 68 ++++++----------- src/js/hooks/useHorizontalScrollOverflow.ts | 52 +++++++++++++ src/js/types/Window.ts | 1 - .../Admin/Menus/Manage/Manage_Menu_Assets.php | 21 ------ .../Menus/Manage/Manage_Menu_Assets_Test.php | 30 -------- 8 files changed, 135 insertions(+), 134 deletions(-) create mode 100644 src/js/hooks/useHorizontalScrollOverflow.ts diff --git a/src/css/common/_kebab-menu.scss b/src/css/common/_kebab-menu.scss index 89cc93b16..4666b859d 100644 --- a/src/css/common/_kebab-menu.scss +++ b/src/css/common/_kebab-menu.scss @@ -45,6 +45,7 @@ background: #fff; border: 1px solid #e2e2e4; border-radius: 5px; + overflow: hidden; box-shadow: 0 3px 16px rgb(0 0 0 / 15%); &.kebab-menu-popover-top { diff --git a/src/css/common/_subnav.scss b/src/css/common/_subnav.scss index d9d4bf03c..cc193c6cd 100644 --- a/src/css/common/_subnav.scss +++ b/src/css/common/_subnav.scss @@ -3,46 +3,63 @@ // Snippet-type navigation: a boxed segmented control on a white band, with // each tab separated by a vertical rule and the active tab shown as a // filled tile. -.snippet-type-nav { - // The tab band scrolls sideways instead of wrapping at any width. - overflow-x: auto; - scrollbar-width: thin; - background: #fff; - - // The toolbar band above already draws the 1px separator, so only a - // bottom border here — a top border would double the line weight. - border-block-end: 1px solid #e2e2e4; - min-block-size: 53px; +.snippet-type-nav-wrapper { + position: relative; - // Bleed across the full admin content width, flush against the plugin - // toolbar above: counteract the .wrap top margin (10px) plus its 2px / - // 20px inline margins and the #wpcontent 20px inline padding. + // Bleed across the full admin content width, flush against the plugin toolbar. box-sizing: border-box; + min-block-size: 53px; margin-block-start: -10px; margin-inline-start: -22px; inline-size: calc(100% + 42px); + overflow: hidden; + background: #fff; + border-block-end: 1px solid #e2e2e4; + + &::before, + &::after { + position: absolute; + inset-block: 0; + z-index: 1; + inline-size: 32px; + content: ''; + opacity: 0; + pointer-events: none; + transition: opacity 150ms ease; + } + + &::before { + inset-inline-start: 0; + background: linear-gradient(to right, #fff, transparent); + } + + &::after { + inset-inline-end: 0; + background: linear-gradient(to left, #fff, transparent); + } + + &.has-scroll-start::before, + &.has-scroll-end::after { + opacity: 1; + } - // Core admin switches to 10px #wpcontent padding, 0/12px .wrap margins - // below 782px; mirror them so the bar stays flush without overflowing - // the viewport. The tabs scroll horizontally on a single row rather than - // stacking, with the scrollbar chrome hidden. @media (width <= 782px) { margin-inline-start: -10px; inline-size: calc(100% + 22px); - overflow-x: auto; + } +} + +.snippet-type-nav { + // The tab band scrolls sideways instead of wrapping at any width. + overflow-x: auto; + scrollbar-width: thin; + + @media (width <= 782px) { scrollbar-width: none; &::-webkit-scrollbar { display: none; } - - ul { - flex-wrap: nowrap; - } - - .snippet-type-link { - white-space: nowrap; - } } ul { @@ -57,6 +74,7 @@ li { display: flex; + flex: 0 0 auto; margin: 0; } @@ -85,6 +103,7 @@ font-size: 14px; font-weight: 600; line-height: 1.5; + white-space: nowrap; color: #646970; text-decoration: none; box-sizing: border-box; @@ -237,7 +256,7 @@ // When an admin notice pushes the page content down, restore the gap and top // border above the subnav bar. -.notice + .wrap .snippet-type-nav { +.notice + .wrap .snippet-type-nav-wrapper { margin-block-start: 20px; border-block-start: 1px solid theme.$control-border; } diff --git a/src/css/common/_toolbar.scss b/src/css/common/_toolbar.scss index e2bda2072..7910c784c 100644 --- a/src/css/common/_toolbar.scss +++ b/src/css/common/_toolbar.scss @@ -149,22 +149,27 @@ $wpcontent-inline-start-indent: 20px; } } - // Mobile: the primary navigation scrolls horizontally on its band rather - // than wrapping, with the scrollbar chrome hidden. + // On narrow screens Settings remains available through the WordPress submenu. + // The remaining primary destinations fill the toolbar evenly rather than + // becoming a second horizontally-scrolling navigation row. @media (width <= 782px) { - overflow-x: auto; - scrollbar-width: none; - - &::-webkit-scrollbar { + li.toolbar-end-item { display: none; } - ul { - flex-wrap: nowrap; + li:not(.toolbar-end-item) { + flex: 1 1 0; + min-inline-size: 0; + } + + li:not(.toolbar-end-item) + li.toolbar-end-item { + margin-inline-start: 0; } li a { - white-space: nowrap; + inline-size: 100%; + padding-inline: 8px; + white-space: normal; } } } diff --git a/src/js/components/ManageMenu/SnippetsTable/SnippetsTable.tsx b/src/js/components/ManageMenu/SnippetsTable/SnippetsTable.tsx index b9392daa8..cc69d192e 100644 --- a/src/js/components/ManageMenu/SnippetsTable/SnippetsTable.tsx +++ b/src/js/components/ManageMenu/SnippetsTable/SnippetsTable.tsx @@ -1,14 +1,15 @@ import { __, sprintf } from '@wordpress/i18n' import { createInterpolateElement } from '@wordpress/element' -import React, { useCallback, useMemo } from 'react' +import React from 'react' import classnames from 'classnames' +import { useHorizontalScrollOverflow } from '../../../hooks/useHorizontalScrollOverflow' import { WithRestAPIContext } from '../../../hooks/useRestAPI' import { useSnippetView } from '../../../hooks/useSnippetView' import { WithSnippetsAPIContext } from '../../../hooks/useSnippetsAPI' -import { WithSnippetsListContext, useSnippetsList } from '../../../hooks/useSnippetsList' +import { WithSnippetsListContext } from '../../../hooks/useSnippetsList' import { SNIPPET_TYPES } from '../../../types/Snippet' import { isLicensed } from '../../../utils/screen' -import { SNIPPET_TYPE_LABELS, getSnippetAddNewUrl, getSnippetType, isProType } from '../../../utils/snippets/snippets' +import { SNIPPET_TYPE_LABELS, getSnippetAddNewUrl, isProType } from '../../../utils/snippets/snippets' import { buildUrl } from '../../../utils/urls' import { Badge } from '../../common/Badge' import { Notice } from '../../common/Notice' @@ -21,10 +22,9 @@ import type { SnippetType } from '../../../types/Snippet' interface SnippetTypeTabProps { type?: SnippetType - count?: number } -const SnippetTypeTab: React.FC = ({ type, count }) => { +const SnippetTypeTab: React.FC = ({ type }) => { const { currentType, setCurrentType } = useSnippetsFilters() const tabName = type ?? 'all' @@ -51,7 +51,6 @@ const SnippetTypeTab: React.FC = ({ type, count }) => { {__('All', 'code-snippets')} } - {count && {count}} {type && isProType(type) && !isLicensed() && {__('Pro', 'code-snippets')}} @@ -79,53 +78,30 @@ const SafeModeNotice = () => : null -// Counts render immediately from the values localized with the page, then -// switch to live values derived from the snippets list once it has loaded. -const useSnippetTypeCounts = () => { - const { snippetsList } = useSnippetsList() - - const countedSnippets = useMemo( - () => snippetsList?.filter(snippet => !snippet.trashed), - [snippetsList] - ) - - const typeCounts = useMemo( - () => countedSnippets?.reduce((counts, snippet) => { - const type = getSnippetType(snippet) - return counts.set(type, (counts.get(type) ?? 0) + 1) - }, new Map()), - [countedSnippets] - ) - - const localized = window.CODE_SNIPPETS_MANAGE?.typeCounts - - const getCount = useCallback( - (type?: SnippetType) => countedSnippets - ? type ? typeCounts?.get(type) ?? 0 : countedSnippets.length - : localized?.[type ?? 'all'], - [countedSnippets, typeCounts, localized] - ) - - return { getCount } -} - const SnippetsTableInner = () => { const { snippetView, setSnippetView } = useSnippetView() const { currentType } = useSnippetsFilters() - const { getCount } = useSnippetTypeCounts() + const { atStart, atEnd, scrollRef } = useHorizontalScrollOverflow() return ( <> - + +
diff --git a/src/js/hooks/useHorizontalScrollOverflow.ts b/src/js/hooks/useHorizontalScrollOverflow.ts new file mode 100644 index 000000000..23535b6b5 --- /dev/null +++ b/src/js/hooks/useHorizontalScrollOverflow.ts @@ -0,0 +1,52 @@ +import { useCallback, useEffect, useRef, useState } from 'react' + +interface HorizontalScrollOverflow { + atStart: boolean + atEnd: boolean + scrollRef: React.RefObject +} + +const EDGE_TOLERANCE = 1 + +export const useHorizontalScrollOverflow = (): HorizontalScrollOverflow => { + const scrollRef = useRef(null) + const [position, setPosition] = useState({ atStart: true, atEnd: true }) + + const updatePosition = useCallback(() => { + const element = scrollRef.current + + if (!element) { + return + } + + const hasOverflow = element.scrollWidth - element.clientWidth > EDGE_TOLERANCE + const atStart = !hasOverflow || element.scrollLeft <= EDGE_TOLERANCE + const atEnd = !hasOverflow || element.scrollLeft + element.clientWidth >= element.scrollWidth - EDGE_TOLERANCE + + setPosition(previous => previous.atStart === atStart && previous.atEnd === atEnd + ? previous + : { atStart, atEnd }) + }, []) + + useEffect(() => { + const element = scrollRef.current + + if (!element) { + return + } + + updatePosition() + element.addEventListener('scroll', updatePosition, { passive: true }) + + const observer = new ResizeObserver(updatePosition) + observer.observe(element) + observer.observe(element.firstElementChild ?? element) + + return () => { + element.removeEventListener('scroll', updatePosition) + observer.disconnect() + } + }, [updatePosition]) + + return { ...position, scrollRef } +} diff --git a/src/js/types/Window.ts b/src/js/types/Window.ts index 2d8b084a4..575cf8a36 100644 --- a/src/js/types/Window.ts +++ b/src/js/types/Window.ts @@ -67,7 +67,6 @@ declare global { hiddenColumns: string[] truncateRowValues: number | string snippetsPerPage: number - typeCounts?: Record cloudSearchPerPage: number isSafeModeActive: boolean bulkDownloadNonce: string diff --git a/src/php/Admin/Menus/Manage/Manage_Menu_Assets.php b/src/php/Admin/Menus/Manage/Manage_Menu_Assets.php index 250f23386..085c50b2c 100644 --- a/src/php/Admin/Menus/Manage/Manage_Menu_Assets.php +++ b/src/php/Admin/Menus/Manage/Manage_Menu_Assets.php @@ -41,26 +41,6 @@ public function __construct( Manage_Menu_Screen_Options $screen_options ) { $this->screen_options = $screen_options; } - /** - * Count non-trashed snippets by type. - * - * @return array Map of type name to snippet count, including 'all'. - */ - private function get_snippet_type_counts(): array { - $counts = [ 'all' => 0 ]; - - foreach ( get_snippets() as $snippet ) { - if ( $snippet->trashed ) { - continue; - } - - $counts[ $snippet->type ] = ( $counts[ $snippet->type ] ?? 0 ) + 1; - ++$counts['all']; - } - - return $counts; - } - /** * Enqueue manage assets and localize their runtime data. * @@ -99,7 +79,6 @@ public function enqueue( array $script_dependencies, array $style_dependencies ) 'bulkDownloadNonce' => wp_create_nonce( 'code_snippets_bulk_download' ), 'supportsZipDownloads' => class_exists( 'ZipArchive' ), 'editorTheme' => get_setting( 'editor', 'theme' ), - 'typeCounts' => $this->get_snippet_type_counts(), ]; if ( $this->screen_options->is_manage_table_view() ) { diff --git a/tests/unit/Admin/Menus/Manage/Manage_Menu_Assets_Test.php b/tests/unit/Admin/Menus/Manage/Manage_Menu_Assets_Test.php index e7b476ad3..27cff23f8 100644 --- a/tests/unit/Admin/Menus/Manage/Manage_Menu_Assets_Test.php +++ b/tests/unit/Admin/Menus/Manage/Manage_Menu_Assets_Test.php @@ -5,12 +5,9 @@ use Code_Snippets\Admin\Menus\Admin_Menu; use Code_Snippets\AdminUnitTestCase; use Code_Snippets\Model\Snippet; -use Code_Snippets\UnitTestCase; -use WP_UnitTest_Factory; use function Code_Snippets\code_snippets; use function Code_Snippets\get_snippets; use function Code_Snippets\save_snippet; -use function Code_Snippets\trash_snippet; /** * Tests for manage menu assets and localized data. @@ -53,7 +50,6 @@ public function test_enqueue_localizes_manage_data(): void { 'bulkDownloadNonce', 'supportsZipDownloads', 'editorTheme', - 'typeCounts', 'snippetsList', ], array_keys( $localized ) @@ -150,32 +146,6 @@ public function test_enqueue_omits_snippets_above_inline_limit(): void { $this->assertArrayNotHasKey( 'snippetsList', $localized ); } - /** - * Compatible scopes are combined and trashed snippets are excluded. - * - * @return void - */ - public function test_count_returns_non_trashed_counts_by_snippet_type(): void { - $this->enqueue_assets(); - $before = $this->get_localized_data(); - - save_snippet( new Snippet( [ 'scope' => 'global' ] ) ); - save_snippet( new Snippet( [ 'scope' => 'admin' ] ) ); - save_snippet( new Snippet( [ 'scope' => 'content' ] ) ); - $trashed = save_snippet( new Snippet( [ 'scope' => 'site-css' ] ) ); - - $this->assertInstanceOf( Snippet::class, $trashed ); - trash_snippet( $trashed->id ); - - $this->enqueue_assets(); - $localized = $this->get_localized_data(); - - $this->assertSame( ( $before['typeCounts']['all'] ?? 0 ) + 3, $localized['typeCounts']['all'] ); - $this->assertSame( ( $before['typeCounts']['php'] ?? 0 ) + 2, $localized['typeCounts']['php'] ); - $this->assertSame( ( $before['typeCounts']['html'] ?? 0 ) + 1, $localized['typeCounts']['html'] ); - $this->assertArrayNotHasKey( 'css', $localized['typeCounts'], 'css' ); - } - /** * Enqueue assets through the extracted service. * From 69a4ef9849c78f77f6b1f20aa6ecd20846d31791 Mon Sep 17 00:00:00 2001 From: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Date: Wed, 5 Aug 2026 07:19:04 +0000 Subject: [PATCH 03/49] fix: retain Core tab counts --- .../SnippetsTable/SnippetsTable.tsx | 46 ++++++++++++++++--- src/js/types/Window.ts | 1 + .../Admin/Menus/Manage/Manage_Menu_Assets.php | 21 +++++++++ .../Menus/Manage/Manage_Menu_Assets_Test.php | 30 ++++++++++++ 4 files changed, 92 insertions(+), 6 deletions(-) diff --git a/src/js/components/ManageMenu/SnippetsTable/SnippetsTable.tsx b/src/js/components/ManageMenu/SnippetsTable/SnippetsTable.tsx index cc69d192e..3b194b836 100644 --- a/src/js/components/ManageMenu/SnippetsTable/SnippetsTable.tsx +++ b/src/js/components/ManageMenu/SnippetsTable/SnippetsTable.tsx @@ -1,15 +1,15 @@ import { __, sprintf } from '@wordpress/i18n' import { createInterpolateElement } from '@wordpress/element' -import React from 'react' +import React, { useCallback, useMemo } from 'react' import classnames from 'classnames' import { useHorizontalScrollOverflow } from '../../../hooks/useHorizontalScrollOverflow' import { WithRestAPIContext } from '../../../hooks/useRestAPI' import { useSnippetView } from '../../../hooks/useSnippetView' import { WithSnippetsAPIContext } from '../../../hooks/useSnippetsAPI' -import { WithSnippetsListContext } from '../../../hooks/useSnippetsList' +import { WithSnippetsListContext, useSnippetsList } from '../../../hooks/useSnippetsList' import { SNIPPET_TYPES } from '../../../types/Snippet' import { isLicensed } from '../../../utils/screen' -import { SNIPPET_TYPE_LABELS, getSnippetAddNewUrl, isProType } from '../../../utils/snippets/snippets' +import { SNIPPET_TYPE_LABELS, getSnippetAddNewUrl, getSnippetType, isProType } from '../../../utils/snippets/snippets' import { buildUrl } from '../../../utils/urls' import { Badge } from '../../common/Badge' import { Notice } from '../../common/Notice' @@ -22,11 +22,13 @@ import type { SnippetType } from '../../../types/Snippet' interface SnippetTypeTabProps { type?: SnippetType + count?: number } -const SnippetTypeTab: React.FC = ({ type }) => { +const SnippetTypeTab: React.FC = ({ type, count }) => { const { currentType, setCurrentType } = useSnippetsFilters() const tabName = type ?? 'all' + const showCount = count !== undefined && (isLicensed() || !type || !isProType(type)) return (
  • @@ -51,6 +53,7 @@ const SnippetTypeTab: React.FC = ({ type }) => { {__('All', 'code-snippets')} } + {showCount && {count}} {type && isProType(type) && !isLicensed() && {__('Pro', 'code-snippets')}}
  • @@ -78,9 +81,40 @@ const SafeModeNotice = () => : null +// Counts render immediately from the values localized with the page, then +// switch to live values derived from the snippets list once it has loaded. +const useSnippetTypeCounts = () => { + const { snippetsList } = useSnippetsList() + + const countedSnippets = useMemo( + () => snippetsList?.filter(snippet => !snippet.trashed), + [snippetsList] + ) + + const typeCounts = useMemo( + () => countedSnippets?.reduce((counts, snippet) => { + const type = getSnippetType(snippet) + return counts.set(type, (counts.get(type) ?? 0) + 1) + }, new Map()), + [countedSnippets] + ) + + const localized = window.CODE_SNIPPETS_MANAGE?.typeCounts + + const getCount = useCallback( + (type?: SnippetType) => countedSnippets + ? type ? typeCounts?.get(type) ?? 0 : countedSnippets.length + : localized?.[type ?? 'all'], + [countedSnippets, typeCounts, localized] + ) + + return { getCount } +} + const SnippetsTableInner = () => { const { snippetView, setSnippetView } = useSnippetView() const { currentType } = useSnippetsFilters() + const { getCount } = useSnippetTypeCounts() const { atStart, atEnd, scrollRef } = useHorizontalScrollOverflow() return ( @@ -97,8 +131,8 @@ const SnippetsTableInner = () => { aria-label={__('Snippet types', 'code-snippets')} >
      - - {SNIPPET_TYPES.map(type => )} + + {SNIPPET_TYPES.map(type => )}
    diff --git a/src/js/types/Window.ts b/src/js/types/Window.ts index 575cf8a36..2d8b084a4 100644 --- a/src/js/types/Window.ts +++ b/src/js/types/Window.ts @@ -67,6 +67,7 @@ declare global { hiddenColumns: string[] truncateRowValues: number | string snippetsPerPage: number + typeCounts?: Record cloudSearchPerPage: number isSafeModeActive: boolean bulkDownloadNonce: string diff --git a/src/php/Admin/Menus/Manage/Manage_Menu_Assets.php b/src/php/Admin/Menus/Manage/Manage_Menu_Assets.php index 085c50b2c..250f23386 100644 --- a/src/php/Admin/Menus/Manage/Manage_Menu_Assets.php +++ b/src/php/Admin/Menus/Manage/Manage_Menu_Assets.php @@ -41,6 +41,26 @@ public function __construct( Manage_Menu_Screen_Options $screen_options ) { $this->screen_options = $screen_options; } + /** + * Count non-trashed snippets by type. + * + * @return array Map of type name to snippet count, including 'all'. + */ + private function get_snippet_type_counts(): array { + $counts = [ 'all' => 0 ]; + + foreach ( get_snippets() as $snippet ) { + if ( $snippet->trashed ) { + continue; + } + + $counts[ $snippet->type ] = ( $counts[ $snippet->type ] ?? 0 ) + 1; + ++$counts['all']; + } + + return $counts; + } + /** * Enqueue manage assets and localize their runtime data. * @@ -79,6 +99,7 @@ public function enqueue( array $script_dependencies, array $style_dependencies ) 'bulkDownloadNonce' => wp_create_nonce( 'code_snippets_bulk_download' ), 'supportsZipDownloads' => class_exists( 'ZipArchive' ), 'editorTheme' => get_setting( 'editor', 'theme' ), + 'typeCounts' => $this->get_snippet_type_counts(), ]; if ( $this->screen_options->is_manage_table_view() ) { diff --git a/tests/unit/Admin/Menus/Manage/Manage_Menu_Assets_Test.php b/tests/unit/Admin/Menus/Manage/Manage_Menu_Assets_Test.php index 27cff23f8..e7b476ad3 100644 --- a/tests/unit/Admin/Menus/Manage/Manage_Menu_Assets_Test.php +++ b/tests/unit/Admin/Menus/Manage/Manage_Menu_Assets_Test.php @@ -5,9 +5,12 @@ use Code_Snippets\Admin\Menus\Admin_Menu; use Code_Snippets\AdminUnitTestCase; use Code_Snippets\Model\Snippet; +use Code_Snippets\UnitTestCase; +use WP_UnitTest_Factory; use function Code_Snippets\code_snippets; use function Code_Snippets\get_snippets; use function Code_Snippets\save_snippet; +use function Code_Snippets\trash_snippet; /** * Tests for manage menu assets and localized data. @@ -50,6 +53,7 @@ public function test_enqueue_localizes_manage_data(): void { 'bulkDownloadNonce', 'supportsZipDownloads', 'editorTheme', + 'typeCounts', 'snippetsList', ], array_keys( $localized ) @@ -146,6 +150,32 @@ public function test_enqueue_omits_snippets_above_inline_limit(): void { $this->assertArrayNotHasKey( 'snippetsList', $localized ); } + /** + * Compatible scopes are combined and trashed snippets are excluded. + * + * @return void + */ + public function test_count_returns_non_trashed_counts_by_snippet_type(): void { + $this->enqueue_assets(); + $before = $this->get_localized_data(); + + save_snippet( new Snippet( [ 'scope' => 'global' ] ) ); + save_snippet( new Snippet( [ 'scope' => 'admin' ] ) ); + save_snippet( new Snippet( [ 'scope' => 'content' ] ) ); + $trashed = save_snippet( new Snippet( [ 'scope' => 'site-css' ] ) ); + + $this->assertInstanceOf( Snippet::class, $trashed ); + trash_snippet( $trashed->id ); + + $this->enqueue_assets(); + $localized = $this->get_localized_data(); + + $this->assertSame( ( $before['typeCounts']['all'] ?? 0 ) + 3, $localized['typeCounts']['all'] ); + $this->assertSame( ( $before['typeCounts']['php'] ?? 0 ) + 2, $localized['typeCounts']['php'] ); + $this->assertSame( ( $before['typeCounts']['html'] ?? 0 ) + 1, $localized['typeCounts']['html'] ); + $this->assertArrayNotHasKey( 'css', $localized['typeCounts'], 'css' ); + } + /** * Enqueue assets through the extracted service. * From 5ee3624a352d2a6114b1ae0c9daff88a09549c3b Mon Sep 17 00:00:00 2001 From: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Date: Wed, 5 Aug 2026 07:54:36 +0000 Subject: [PATCH 04/49] fix: hide locked tab counts in Core --- src/js/components/ManageMenu/SnippetsTable/SnippetsTable.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/components/ManageMenu/SnippetsTable/SnippetsTable.tsx b/src/js/components/ManageMenu/SnippetsTable/SnippetsTable.tsx index 3b194b836..7f4c295e4 100644 --- a/src/js/components/ManageMenu/SnippetsTable/SnippetsTable.tsx +++ b/src/js/components/ManageMenu/SnippetsTable/SnippetsTable.tsx @@ -28,7 +28,7 @@ interface SnippetTypeTabProps { const SnippetTypeTab: React.FC = ({ type, count }) => { const { currentType, setCurrentType } = useSnippetsFilters() const tabName = type ?? 'all' - const showCount = count !== undefined && (isLicensed() || !type || !isProType(type)) + const showCount = count !== undefined && (!type || 'php' === type || 'html' === type) return (
  • From 3924ae3219e7d91a1769bc1089716f481807cf00 Mon Sep 17 00:00:00 2001 From: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Date: Wed, 5 Aug 2026 08:04:22 +0000 Subject: [PATCH 05/49] fix: version Manage assets by build time --- src/php/Admin/Menus/Manage/Manage_Menu_Assets.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/php/Admin/Menus/Manage/Manage_Menu_Assets.php b/src/php/Admin/Menus/Manage/Manage_Menu_Assets.php index 250f23386..da37eb358 100644 --- a/src/php/Admin/Menus/Manage/Manage_Menu_Assets.php +++ b/src/php/Admin/Menus/Manage/Manage_Menu_Assets.php @@ -70,18 +70,24 @@ private function get_snippet_type_counts(): array { * @return void */ public function enqueue( array $script_dependencies, array $style_dependencies ): void { + $plugin_dir = plugin_dir_path( PLUGIN_FILE ); + $css_version = filemtime( $plugin_dir . 'dist/manage.css' ); + $js_version = filemtime( $plugin_dir . 'dist/manage.js' ); + $css_version = false !== $css_version ? $css_version : PLUGIN_VERSION; + $js_version = false !== $js_version ? $js_version : PLUGIN_VERSION; + wp_enqueue_style( self::CSS_HANDLE, plugins_url( 'dist/manage.css', PLUGIN_FILE ), $style_dependencies, - PLUGIN_VERSION + $css_version ); wp_enqueue_script( self::JS_HANDLE, plugins_url( 'dist/manage.js', PLUGIN_FILE ), $script_dependencies, - PLUGIN_VERSION, + $js_version, [ 'in_footer' => true ] ); From bd09095a6ad9066a6fc583b9bd3b9596d83c1d02 Mon Sep 17 00:00:00 2001 From: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Date: Wed, 5 Aug 2026 08:16:20 +0000 Subject: [PATCH 06/49] fix: refine settings navigation --- src/css/common/_toolbar.scss | 46 ++++++++++++++++++++ src/css/settings.scss | 60 +++++++++++++++++++++++++++ src/js/components/common/Toolbar.tsx | 2 +- src/php/Admin/Menus/Settings_Menu.php | 11 +++-- 4 files changed, 115 insertions(+), 4 deletions(-) diff --git a/src/css/common/_toolbar.scss b/src/css/common/_toolbar.scss index 7910c784c..cc0eb8b1d 100644 --- a/src/css/common/_toolbar.scss +++ b/src/css/common/_toolbar.scss @@ -68,6 +68,52 @@ $wpcontent-inline-start-indent: 20px; color: inherit; text-decoration: none; } + + @media (width <= 960px) { + padding-inline: 16px; + gap: 12px; + + .logo { + flex: 0 1 auto; + min-inline-size: 0; + + div { + white-space: nowrap; + } + } + + nav { + margin-inline-start: auto; + } + + ul { + gap: 0; + } + + li:not(.toolbar-upgrade-item) { + display: none; + } + + .toolbar-upgrade-item .button { + white-space: nowrap; + } + } + + @media (width <= 480px) { + padding-inline: 10px; + + .logo img { + block-size: 34px; + } + + .logo div { + display: none; + } + + .toolbar-upgrade-item .button { + padding-inline: 10px; + } + } } .code-snippets-toolbar-lower { diff --git a/src/css/settings.scss b/src/css/settings.scss index 9622512d1..66d1c7ea8 100644 --- a/src/css/settings.scss +++ b/src/css/settings.scss @@ -242,5 +242,65 @@ body.js { } #settings-sections-tabs { + display: flex; + align-items: stretch; margin-block-end: 15px; + padding: 0; + background: #fff; + border-block-end: 1px solid theme.$control-border; + + .snippet-type-link { + display: flex; + align-items: center; + min-block-size: 52px; + margin: 0; + padding-inline: 24px; + color: #646970; + font-size: 14px; + font-weight: 600; + line-height: 1.5; + border: 0; + border-inline-end: 1px solid #e2e2e4; + background: transparent; + + &:hover, + &:focus { + color: #2c3337; + background: #f6f7f7; + } + + &.active-type { + position: relative; + z-index: 1; + margin-block-end: -1px; + color: theme.$accent; + background: #f0f0f1; + border-color: #f0f0f1; + } + } +} + +@media (width <= 782px) { + body.js { + #settings-sections-tabs { + display: none; + } + + .settings-section-title { + position: static; + inline-size: auto; + block-size: auto; + margin: 0 0 16px; + overflow: visible; + clip: auto; + clip-path: none; + font-size: 20px; + line-height: 1.3; + } + + .settings-section { + display: block !important; + margin-block-end: 40px; + } + } } diff --git a/src/js/components/common/Toolbar.tsx b/src/js/components/common/Toolbar.tsx index 8e4c8fddf..bcfe49cda 100644 --- a/src/js/components/common/Toolbar.tsx +++ b/src/js/components/common/Toolbar.tsx @@ -116,7 +116,7 @@ const UpperNav: React.FC = ({ setIsUpsellDialogOpen }) =>
  • )} {shouldShowUpsell() && ( -
  • +
  • enqueue_codemirror(); - $handle = 'code-snippets-settings'; + $handle = 'code-snippets-settings'; + $plugin_dir = plugin_dir_path( PLUGIN_FILE ); + $css_version = filemtime( $plugin_dir . 'dist/settings.css' ); + $js_version = filemtime( $plugin_dir . 'dist/settings.js' ); + $css_version = false !== $css_version ? $css_version : PLUGIN_VERSION; + $js_version = false !== $js_version ? $js_version : PLUGIN_VERSION; wp_enqueue_style( $handle, plugins_url( 'dist/settings.css', PLUGIN_FILE ), self::$style_deps + [ 'code-editor' ], - PLUGIN_VERSION + $css_version ); wp_enqueue_script( $handle, plugins_url( 'dist/settings.js', PLUGIN_FILE ), self::$script_deps + [ 'code-snippets-code-editor' ], - PLUGIN_VERSION, + $js_version, true ); From 99e3aa69620ab853ca01d1062409945dd91dc8f8 Mon Sep 17 00:00:00 2001 From: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Date: Wed, 5 Aug 2026 08:30:32 +0000 Subject: [PATCH 07/49] fix: preserve responsive settings access --- src/css/common/_toolbar.scss | 122 ++++++++++++++++++++------- src/css/settings.scss | 8 +- src/js/components/common/Toolbar.tsx | 21 +++++ 3 files changed, 120 insertions(+), 31 deletions(-) diff --git a/src/css/common/_toolbar.scss b/src/css/common/_toolbar.scss index cc0eb8b1d..d77ae0c74 100644 --- a/src/css/common/_toolbar.scss +++ b/src/css/common/_toolbar.scss @@ -69,36 +69,6 @@ $wpcontent-inline-start-indent: 20px; text-decoration: none; } - @media (width <= 960px) { - padding-inline: 16px; - gap: 12px; - - .logo { - flex: 0 1 auto; - min-inline-size: 0; - - div { - white-space: nowrap; - } - } - - nav { - margin-inline-start: auto; - } - - ul { - gap: 0; - } - - li:not(.toolbar-upgrade-item) { - display: none; - } - - .toolbar-upgrade-item .button { - white-space: nowrap; - } - } - @media (width <= 480px) { padding-inline: 10px; @@ -257,3 +227,95 @@ $wpcontent-inline-start-indent: 20px; .code-snippets-return-link { float: inline-end; } + +.code-snippets-toolbar-upper .toolbar-more-item { + display: none; +} + +.code-snippets-toolbar-upper .toolbar-more-menu { + position: relative; +} + +.code-snippets-toolbar-upper .toolbar-more-menu summary { + cursor: pointer; +} + +.code-snippets-toolbar-upper .toolbar-more-menu ul { + position: absolute; + inset-block-start: calc(100% + 8px); + inset-inline-end: 0; + z-index: 2; + display: grid; + min-inline-size: 180px; + block-size: auto; + margin: 0; + padding: 8px; + background: #fff; + border: 1px solid theme.$control-border; + box-shadow: 0 3px 8px rgb(0 0 0 / 15%); +} + +.code-snippets-toolbar-upper .toolbar-more-menu li { + margin: 0; +} + +// The upper and lower toolbars are distinct navigation trees; keep this rule +// after the lower-toolbar link state rules so the compact menu owns its hover state. +// stylelint-disable-next-line no-descending-specificity +.code-snippets-toolbar-upper .toolbar-more-menu a { + display: block; + padding: 8px; + white-space: nowrap; +} + +.code-snippets-toolbar-upper .toolbar-more-menu a:hover, +.code-snippets-toolbar-upper .toolbar-more-menu a:focus { + background: #f6f7f7; +} + +@media (width <= 960px) { + .code-snippets-toolbar-upper { + gap: 12px; + padding-inline: 16px; + } + + .code-snippets-toolbar-upper .logo { + flex: 0 1 auto; + min-inline-size: 0; + } + + .code-snippets-toolbar-upper nav { + margin-inline-start: auto; + } + + .code-snippets-toolbar-upper > nav > ul { + gap: 8px; + } + + .code-snippets-toolbar-upper > nav > ul > li:not(.toolbar-upgrade-item, .toolbar-more-item) { + display: none; + } + + .code-snippets-toolbar-upper .toolbar-more-item { + display: flex; + } + + .code-snippets-toolbar-upper .toolbar-more-menu summary { + padding: 8px; + white-space: nowrap; + } + + .code-snippets-toolbar-upper .toolbar-upgrade-item .button { + display: block; + max-inline-size: 180px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } +} + +@media (width <= 640px) { + .code-snippets-toolbar-upper .logo div { + display: none; + } +} diff --git a/src/css/settings.scss b/src/css/settings.scss index 66d1c7ea8..0175dc6bb 100644 --- a/src/css/settings.scss +++ b/src/css/settings.scss @@ -269,7 +269,9 @@ body.js { background: #f6f7f7; } - &.active-type { + &.active-type, + &.active-type:hover, + &.active-type:focus { position: relative; z-index: 1; margin-block-end: -1px; @@ -302,5 +304,9 @@ body.js { display: block !important; margin-block-end: 40px; } + + .wrap[data-active-tab='license'] .submit { + display: flex; + } } } diff --git a/src/js/components/common/Toolbar.tsx b/src/js/components/common/Toolbar.tsx index bcfe49cda..83288d1c9 100644 --- a/src/js/components/common/Toolbar.tsx +++ b/src/js/components/common/Toolbar.tsx @@ -90,6 +90,25 @@ interface UpperNavProps { setIsUpsellDialogOpen: (isOpen: boolean) => void } +const MoreNav = () => +
  • +
    + {__('More', 'code-snippets')} + +
    +
  • + const UpperNav: React.FC = ({ setIsUpsellDialogOpen }) =>
    @@ -115,6 +134,8 @@ const UpperNav: React.FC = ({ setIsUpsellDialogOpen }) => )} + + {shouldShowUpsell() && (
  • Date: Wed, 5 Aug 2026 08:52:48 +0000 Subject: [PATCH 08/49] fix: refine mobile table controls --- package-lock.json | 27 ++++++++++++++++++++++ package.json | 1 + src/css/common/_toolbar.scss | 3 +++ src/css/common/list-table/_pagination.scss | 25 +++++++++++++++++--- src/js/components/common/Toolbar.tsx | 6 ++++- 5 files changed, 58 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7f515745a..1ed06ac01 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "GPL-2.0-or-later", "dependencies": { "@codemirror/fold": "^0.19.4", + "@tabler/icons-react": "^3.46.0", "@wordpress/components": "^29.3.0", "@wordpress/date": "^5.43.0", "@wordpress/dom-ready": "^4.17.0", @@ -3031,6 +3032,32 @@ "node": ">=10" } }, + "node_modules/@tabler/icons": { + "version": "3.46.0", + "resolved": "https://registry.npmjs.org/@tabler/icons/-/icons-3.46.0.tgz", + "integrity": "sha512-f2RYFl3fzPwj5WO82x6en0dmkjefxEfOm16D1ByM6cj/McNiwOkL4VaPUoP9VVIrXAD9WnTSVFr70px703b//A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/codecalm" + } + }, + "node_modules/@tabler/icons-react": { + "version": "3.46.0", + "resolved": "https://registry.npmjs.org/@tabler/icons-react/-/icons-react-3.46.0.tgz", + "integrity": "sha512-CCm7xJWhDT2PH4ZIFkP6AgYKtVhq0gpYkjUN+GVh1AzmIQaa77OW0bQPBPQiTE0PsXMR9oSxFqA3qBglzPyrVQ==", + "license": "MIT", + "dependencies": { + "@tabler/icons": "3.46.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/codecalm" + }, + "peerDependencies": { + "react": ">= 16" + } + }, "node_modules/@tannin/compile": { "version": "1.1.0", "license": "MIT", diff --git a/package.json b/package.json index 5de45643c..783c74eb8 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ }, "dependencies": { "@codemirror/fold": "^0.19.4", + "@tabler/icons-react": "^3.46.0", "@wordpress/components": "^29.3.0", "@wordpress/date": "^5.43.0", "@wordpress/dom-ready": "^4.17.0", diff --git a/src/css/common/_toolbar.scss b/src/css/common/_toolbar.scss index d77ae0c74..5015e86c7 100644 --- a/src/css/common/_toolbar.scss +++ b/src/css/common/_toolbar.scss @@ -237,6 +237,9 @@ $wpcontent-inline-start-indent: 20px; } .code-snippets-toolbar-upper .toolbar-more-menu summary { + display: inline-flex; + align-items: center; + gap: 4px; cursor: pointer; } diff --git a/src/css/common/list-table/_pagination.scss b/src/css/common/list-table/_pagination.scss index 4dc8e7943..4624c6186 100644 --- a/src/css/common/list-table/_pagination.scss +++ b/src/css/common/list-table/_pagination.scss @@ -79,6 +79,14 @@ } @media (width <= 782px) { + // Wide table columns scroll inside the results region instead of extending + // the WordPress admin document horizontally. + .snippets-list-view { + max-inline-size: 100%; + overflow-x: auto; + overscroll-behavior-inline: contain; + } + p.search-box { float: inline-start; position: initial; @@ -87,10 +95,21 @@ block-size: auto; } - // Mobile: the search area takes a full row of its own, and the action - // groups stay visible (core admin hides .tablenav.top .actions here). + + // Mobile keeps the filters compact. Pagination remains below the results, + // avoiding a second navigation cluster above the table. + .tablenav.top .tablenav-pages-nav { + display: none; + } + + .tablenav.top .tablenav-end-group { + margin-inline-start: 0; + } + .tablenav .snippets-search-area { - flex-basis: 100%; + flex: 0 1 20rem; + inline-size: min(100%, 20rem); + max-inline-size: 20rem; } .tablenav .alignleft.actions { diff --git a/src/js/components/common/Toolbar.tsx b/src/js/components/common/Toolbar.tsx index 83288d1c9..3da4f2231 100644 --- a/src/js/components/common/Toolbar.tsx +++ b/src/js/components/common/Toolbar.tsx @@ -1,3 +1,4 @@ +import { IconChevronDown } from '@tabler/icons-react' import { __, _x } from '@wordpress/i18n' import classnames from 'classnames' import React, { useState } from 'react' @@ -93,7 +94,10 @@ interface UpperNavProps { const MoreNav = () =>
  • - {__('More', 'code-snippets')} + + {__('More', 'code-snippets')} +
      {UPPER_NAV_LINKS.map(link =>
    • From 8c43df659103e7319b83a46980f6377bd26145ae Mon Sep 17 00:00:00 2001 From: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Date: Wed, 5 Aug 2026 09:38:04 +0000 Subject: [PATCH 09/49] fix: refine editor sidebar controls --- src/css/edit/_form.scss | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/css/edit/_form.scss b/src/css/edit/_form.scss index 59a97a24f..1edb15e39 100644 --- a/src/css/edit/_form.scss +++ b/src/css/edit/_form.scss @@ -54,8 +54,14 @@ $sidebar-gap: 30px; .conditions-editor-header { display: flex; align-items: center; - justify-content: space-between; + justify-content: flex-start; gap: 8px; + margin-block-end: 8px; + + strong { + font-size: 14px; + line-height: 20px; + } } .button.button-large { @@ -64,8 +70,14 @@ $sidebar-gap: 30px; gap: 8px; inline-size: 100%; border-color: #ccc; + background: #fff; padding-block: 6px; padding-inline: 12px; + + &:hover:not(:disabled), + &:focus:not(:disabled) { + background: #f6f7f7; + } } &.no-condition .cond-badge { @@ -116,8 +128,15 @@ $sidebar-gap: 30px; grid-area: span 3 / sidebar; max-inline-size: $sidebar-width; position: sticky; - inset-block-start: 32px; + + // Keep the sidebar clear of the fixed WordPress admin bar and leave a + // usable visual gap beneath it while the editor scrolls. + inset-block-start: 48px; align-self: start; + + @media (width <= 782px) { + position: static; + } } @media (width <= 1024px) { From e45331e1857faa6febc34661ea550001d93da041 Mon Sep 17 00:00:00 2001 From: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Date: Wed, 5 Aug 2026 10:26:51 +0000 Subject: [PATCH 10/49] fix: version editor assets by build time --- src/php/Admin/Menus/Edit_Menu.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/php/Admin/Menus/Edit_Menu.php b/src/php/Admin/Menus/Edit_Menu.php index 85556ac6b..9d3c94ff0 100644 --- a/src/php/Admin/Menus/Edit_Menu.php +++ b/src/php/Admin/Menus/Edit_Menu.php @@ -192,6 +192,10 @@ public function enqueue_assets() { enqueue_code_editor( $this->snippet->type ); + $plugin_dir = plugin_dir_path( PLUGIN_FILE ); + $css_version = filemtime( $plugin_dir . 'dist/edit.css' ); + $js_version = filemtime( $plugin_dir . 'dist/edit.js' ); + wp_enqueue_style( self::CSS_HANDLE, plugins_url( 'dist/edit.css', PLUGIN_FILE ), @@ -199,14 +203,14 @@ public function enqueue_assets() { 'code-editor', 'wp-components', ], - PLUGIN_VERSION + false !== $css_version ? $css_version : PLUGIN_VERSION ); wp_enqueue_script( self::JS_HANDLE, plugins_url( 'dist/edit.js', PLUGIN_FILE ), [ 'code-snippets-code-editor' ] + self::$script_deps, - PLUGIN_VERSION, + false !== $js_version ? $js_version : PLUGIN_VERSION, [ 'in_footer' => true ] ); From fc44d7ebb88183843b34e7c9303a6a278b55f831 Mon Sep 17 00:00:00 2001 From: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Date: Wed, 5 Aug 2026 10:55:13 +0000 Subject: [PATCH 11/49] fix: unify subnavigation bands --- src/css/settings.scss | 14 +++-- src/js/components/common/SubnavTabs.tsx | 71 +++++++++++++++---------- src/php/Admin/Menus/Settings_Menu.php | 4 +- 3 files changed, 53 insertions(+), 36 deletions(-) diff --git a/src/css/settings.scss b/src/css/settings.scss index 0175dc6bb..647b5564e 100644 --- a/src/css/settings.scss +++ b/src/css/settings.scss @@ -242,12 +242,16 @@ body.js { } #settings-sections-tabs { - display: flex; - align-items: stretch; margin-block-end: 15px; padding: 0; - background: #fff; - border-block-end: 1px solid theme.$control-border; + + ul { + display: flex; + align-items: stretch; + margin: 0; + padding: 0; + list-style: none; + } .snippet-type-link { display: flex; @@ -284,7 +288,7 @@ body.js { @media (width <= 782px) { body.js { - #settings-sections-tabs { + .settings-type-nav-wrapper { display: none; } diff --git a/src/js/components/common/SubnavTabs.tsx b/src/js/components/common/SubnavTabs.tsx index f72990e86..879cd19c5 100644 --- a/src/js/components/common/SubnavTabs.tsx +++ b/src/js/components/common/SubnavTabs.tsx @@ -2,6 +2,7 @@ import classnames from 'classnames' import { __ } from '@wordpress/i18n' import React from 'react' import { isLicensed } from '../../utils/screen' +import { useHorizontalScrollOverflow } from '../../hooks/useHorizontalScrollOverflow' import { fetchQueryParam, updateQueryParams } from '../../utils/urls' export interface SubnavTab { @@ -43,34 +44,46 @@ export const SubnavTabs = ({ getTabCount, setCurrentTab, queryParamName, -}: SubnavTabsProps) => - + > + +
  • + ) +} diff --git a/src/php/Admin/Menus/Settings_Menu.php b/src/php/Admin/Menus/Settings_Menu.php index 46d65063d..4fb7b3abc 100644 --- a/src/php/Admin/Menus/Settings_Menu.php +++ b/src/php/Admin/Menus/Settings_Menu.php @@ -268,7 +268,7 @@ protected function render_section_tabs() { $sections = $this->get_sections(); $active_tab = $this->get_current_section(); - echo '
    '; echo '
    '; } From a8358e9d4eb990a55205dbe60b1d0c3c013bf1f6 Mon Sep 17 00:00:00 2001 From: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Date: Wed, 5 Aug 2026 11:31:00 +0000 Subject: [PATCH 12/49] fix: remove settings tab spacing --- src/css/settings.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/css/settings.scss b/src/css/settings.scss index 647b5564e..be7038058 100644 --- a/src/css/settings.scss +++ b/src/css/settings.scss @@ -242,7 +242,7 @@ body.js { } #settings-sections-tabs { - margin-block-end: 15px; + margin: 0; padding: 0; ul { From 74e88de373a782a1f0227520b6aace0b4778dee2 Mon Sep 17 00:00:00 2001 From: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Date: Wed, 5 Aug 2026 12:02:59 +0000 Subject: [PATCH 13/49] fix: fill responsive secondary navigation --- src/css/common/_subnav.scss | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/css/common/_subnav.scss b/src/css/common/_subnav.scss index cc193c6cd..a28ef3ab0 100644 --- a/src/css/common/_subnav.scss +++ b/src/css/common/_subnav.scss @@ -54,6 +54,18 @@ overflow-x: auto; scrollbar-width: thin; + @media (width <= 960px) { + li { + flex: 1 1 0; + } + + .snippet-type-link { + inline-size: 100%; + min-inline-size: 100px; + padding-inline: 4px; + } + } + @media (width <= 782px) { scrollbar-width: none; From 7bcda6d5fd690d6a552d29c80d99f13a6bc8a218 Mon Sep 17 00:00:00 2001 From: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Date: Wed, 5 Aug 2026 12:46:21 +0000 Subject: [PATCH 14/49] fix: normalize card text metrics --- src/css/common/_theme.scss | 4 ++-- src/css/manage/_cloud-community-cards.scss | 2 ++ src/css/manage/_snippets-table.scss | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/css/common/_theme.scss b/src/css/common/_theme.scss index f7d183526..1c42a31ce 100644 --- a/src/css/common/_theme.scss +++ b/src/css/common/_theme.scss @@ -13,7 +13,7 @@ $cloud-update: #ff9800; // screens regardless of WordPress version (see common/_wp-admin.scss). $accent-hover: #0a4b78; $control-border: #c3c4c7; -$control-text: #2c3338; +$control-text: #2c3337; $control-height: 38px; $control-radius: 5px; @@ -22,7 +22,7 @@ $control-radius: 5px; :root { --cs-color-accent: #2271b1; --cs-color-accent-hover: #0a4b78; - --cs-color-text: #2c3338; + --cs-color-text: #2c3337; --cs-color-text-muted: #646970; --cs-color-surface: #fff; --cs-color-surface-subtle: #f6f7f7; diff --git a/src/css/manage/_cloud-community-cards.scss b/src/css/manage/_cloud-community-cards.scss index 8a0c9c3f4..254b63d8f 100644 --- a/src/css/manage/_cloud-community-cards.scss +++ b/src/css/manage/_cloud-community-cards.scss @@ -62,6 +62,7 @@ flex-wrap: wrap; gap: 16px; font-size: 14px; + line-height: 1.5; .cloud-snippet-tags { color: theme.$accent; @@ -81,6 +82,7 @@ overflow: hidden; margin: 0; font-size: 14px; + line-height: 1.5; color: #646970; border-block-start: 1px solid #dcdcde; padding-block-start: 16px; diff --git a/src/css/manage/_snippets-table.scss b/src/css/manage/_snippets-table.scss index fa23d4a2f..dffdec55a 100644 --- a/src/css/manage/_snippets-table.scss +++ b/src/css/manage/_snippets-table.scss @@ -162,6 +162,7 @@ flex-wrap: wrap; gap: 16px; font-size: 14px; + line-height: 1.5; .snippet-card-tags-label { color: #646970; @@ -188,6 +189,7 @@ line-clamp: 2; overflow: hidden; font-size: 14px; + line-height: 1.5; color: #646970; border-block-start: 1px solid #dcdcde; padding-block-start: 16px; From 3b5d56ba6b004838809b3fd37623618c1ce732b5 Mon Sep 17 00:00:00 2001 From: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Date: Wed, 5 Aug 2026 12:51:36 +0000 Subject: [PATCH 15/49] fix: normalize editor controls --- src/css/edit/_form.scss | 6 +++--- src/css/edit/_sidebar.scss | 2 +- src/css/manage/_snippets-table.scss | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/css/edit/_form.scss b/src/css/edit/_form.scss index 1edb15e39..266d48590 100644 --- a/src/css/edit/_form.scss +++ b/src/css/edit/_form.scss @@ -5,7 +5,7 @@ $sidebar-gap: 30px; .snippet-form #titlediv #title, .snippet-type-container { - border-color: #ccc; + border-color: theme.$control-border; block-size: 45px } @@ -69,8 +69,8 @@ $sidebar-gap: 30px; align-items: center; gap: 8px; inline-size: 100%; - border-color: #ccc; - background: #fff; + border-color: theme.$control-border; + background: transparent; padding-block: 6px; padding-inline: 12px; diff --git a/src/css/edit/_sidebar.scss b/src/css/edit/_sidebar.scss index 888307702..3685abf51 100644 --- a/src/css/edit/_sidebar.scss +++ b/src/css/edit/_sidebar.scss @@ -57,7 +57,7 @@ .box { background-color: #fff; - border: 1px solid #ccc; + border: 1px solid theme.$control-border; border-radius: 4px; padding: 1.5em; display: flex; diff --git a/src/css/manage/_snippets-table.scss b/src/css/manage/_snippets-table.scss index dffdec55a..5a8e23f28 100644 --- a/src/css/manage/_snippets-table.scss +++ b/src/css/manage/_snippets-table.scss @@ -208,7 +208,7 @@ } .button:not(.button-primary) { - background: #fff; + background: transparent; } } From 34bbdccf8938fa1a2ef95651b81cffa9ac4ca1b8 Mon Sep 17 00:00:00 2001 From: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Date: Wed, 5 Aug 2026 12:57:54 +0000 Subject: [PATCH 16/49] fix: clarify editor heading structure --- src/css/edit.scss | 13 +++++++++++++ .../EditMenu/SnippetForm/fields/TagsEditor.tsx | 4 +--- .../EditMenu/SnippetForm/page/PageHeading.tsx | 4 ++-- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/css/edit.scss b/src/css/edit.scss index b7a10773d..242788d5f 100644 --- a/src/css/edit.scss +++ b/src/css/edit.scss @@ -59,6 +59,19 @@ } .snippet-tags-container { + .components-form-token-field__label { + border: 0; + clip: rect(1px, 1px, 1px, 1px); + clip-path: inset(50%); + block-size: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + inline-size: 1px; + word-wrap: normal !important; + } + .components-form-token-field__input-container { background: #fff; border-color: #c3c4c7; diff --git a/src/js/components/EditMenu/SnippetForm/fields/TagsEditor.tsx b/src/js/components/EditMenu/SnippetForm/fields/TagsEditor.tsx index 33c881618..52cefe866 100644 --- a/src/js/components/EditMenu/SnippetForm/fields/TagsEditor.tsx +++ b/src/js/components/EditMenu/SnippetForm/fields/TagsEditor.tsx @@ -10,9 +10,7 @@ export const TagsEditor: React.FC = () => { return options?.enabled ?
    -

    - -

    +

    {__('Snippet Tags', 'code-snippets')}

    { return ( <> -

    +

    {snippet.id ? <> {`${getPageHeading(snippet)} `} @@ -58,7 +58,7 @@ export const PageHeading: React.FC = () => { {label}{' '} )} -

    +
    From 59ddc51c4a2174a450f615a7d55d7cf6dbf62a11 Mon Sep 17 00:00:00 2001 From: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Date: Wed, 5 Aug 2026 13:06:34 +0000 Subject: [PATCH 17/49] fix: align shared control variants --- src/css/edit/_form.scss | 4 ++-- src/css/manage/_cloud-community-cards.scss | 18 ++++++++---------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/css/edit/_form.scss b/src/css/edit/_form.scss index 266d48590..9bb4ec94d 100644 --- a/src/css/edit/_form.scss +++ b/src/css/edit/_form.scss @@ -3,8 +3,7 @@ $sidebar-width: 321px; $sidebar-gap: 30px; -.snippet-form #titlediv #title, -.snippet-type-container { +.snippet-form #titlediv #title { border-color: theme.$control-border; block-size: 45px } @@ -18,6 +17,7 @@ $sidebar-gap: 30px; // Target the control box only (:first-of-type). The open menu is a later // sibling div; matching it here would clamp and flex-row the option list. > div:first-of-type { + border-color: theme.$control-border; min-block-size: 45px; block-size: 45px; max-block-size: 45px; diff --git a/src/css/manage/_cloud-community-cards.scss b/src/css/manage/_cloud-community-cards.scss index 254b63d8f..ac8bf3fae 100644 --- a/src/css/manage/_cloud-community-cards.scss +++ b/src/css/manage/_cloud-community-cards.scss @@ -1,4 +1,3 @@ -@use 'sass:color'; @use '../common/theme'; @use '../common/cards'; @@ -114,7 +113,7 @@ } .button:not(.button-primary, .cloud-pro-button) { - background: #fff; + background: transparent; } } } @@ -123,18 +122,17 @@ margin: 0; } -// Anchored to the card container so this keeps its brand-orange fill above the WP-admin -// compatibility layer's secondary-button styling (equal specificity, resolved by source order). +// Pro-only actions use the same quiet outline treatment in card and table views. .cloud-search-result .cloud-pro-button.button { - background-color: theme.$secondary; - border-color: theme.$secondary; - color: #fff; + background-color: transparent; + border-color: theme.$accent; + color: theme.$accent; &:hover, &:focus { - background-color: color.adjust(theme.$secondary, $lightness: -10%); - border-color: color.adjust(theme.$secondary, $lightness: -10%); - color: #fff; + background-color: #eff5f9; + border-color: theme.$accent; + color: theme.$accent; } } From e966312682394629ab8cc8d88e4edf58ae12e6ef Mon Sep 17 00:00:00 2001 From: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Date: Wed, 5 Aug 2026 13:10:39 +0000 Subject: [PATCH 18/49] fix: unify Pro-only action styling --- src/css/manage/_cloud-community-cards.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/css/manage/_cloud-community-cards.scss b/src/css/manage/_cloud-community-cards.scss index ac8bf3fae..82ead781a 100644 --- a/src/css/manage/_cloud-community-cards.scss +++ b/src/css/manage/_cloud-community-cards.scss @@ -123,7 +123,7 @@ } // Pro-only actions use the same quiet outline treatment in card and table views. -.cloud-search-result .cloud-pro-button.button { +.cloud-pro-button.button { background-color: transparent; border-color: theme.$accent; color: theme.$accent; From a05ec3c1ed50b9ee8ca783251b4b73c3525aae9b Mon Sep 17 00:00:00 2001 From: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Date: Wed, 5 Aug 2026 13:14:18 +0000 Subject: [PATCH 19/49] fix: retain editor heading layout --- src/css/edit.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/css/edit.scss b/src/css/edit.scss index 242788d5f..1e64c7559 100644 --- a/src/css/edit.scss +++ b/src/css/edit.scss @@ -108,7 +108,7 @@ } } -.wrap > h2:first-of-type { +.wrap > h1:first-of-type { font-size: 1.5rem; font-weight: 400; line-height: 1.3; From 23e4469f11dd0d58a2cdfcd8c9bf41f8386f39fd Mon Sep 17 00:00:00 2001 From: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Date: Wed, 5 Aug 2026 15:04:59 +0000 Subject: [PATCH 20/49] fix: align search field border --- src/css/common/list-table/_pagination.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/css/common/list-table/_pagination.scss b/src/css/common/list-table/_pagination.scss index 4624c6186..137f791fb 100644 --- a/src/css/common/list-table/_pagination.scss +++ b/src/css/common/list-table/_pagination.scss @@ -1,4 +1,5 @@ @use '../checkbox'; +@use '../theme'; // The pagination group and view toggle wrap together as one end-pinned // cluster so narrow viewports never split them across rows. @@ -65,7 +66,7 @@ } .wrap .snippets-search-area input[type='search'] { - border-color: #e3e3e4; + border-color: theme.$control-border; } // Tablet widths: let the search area grow to fill the remainder of its row From a40cd43266cfcca10f3252286331a7a70aea8275 Mon Sep 17 00:00:00 2001 From: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Date: Wed, 5 Aug 2026 15:46:34 +0000 Subject: [PATCH 21/49] fix: refine cloud preview controls --- src/css/manage/_cloud-community.scss | 17 ++++++-- .../components/common/SnippetPreviewModal.tsx | 40 +++++++++++++++++-- 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/src/css/manage/_cloud-community.scss b/src/css/manage/_cloud-community.scss index 088478d27..cdbabc136 100644 --- a/src/css/manage/_cloud-community.scss +++ b/src/css/manage/_cloud-community.scss @@ -1,4 +1,5 @@ @use '../common/banners'; +@use '../common/checkbox'; @use '../common/theme'; .cloud-snippet-status { @@ -98,8 +99,10 @@ $status-colors: ( // Keep the Download / Edit / Pro Only button a consistent width so // rows line up regardless of which state the snippet is in. .button-primary, - .cloud-pro-button { - min-inline-size: 90px; + .cloud-pro-button, + .button:not(.button-primary) { + inline-size: 103px; + min-inline-size: 103px; text-align: center; justify-content: center; } @@ -115,6 +118,14 @@ $status-colors: ( vertical-align: middle; } + .check-column { + vertical-align: middle; + + input[type='checkbox'] { + @include checkbox.canonical; + } + } + .column-name { inline-size: 22%; } @@ -128,7 +139,7 @@ $status-colors: ( } .column-actions { - inline-size: 210px; + inline-size: 234px; } .cloud-table-name-button { diff --git a/src/js/components/common/SnippetPreviewModal.tsx b/src/js/components/common/SnippetPreviewModal.tsx index d535b9fb5..9cba3929f 100644 --- a/src/js/components/common/SnippetPreviewModal.tsx +++ b/src/js/components/common/SnippetPreviewModal.tsx @@ -83,6 +83,35 @@ const usePreviewActionHandlers = ({ return { handleClone, handleExport } } +const CopyCodeButton: React.FC<{ code: string }> = ({ code }) => { + const [copyStatus, setCopyStatus] = useState<'copied' | 'failed' | null>(null) + + const copyCode = () => { + const clipboard = navigator.clipboard as Clipboard | undefined + + if (!window.isSecureContext || !clipboard) { + setCopyStatus('failed') + return + } + + void clipboard.writeText(code) + .then(() => setCopyStatus('copied')) + .catch(() => setCopyStatus('failed')) + } + + const label = 'copied' === copyStatus + ? __('Copied', 'code-snippets') + : 'failed' === copyStatus + ? __('Copy unavailable', 'code-snippets') + : __('Copy code', 'code-snippets') + + return ( + + ) +} + const SnippetPreviewButtons: React.FC = ({ snippet, closeModal, @@ -118,6 +147,8 @@ const SnippetPreviewButtons: React.FC = ({ {__('Export', 'code-snippets')} + + {snippet.locked || !canModify ? null :
    -const PreviewFooterActionsWrapper: React.FC = ({ children }) => +const PreviewFooterActionsWrapper: React.FC> = ({ children, code }) => children ?
    -
    {children}
    +
    + {children} + +
    : null @@ -240,7 +274,7 @@ export const SnippetPreviewModal: React.FC = ({ {snippet ? setIsOpen(false)} /> - : {footerActions}} + : {footerActions}} : null } From 52ea778ff3a97c0b0f4b0cb3ced454b1fb77f73c Mon Sep 17 00:00:00 2001 From: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Date: Wed, 5 Aug 2026 16:15:21 +0000 Subject: [PATCH 22/49] feat: add snippet search action --- .../SnippetsTable/SnippetsTableSearch.tsx | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/js/components/ManageMenu/SnippetsTable/SnippetsTableSearch.tsx b/src/js/components/ManageMenu/SnippetsTable/SnippetsTableSearch.tsx index 2fb89c130..7465b1a54 100644 --- a/src/js/components/ManageMenu/SnippetsTable/SnippetsTableSearch.tsx +++ b/src/js/components/ManageMenu/SnippetsTable/SnippetsTableSearch.tsx @@ -1,24 +1,38 @@ import { __, sprintf } from '@wordpress/i18n' -import React from 'react' +import React, { useEffect, useState } from 'react' import { Button } from '../../common/Button' import { useSnippetsFilters } from './WithSnippetsTableFilters' export const SearchArea = () => { const { searchQuery, setSearchQuery } = useSnippetsFilters() + const [query, setQuery] = useState(searchQuery ?? '') + + useEffect(() => setQuery(searchQuery ?? ''), [searchQuery]) return (
    - +
    { + event.preventDefault() + setSearchQuery(query) + }} + > setSearchQuery(event.target.value)} + onChange={event => setQuery(event.target.value)} placeholder={__('Search snippets', 'code-snippets')} /> - + + +
    ) } From 1f517fcfc042fbfe363790958d5bdc1c879890c0 Mon Sep 17 00:00:00 2001 From: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Date: Wed, 5 Aug 2026 16:38:07 +0000 Subject: [PATCH 23/49] fix: show settings navigation overflow --- src/js/services/settings/tabs.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/js/services/settings/tabs.ts b/src/js/services/settings/tabs.ts index 7c2ff0769..acede2cf4 100644 --- a/src/js/services/settings/tabs.ts +++ b/src/js/services/settings/tabs.ts @@ -34,6 +34,26 @@ const updateHttpReferer = (section: string) => { httpReferer.value = newReferer + (newReferer === httpReferer.value ? `§ion=${section}` : '') } +const setupHorizontalOverflow = (tabs: HTMLElement) => { + const wrapper = tabs.closest('.snippet-type-nav-wrapper') + if (!wrapper) { + return + } + + const updateFades = () => { + const remainingScroll = tabs.scrollWidth - tabs.clientWidth - tabs.scrollLeft + wrapper.classList.toggle('has-scroll-start', 1 < tabs.scrollLeft) + wrapper.classList.toggle('has-scroll-end', 1 < remainingScroll) + } + + tabs.addEventListener('scroll', updateFades, { passive: true }) + + const observer = new ResizeObserver(updateFades) + observer.observe(tabs) + observer.observe(tabs.firstElementChild ?? tabs) + updateFades() +} + export const handleSettingsTabs = () => { const tabsWrapper = document.getElementById('settings-sections-tabs') if (!tabsWrapper) { @@ -41,6 +61,8 @@ export const handleSettingsTabs = () => { return } + setupHorizontalOverflow(tabsWrapper) + const tabs = tabsWrapper.querySelectorAll('.snippet-type-link') for (const tab of tabs) { From 5ada5c3ef690ab074a9d9618a99720d0918adbcd Mon Sep 17 00:00:00 2001 From: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Date: Wed, 5 Aug 2026 17:34:55 +0000 Subject: [PATCH 24/49] fix: blend active navigation tab --- src/css/common/_subnav.scss | 10 ++++------ src/css/common/_wp-admin.scss | 5 +++++ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/css/common/_subnav.scss b/src/css/common/_subnav.scss index a28ef3ab0..d15c17512 100644 --- a/src/css/common/_subnav.scss +++ b/src/css/common/_subnav.scss @@ -14,7 +14,6 @@ inline-size: calc(100% + 42px); overflow: hidden; background: #fff; - border-block-end: 1px solid #e2e2e4; &::before, &::after { @@ -54,6 +53,9 @@ overflow-x: auto; scrollbar-width: thin; + // The row line is painted on the scrollport so it remains fixed while tabs scroll. + background: linear-gradient(to top, #e2e2e4 1px, #fff 1px); + @media (width <= 960px) { li { flex: 1 1 0; @@ -78,7 +80,7 @@ display: flex; align-items: stretch; flex-wrap: nowrap; - min-block-size: 52px; + min-block-size: 53px; margin: 0; padding: 0; list-style: none; @@ -143,10 +145,6 @@ background: #f0f0f1; border-color: #f0f0f1; color: theme.$accent; - - // Children paint over the parent nav's border, so this recolours - // the 1px band separator beneath the active tab to match its fill. - box-shadow: 0 1px 0 0 #f0f0f1; } // Suppress the persistent focus ring left behind after a mouse click; diff --git a/src/css/common/_wp-admin.scss b/src/css/common/_wp-admin.scss index babb82a95..30a48456b 100644 --- a/src/css/common/_wp-admin.scss +++ b/src/css/common/_wp-admin.scss @@ -1,6 +1,11 @@ @use 'checkbox'; @use 'theme'; +// Match the active subnavigation tile so it blends into the admin canvas. +body.wp-admin { + background: #f0f0f1; +} + /** * Native form control styling. * From d8de5c167643379801f202d72af74d34c10018ec Mon Sep 17 00:00:00 2001 From: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Date: Wed, 5 Aug 2026 17:49:03 +0000 Subject: [PATCH 25/49] fix: align navigation and page titles --- src/css/common/_page-header.scss | 4 ++-- src/css/common/_subnav.scss | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/css/common/_page-header.scss b/src/css/common/_page-header.scss index 81a8c10e0..9c72c42df 100644 --- a/src/css/common/_page-header.scss +++ b/src/css/common/_page-header.scss @@ -15,10 +15,10 @@ h2 { margin: 0; padding: 0; - font-size: 32px; + font-size: 26px; font-weight: 510; line-height: 1.25; - color: #000; + color: theme.$control-text; } } diff --git a/src/css/common/_subnav.scss b/src/css/common/_subnav.scss index d15c17512..9fa313dc2 100644 --- a/src/css/common/_subnav.scss +++ b/src/css/common/_subnav.scss @@ -143,7 +143,6 @@ &.active-type { background: #f0f0f1; - border-color: #f0f0f1; color: theme.$accent; } From 52b4b02017cdef662992a0d1c779397462efbbb0 Mon Sep 17 00:00:00 2001 From: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Date: Wed, 5 Aug 2026 18:04:01 +0000 Subject: [PATCH 26/49] fix: increase table cell spacing --- src/css/common/_subnav.scss | 1 + src/css/common/list-table/_layout.scss | 15 +++++++++++---- src/css/manage/_cloud-community.scss | 7 ++----- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/css/common/_subnav.scss b/src/css/common/_subnav.scss index 9fa313dc2..a23f98e59 100644 --- a/src/css/common/_subnav.scss +++ b/src/css/common/_subnav.scss @@ -217,6 +217,7 @@ display: flex; justify-content: flex-end; margin: 0; + border-color: #e2e2e4; } #screen-meta { diff --git a/src/css/common/list-table/_layout.scss b/src/css/common/list-table/_layout.scss index 009bded69..4b0775ca0 100644 --- a/src/css/common/list-table/_layout.scss +++ b/src/css/common/list-table/_layout.scss @@ -49,11 +49,19 @@ @include checkbox.canonical; } + // Shared cell rhythm for snippet and Cloud tables. Padding, rather than a + // fixed row height, keeps multi-line values comfortable and uncropped. + td, + th { + padding-block: 16px; + padding-inline: 8px; + } + // Shared horizontal rhythm for every snippet table, so the cloud table lines // up with the snippets table rather than keeping the wider WordPress default. th:not(.check-column), td:not(.check-column) { - padding-inline: 9px; + padding-inline: 8px; } td.column-id { @@ -203,8 +211,8 @@ tfoot td.check-column, tbody th.check-column, tbody td.check-column { - padding-block: 0; - padding-inline-start: 9px; + padding-block: 16px; + padding-inline-start: 8px; vertical-align: middle; } @@ -223,7 +231,6 @@ .active-snippet, .inactive-snippet { td, th { - padding: 10px 9px; box-shadow: inset 0 -1px 0 rgb(0 0 0 / 10%); vertical-align: middle; } diff --git a/src/css/manage/_cloud-community.scss b/src/css/manage/_cloud-community.scss index cdbabc136..4c3a8bd70 100644 --- a/src/css/manage/_cloud-community.scss +++ b/src/css/manage/_cloud-community.scss @@ -109,12 +109,9 @@ $status-colors: ( } .cloud-snippets-table { - // Body rows only: the header row also holds a `td` for the select-all cell, - // and a fixed height there would make this header taller than the one on the - // snippets table. + // Body rows inherit the shared table-cell padding so descriptions can grow + // naturally instead of being constrained to a fixed row height. tbody td { - box-sizing: border-box; - block-size: 60px; vertical-align: middle; } From eab2c32154e26f57048db9f6efdb785919caf212 Mon Sep 17 00:00:00 2001 From: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Date: Wed, 5 Aug 2026 18:36:06 +0000 Subject: [PATCH 27/49] fix: refine table and heading spacing --- src/css/common/_page-header.scss | 7 +++++++ src/css/common/list-table/_layout.scss | 10 ++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/css/common/_page-header.scss b/src/css/common/_page-header.scss index 9c72c42df..60d07f53d 100644 --- a/src/css/common/_page-header.scss +++ b/src/css/common/_page-header.scss @@ -18,6 +18,13 @@ font-size: 26px; font-weight: 510; line-height: 1.25; + } + + h1 { + color: #1d2327; + } + + h2 { color: theme.$control-text; } } diff --git a/src/css/common/list-table/_layout.scss b/src/css/common/list-table/_layout.scss index 4b0775ca0..713742696 100644 --- a/src/css/common/list-table/_layout.scss +++ b/src/css/common/list-table/_layout.scss @@ -57,6 +57,16 @@ padding-inline: 8px; } + th:first-child, + td:first-child { + padding-inline-start: 16px; + } + + th:last-child, + td:last-child { + padding-inline-end: 16px; + } + // Shared horizontal rhythm for every snippet table, so the cloud table lines // up with the snippets table rather than keeping the wider WordPress default. th:not(.check-column), From 3b925bff817344d0d285b4287dde051d5ea5575a Mon Sep 17 00:00:00 2001 From: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Date: Fri, 7 Aug 2026 10:05:37 +0000 Subject: [PATCH 28/49] fix: remove top margin on secondary tabs row --- src/css/common/_subnav.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/css/common/_subnav.scss b/src/css/common/_subnav.scss index a23f98e59..4b6eed963 100644 --- a/src/css/common/_subnav.scss +++ b/src/css/common/_subnav.scss @@ -9,7 +9,7 @@ // Bleed across the full admin content width, flush against the plugin toolbar. box-sizing: border-box; min-block-size: 53px; - margin-block-start: -10px; + margin-block-start: 0; margin-inline-start: -22px; inline-size: calc(100% + 42px); overflow: hidden; From 889123b363723a12a0f28c1c53403c96c7b3f96e Mon Sep 17 00:00:00 2001 From: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Date: Fri, 7 Aug 2026 10:11:08 +0000 Subject: [PATCH 29/49] fix: remove notice margin on nav wrapper --- src/css/common/_subnav.scss | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/css/common/_subnav.scss b/src/css/common/_subnav.scss index 4b6eed963..6240e8331 100644 --- a/src/css/common/_subnav.scss +++ b/src/css/common/_subnav.scss @@ -264,9 +264,8 @@ } } -// When an admin notice pushes the page content down, restore the gap and top -// border above the subnav bar. +// When an admin notice pushes the page content down, restore the top border +// above the subnav bar (no extra margin — the wrapper sits flush). .notice + .wrap .snippet-type-nav-wrapper { - margin-block-start: 20px; border-block-start: 1px solid theme.$control-border; } From 1d4e41024684d30d79db1055ac89e49e77092218 Mon Sep 17 00:00:00 2001 From: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Date: Fri, 7 Aug 2026 10:23:23 +0000 Subject: [PATCH 30/49] fix: unify border colors and remove container margin --- src/css/common/_subnav.scss | 6 +++++- src/css/common/_wp-admin.scss | 6 ++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/css/common/_subnav.scss b/src/css/common/_subnav.scss index 6240e8331..8484c8c3f 100644 --- a/src/css/common/_subnav.scss +++ b/src/css/common/_subnav.scss @@ -218,6 +218,10 @@ justify-content: flex-end; margin: 0; border-color: #e2e2e4; + + .show-settings { + border-color: #e2e2e4; + } } #screen-meta { @@ -267,5 +271,5 @@ // When an admin notice pushes the page content down, restore the top border // above the subnav bar (no extra margin — the wrapper sits flush). .notice + .wrap .snippet-type-nav-wrapper { - border-block-start: 1px solid theme.$control-border; + border-block-start: 1px solid #e2e2e4; } diff --git a/src/css/common/_wp-admin.scss b/src/css/common/_wp-admin.scss index 30a48456b..1dd6f035a 100644 --- a/src/css/common/_wp-admin.scss +++ b/src/css/common/_wp-admin.scss @@ -6,6 +6,12 @@ body.wp-admin { background: #f0f0f1; } +// Remove the WordPress core 10px top margin on the manage snippets container +// so the subnav band sits flush against the toolbar. +#manage-snippets-container.wrap { + margin-block-start: 0; +} + /** * Native form control styling. * From c60680bfaa25b17613d96a3a280ec8664c981164 Mon Sep 17 00:00:00 2001 From: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Date: Fri, 7 Aug 2026 10:32:45 +0000 Subject: [PATCH 31/49] fix: compensate double border with -1px nav wrapper margin --- src/css/common/_subnav.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/css/common/_subnav.scss b/src/css/common/_subnav.scss index 8484c8c3f..d0c5033a2 100644 --- a/src/css/common/_subnav.scss +++ b/src/css/common/_subnav.scss @@ -9,7 +9,7 @@ // Bleed across the full admin content width, flush against the plugin toolbar. box-sizing: border-box; min-block-size: 53px; - margin-block-start: 0; + margin-block-start: -1px; margin-inline-start: -22px; inline-size: calc(100% + 42px); overflow: hidden; From ab0905887b5259e5f5e3dbe1d09e67825ffe1bb2 Mon Sep 17 00:00:00 2001 From: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Date: Fri, 7 Aug 2026 10:40:52 +0000 Subject: [PATCH 32/49] fix: adjust badge colours to WCAG AA-compliant values --- src/css/common/_theme.scss | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/css/common/_theme.scss b/src/css/common/_theme.scss index 1c42a31ce..f4f40c544 100644 --- a/src/css/common/_theme.scss +++ b/src/css/common/_theme.scss @@ -43,12 +43,12 @@ $badges: ( css: #9b59b6, js: #f7d67a #1c1f20, cond: #22826f, - core: #61c5cb, + core: #4fa1a6, pro: #f7e8e3 #df9279, - cloud: $cloud, + cloud: #009fb4, bundles: #50575e, - cloud_search: #ff9800, - private: #f7e6be #ca961b, + cloud_search: #d27c00, + private: #f7e6be #a27813, public: #dbebf7 $accent, success: #d3e8d1 #447340, failure: #fad7c1 #a24b16, From 1de133d808348caf7881e2666467fa7283bc0ec0 Mon Sep 17 00:00:00 2001 From: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Date: Fri, 7 Aug 2026 11:41:57 +0000 Subject: [PATCH 33/49] fix: tooltip width fits content --- src/css/common/_tooltips.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/css/common/_tooltips.scss b/src/css/common/_tooltips.scss index 1582d2942..5b0a4c0b6 100644 --- a/src/css/common/_tooltips.scss +++ b/src/css/common/_tooltips.scss @@ -64,7 +64,8 @@ $bg-color: hsl(0deg 0% 20% / 90%); font-size: small; font-weight: normal; text-transform: none; - min-inline-size: 200px; + min-inline-size: auto; + max-inline-size: 300px; backdrop-filter: blur(3px); .tooltip-block & { From 79db211ed7466201980cebf47b56d2b65dabdf18 Mon Sep 17 00:00:00 2001 From: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Date: Fri, 7 Aug 2026 12:16:22 +0000 Subject: [PATCH 34/49] fix: tooltip width fits content with dark bg --- src/css/common/_tooltips.scss | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/css/common/_tooltips.scss b/src/css/common/_tooltips.scss index 5b0a4c0b6..dfc7a99d0 100644 --- a/src/css/common/_tooltips.scss +++ b/src/css/common/_tooltips.scss @@ -1,4 +1,4 @@ -$bg-color: hsl(0deg 0% 20% / 90%); +$bg-color: rgb(19 18 18 / 90%); .tooltip { position: relative; @@ -57,15 +57,15 @@ $bg-color: hsl(0deg 0% 20% / 90%); .tooltip-content { z-index: 1000; padding: 8px; - background-color: $bg-color; + background-color: rgb(19 18 18 / 90%); color: #fff; border-radius: 6px; position: absolute; - font-size: small; + font-size: 12px; font-weight: normal; text-transform: none; - min-inline-size: auto; - max-inline-size: 300px; + inline-size: max-content; + max-inline-size: 200px; backdrop-filter: blur(3px); .tooltip-block & { From 904795d441eddc5299f1d97dd11e9b5b42c96e1d Mon Sep 17 00:00:00 2001 From: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Date: Fri, 7 Aug 2026 14:28:08 +0000 Subject: [PATCH 35/49] fix: resolve PR review comments --- package.json | 1 - .../EditMenu/SnippetForm/fields/TagsEditor.tsx | 2 +- .../ManageMenu/SnippetsTable/ManageSnippetCard.tsx | 2 +- .../ManageMenu/SnippetsTable/SnippetsTable.tsx | 2 +- src/js/components/common/SnippetPreviewModal.tsx | 6 +++--- src/js/components/common/Toolbar.tsx | 3 +-- src/php/Admin/Menus/Edit_Menu.php | 8 ++------ src/php/Admin/Menus/Settings_Menu.php | 11 +++-------- 8 files changed, 12 insertions(+), 23 deletions(-) diff --git a/package.json b/package.json index bf19e786d..417d03066 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,6 @@ }, "dependencies": { "@codemirror/fold": "^0.19.4", - "@tabler/icons-react": "^3.46.0", "@wordpress/components": "^29.3.0", "@wordpress/date": "^5.43.0", "@wordpress/dom-ready": "^4.17.0", diff --git a/src/js/components/EditMenu/SnippetForm/fields/TagsEditor.tsx b/src/js/components/EditMenu/SnippetForm/fields/TagsEditor.tsx index 715bdb50b..62a15778a 100644 --- a/src/js/components/EditMenu/SnippetForm/fields/TagsEditor.tsx +++ b/src/js/components/EditMenu/SnippetForm/fields/TagsEditor.tsx @@ -11,8 +11,8 @@ export const TagsEditor: React.FC = () => { return options?.enabled ?
    = ({
    - {0 < snippet.tags.length || snippet.modified && ( + {(0 < snippet.tags.length || !!snippet.modified) && (
    {0 < snippet.tags.length && ( diff --git a/src/js/components/ManageMenu/SnippetsTable/SnippetsTable.tsx b/src/js/components/ManageMenu/SnippetsTable/SnippetsTable.tsx index 83860c086..b51033ec5 100644 --- a/src/js/components/ManageMenu/SnippetsTable/SnippetsTable.tsx +++ b/src/js/components/ManageMenu/SnippetsTable/SnippetsTable.tsx @@ -52,7 +52,7 @@ const SnippetTypeTab: React.FC = ({ type, count }) => { {__('All', 'code-snippets')} } - {count !== undefined && (!type || 'php' === type || 'html' === type) && {count}} + {count !== undefined && (isLicensed() || !type || !isProType(type)) && {count}} {type && isProType(type) && !isLicensed() && {__('Pro', 'code-snippets')}} diff --git a/src/js/components/common/SnippetPreviewModal.tsx b/src/js/components/common/SnippetPreviewModal.tsx index a20bbeed2..eaddc5524 100644 --- a/src/js/components/common/SnippetPreviewModal.tsx +++ b/src/js/components/common/SnippetPreviewModal.tsx @@ -31,7 +31,7 @@ export interface SnippetPreviewModalProps { setIsOpen: (isOpen: boolean) => void snippet?: Snippet footerActions?: ReactNode - extraActions?: PreviewExtraActions + extraActions?: ReactNode } const EDITOR_MODES: Record = { @@ -56,7 +56,7 @@ const getPreviewEditorSettings = (type: string): EditorConfiguration => ({ interface SnippetPreviewActionsProps { snippet: Snippet closeModal: () => void - extraActions?: PreviewExtraActions + extraActions?: ReactNode } interface SnippetPreviewButtonsProps extends SnippetPreviewActionsProps { @@ -188,7 +188,7 @@ const SnippetPreviewButtons: React.FC = ({ - {extraActions?.(working)} + {extraActions} {snippet.locked || !canModify ? null diff --git a/src/js/components/common/Toolbar.tsx b/src/js/components/common/Toolbar.tsx index 3da4f2231..3c48c708f 100644 --- a/src/js/components/common/Toolbar.tsx +++ b/src/js/components/common/Toolbar.tsx @@ -1,4 +1,3 @@ -import { IconChevronDown } from '@tabler/icons-react' import { __, _x } from '@wordpress/i18n' import classnames from 'classnames' import React, { useState } from 'react' @@ -96,7 +95,7 @@ const MoreNav = () =>
    {__('More', 'code-snippets')} -
      {UPPER_NAV_LINKS.map(link => diff --git a/src/php/Admin/Menus/Edit_Menu.php b/src/php/Admin/Menus/Edit_Menu.php index 9d3c94ff0..85556ac6b 100644 --- a/src/php/Admin/Menus/Edit_Menu.php +++ b/src/php/Admin/Menus/Edit_Menu.php @@ -192,10 +192,6 @@ public function enqueue_assets() { enqueue_code_editor( $this->snippet->type ); - $plugin_dir = plugin_dir_path( PLUGIN_FILE ); - $css_version = filemtime( $plugin_dir . 'dist/edit.css' ); - $js_version = filemtime( $plugin_dir . 'dist/edit.js' ); - wp_enqueue_style( self::CSS_HANDLE, plugins_url( 'dist/edit.css', PLUGIN_FILE ), @@ -203,14 +199,14 @@ public function enqueue_assets() { 'code-editor', 'wp-components', ], - false !== $css_version ? $css_version : PLUGIN_VERSION + PLUGIN_VERSION ); wp_enqueue_script( self::JS_HANDLE, plugins_url( 'dist/edit.js', PLUGIN_FILE ), [ 'code-snippets-code-editor' ] + self::$script_deps, - false !== $js_version ? $js_version : PLUGIN_VERSION, + PLUGIN_VERSION, [ 'in_footer' => true ] ); diff --git a/src/php/Admin/Menus/Settings_Menu.php b/src/php/Admin/Menus/Settings_Menu.php index 854efb131..833ed22e3 100644 --- a/src/php/Admin/Menus/Settings_Menu.php +++ b/src/php/Admin/Menus/Settings_Menu.php @@ -57,25 +57,20 @@ public function load() { */ public function enqueue_assets() { $this->enqueue_codemirror(); - $handle = 'code-snippets-settings'; - $plugin_dir = plugin_dir_path( PLUGIN_FILE ); - $css_version = filemtime( $plugin_dir . 'dist/settings.css' ); - $js_version = filemtime( $plugin_dir . 'dist/settings.js' ); - $css_version = false !== $css_version ? $css_version : PLUGIN_VERSION; - $js_version = false !== $js_version ? $js_version : PLUGIN_VERSION; + $handle = 'code-snippets-settings'; wp_enqueue_style( $handle, plugins_url( 'dist/settings.css', PLUGIN_FILE ), self::$style_deps + [ 'code-editor' ], - $css_version + PLUGIN_VERSION ); wp_enqueue_script( $handle, plugins_url( 'dist/settings.js', PLUGIN_FILE ), self::$script_deps + [ 'code-snippets-code-editor' ], - $js_version, + PLUGIN_VERSION, true ); From 9f67c6829b368228f41a2bef03bc1e9a1bc3629c Mon Sep 17 00:00:00 2001 From: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Date: Fri, 7 Aug 2026 15:35:21 +0000 Subject: [PATCH 36/49] fix: layer active toolbar link above neighbours --- src/css/common/_toolbar.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/src/css/common/_toolbar.scss b/src/css/common/_toolbar.scss index 5015e86c7..08745f92a 100644 --- a/src/css/common/_toolbar.scss +++ b/src/css/common/_toolbar.scss @@ -130,6 +130,7 @@ $wpcontent-inline-start-indent: 20px; &.active-link, &:hover, &:focus, &:active { color: theme.$accent; + z-index: 1; } } From 0c343b7e57db61ef7629b40f7f54f60d95791f65 Mon Sep 17 00:00:00 2001 From: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Date: Fri, 7 Aug 2026 15:45:46 +0000 Subject: [PATCH 37/49] fix: address ramiy's PR review follow-ups --- src/css/edit.scss | 13 ------------- .../EditMenu/SnippetForm/fields/TagsEditor.tsx | 2 +- .../ManageMenu/SnippetsTable/SnippetsTable.tsx | 2 +- .../SnippetsTable/SnippetsTableSearch.tsx | 4 ++-- 4 files changed, 4 insertions(+), 17 deletions(-) diff --git a/src/css/edit.scss b/src/css/edit.scss index 1e64c7559..6f270cea9 100644 --- a/src/css/edit.scss +++ b/src/css/edit.scss @@ -59,19 +59,6 @@ } .snippet-tags-container { - .components-form-token-field__label { - border: 0; - clip: rect(1px, 1px, 1px, 1px); - clip-path: inset(50%); - block-size: 1px; - margin: -1px; - overflow: hidden; - padding: 0; - position: absolute; - inline-size: 1px; - word-wrap: normal !important; - } - .components-form-token-field__input-container { background: #fff; border-color: #c3c4c7; diff --git a/src/js/components/EditMenu/SnippetForm/fields/TagsEditor.tsx b/src/js/components/EditMenu/SnippetForm/fields/TagsEditor.tsx index 62a15778a..715bdb50b 100644 --- a/src/js/components/EditMenu/SnippetForm/fields/TagsEditor.tsx +++ b/src/js/components/EditMenu/SnippetForm/fields/TagsEditor.tsx @@ -11,8 +11,8 @@ export const TagsEditor: React.FC = () => { return options?.enabled ?
      { currentType ? SNIPPET_TYPE_LABELS[currentType] : __('All Snippets', 'code-snippets') )} - {__('Create New', 'code-snippets')} + {__('Add Snippet', 'code-snippets')}
      diff --git a/src/js/components/ManageMenu/SnippetsTable/SnippetsTableSearch.tsx b/src/js/components/ManageMenu/SnippetsTable/SnippetsTableSearch.tsx index 7465b1a54..c4493823f 100644 --- a/src/js/components/ManageMenu/SnippetsTable/SnippetsTableSearch.tsx +++ b/src/js/components/ManageMenu/SnippetsTable/SnippetsTableSearch.tsx @@ -10,7 +10,7 @@ export const SearchArea = () => { useEffect(() => setQuery(searchQuery ?? ''), [searchQuery]) return ( -
      +
      { {__('Search', 'code-snippets')}
      -
      + ) } From 56569f9923deda4a695eea4aaf4eeb3af6f3983a Mon Sep 17 00:00:00 2001 From: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Date: Fri, 7 Aug 2026 15:58:43 +0000 Subject: [PATCH 38/49] fix: upsell page fills content area; remove manage assets filemtime versioning --- src/css/common/_upsell.scss | 13 +++++++++++++ src/php/Admin/Menus/Manage/Manage_Menu_Assets.php | 10 ++-------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/css/common/_upsell.scss b/src/css/common/_upsell.scss index 5b2a25f77..783278b4a 100644 --- a/src/css/common/_upsell.scss +++ b/src/css/common/_upsell.scss @@ -121,6 +121,19 @@ border-radius: 8px; } +// Bleed the upsell to the full width of the admin content area and fill the +// remaining viewport height, so it reads as a full page rather than a small +// boxed card floating in the subpage. 168px accounts for the WP admin bar +// (32px) plus the plugin toolbar ($toolbar-block-size, 136px in _toolbar.scss). +.code-snippets-upsell-page { + box-sizing: border-box; + margin-inline-start: -22px; + inline-size: calc(100% + 42px); + min-block-size: calc(100vh - 168px); + border-radius: 0; + justify-content: center; +} + .code-snippets-upsell-page, .code-snippets-upsell-dialog .components-modal__content > div:last-child { display: flex; diff --git a/src/php/Admin/Menus/Manage/Manage_Menu_Assets.php b/src/php/Admin/Menus/Manage/Manage_Menu_Assets.php index 8641efeb9..be1ebac7f 100644 --- a/src/php/Admin/Menus/Manage/Manage_Menu_Assets.php +++ b/src/php/Admin/Menus/Manage/Manage_Menu_Assets.php @@ -71,24 +71,18 @@ private function get_snippet_type_counts(): array { * @return void */ public function enqueue( array $script_dependencies, array $style_dependencies ): void { - $plugin_dir = plugin_dir_path( PLUGIN_FILE ); - $css_version = filemtime( $plugin_dir . 'dist/manage.css' ); - $js_version = filemtime( $plugin_dir . 'dist/manage.js' ); - $css_version = false !== $css_version ? $css_version : PLUGIN_VERSION; - $js_version = false !== $js_version ? $js_version : PLUGIN_VERSION; - wp_enqueue_style( self::CSS_HANDLE, plugins_url( 'dist/manage.css', PLUGIN_FILE ), $style_dependencies, - $css_version + PLUGIN_VERSION ); wp_enqueue_script( self::JS_HANDLE, plugins_url( 'dist/manage.js', PLUGIN_FILE ), $script_dependencies, - $js_version, + PLUGIN_VERSION, [ 'in_footer' => true ] ); From 41c42175839a49e2215b44066e7fe12dddefea3c Mon Sep 17 00:00:00 2001 From: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Date: Fri, 7 Aug 2026 16:25:17 +0000 Subject: [PATCH 39/49] fix: hide screen meta links on upsell pages --- src/php/Admin/Menus/Manage/Manage_Menu.php | 4 ++++ .../Menus/Manage/Manage_Menu_Screen_Options.php | 17 +++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/php/Admin/Menus/Manage/Manage_Menu.php b/src/php/Admin/Menus/Manage/Manage_Menu.php index e3984f1fb..75fbbf5cb 100644 --- a/src/php/Admin/Menus/Manage/Manage_Menu.php +++ b/src/php/Admin/Menus/Manage/Manage_Menu.php @@ -186,6 +186,10 @@ public function load() { $this->screen_options->load(); + if ( $this->screen_options->is_upsell_view() ) { + return; + } + $contextual_help = new Contextual_Help( 'edit' ); $contextual_help->load(); } diff --git a/src/php/Admin/Menus/Manage/Manage_Menu_Screen_Options.php b/src/php/Admin/Menus/Manage/Manage_Menu_Screen_Options.php index 0f1bd9a25..59bc4512b 100644 --- a/src/php/Admin/Menus/Manage/Manage_Menu_Screen_Options.php +++ b/src/php/Admin/Menus/Manage/Manage_Menu_Screen_Options.php @@ -25,6 +25,13 @@ public function __construct() { public function load() { $screen = get_current_screen(); + if ( $this->is_upsell_view() ) { + // The upsell page has no table or settings of its own, so the whole + // screen-meta-links block (Screen Options + Help tabs) is unhooked. + add_filter( 'screen_options_show_screen', '__return_false' ); + return; + } + if ( $screen && ! $this->is_cloud_community_view() ) { add_filter( "manage_{$screen->id}_columns", [ $this, 'get_columns' ] ); add_filter( 'screen_settings', [ $this, 'render' ] ); @@ -112,6 +119,16 @@ public function is_cloud_community_view(): bool { return 'cloud-community' === $this->get_current_subpage(); } + /** + * Whether the current request renders an upsell page (Blueprints or Cloud + * Library on Core, where these features require Pro). + * + * @return bool + */ + public function is_upsell_view(): bool { + return in_array( $this->get_current_subpage(), [ 'blueprints', 'cloud-library' ], true ); + } + /** * Render the manage table controls. * From 6c7bc69cf53285c0b159de4f007e3d5f7f0b08f4 Mon Sep 17 00:00:00 2001 From: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Date: Fri, 7 Aug 2026 16:27:14 +0000 Subject: [PATCH 40/49] fix: consolidate CTA hover colors to #0ca0a9 --- src/css/common/_upsell.scss | 5 ++--- src/css/menu.scss | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/css/common/_upsell.scss b/src/css/common/_upsell.scss index 783278b4a..eb6c4f77b 100644 --- a/src/css/common/_upsell.scss +++ b/src/css/common/_upsell.scss @@ -1,4 +1,3 @@ -@use 'sass:color'; @use 'theme'; .code-snippets-upsell-banner { @@ -26,7 +25,7 @@ margin-inline-start: auto; &:hover, &:focus { - background-color: color.adjust(theme.$secondary, $lightness: -10%); + background-color: #0ca0a9; } } @@ -100,7 +99,7 @@ border-color: currentcolor; &:hover { - background-color: color.adjust(#d46f4d, $lightness: -10%); + background-color: #0ca0a9; } } } diff --git a/src/css/menu.scss b/src/css/menu.scss index 53aa21321..c4a9ec286 100644 --- a/src/css/menu.scss +++ b/src/css/menu.scss @@ -36,7 +36,7 @@ } &:hover { - background-color: #08c5d1; + background-color: #0ca0a9; } .dashicons { From 9703a7036e1d516794383a50d7948e527316ad61 Mon Sep 17 00:00:00 2001 From: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Date: Fri, 7 Aug 2026 16:36:02 +0000 Subject: [PATCH 41/49] fix: strengthen upsell CTA hover specificity, hide bundles tab header, avoid hardcoded subpage names --- src/css/common/_upsell.scss | 5 +++- .../CommunityCloud/CommunityCloud.tsx | 24 +++++++++++-------- .../Manage/Manage_Menu_Screen_Options.php | 10 +++++++- 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/css/common/_upsell.scss b/src/css/common/_upsell.scss index eb6c4f77b..90a70dac0 100644 --- a/src/css/common/_upsell.scss +++ b/src/css/common/_upsell.scss @@ -98,7 +98,10 @@ margin-inline: auto; border-color: currentcolor; - &:hover { + // Matches the specificity of the native-control override in _wp-admin.scss + // (.wrap .button-primary:not(.button-link):hover:not(:disabled)) so this + // upsell-specific hover colour isn't overridden by the shared button rule. + &:hover:not(.button-link):not(:disabled) { background-color: #0ca0a9; } } diff --git a/src/js/components/ManageMenu/CommunityCloud/CommunityCloud.tsx b/src/js/components/ManageMenu/CommunityCloud/CommunityCloud.tsx index e42dfa532..289c26852 100644 --- a/src/js/components/ManageMenu/CommunityCloud/CommunityCloud.tsx +++ b/src/js/components/ManageMenu/CommunityCloud/CommunityCloud.tsx @@ -57,19 +57,23 @@ const CommunityCloudInner = () => { }} /> - + {'bundles' === currentTab.name + ? null + : <> + -
      -

      { - // translators: %s: label of the currently selected community cloud tab. - sprintf(__('Community Cloud: %s', 'code-snippets'), currentTab.label)}

      -
      +
      +

      { + // translators: %s: label of the currently selected community cloud tab. + sprintf(__('Community Cloud: %s', 'code-snippets'), currentTab.label)}

      +
      -
      +
      -

      - {__('Search the community cloud and download user-shared snippets directly to your site.', 'code-snippets')} -

      +

      + {__('Search the community cloud and download user-shared snippets directly to your site.', 'code-snippets')} +

      + } diff --git a/src/php/Admin/Menus/Manage/Manage_Menu_Screen_Options.php b/src/php/Admin/Menus/Manage/Manage_Menu_Screen_Options.php index 59bc4512b..e153bde79 100644 --- a/src/php/Admin/Menus/Manage/Manage_Menu_Screen_Options.php +++ b/src/php/Admin/Menus/Manage/Manage_Menu_Screen_Options.php @@ -9,6 +9,14 @@ */ class Manage_Menu_Screen_Options { + /** + * Subpage slugs that render an upsell in place of their normal content + * (Pro-only features shown to non-Pro users). + * + * @var string[] + */ + private const UPSELL_SUBPAGES = [ 'blueprints', 'cloud-library' ]; + /** * Register hooks this class. */ @@ -126,7 +134,7 @@ public function is_cloud_community_view(): bool { * @return bool */ public function is_upsell_view(): bool { - return in_array( $this->get_current_subpage(), [ 'blueprints', 'cloud-library' ], true ); + return in_array( $this->get_current_subpage(), self::UPSELL_SUBPAGES, true ); } /** From e8409b75f51167cd8aec97705c78354d0b114758 Mon Sep 17 00:00:00 2001 From: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Date: Fri, 7 Aug 2026 16:43:20 +0000 Subject: [PATCH 42/49] fix: derive upsell view detection from existing view checks --- .../Manage/Manage_Menu_Screen_Options.php | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/php/Admin/Menus/Manage/Manage_Menu_Screen_Options.php b/src/php/Admin/Menus/Manage/Manage_Menu_Screen_Options.php index e153bde79..a9043449b 100644 --- a/src/php/Admin/Menus/Manage/Manage_Menu_Screen_Options.php +++ b/src/php/Admin/Menus/Manage/Manage_Menu_Screen_Options.php @@ -9,14 +9,6 @@ */ class Manage_Menu_Screen_Options { - /** - * Subpage slugs that render an upsell in place of their normal content - * (Pro-only features shown to non-Pro users). - * - * @var string[] - */ - private const UPSELL_SUBPAGES = [ 'blueprints', 'cloud-library' ]; - /** * Register hooks this class. */ @@ -128,13 +120,17 @@ public function is_cloud_community_view(): bool { } /** - * Whether the current request renders an upsell page (Blueprints or Cloud - * Library on Core, where these features require Pro). + * Whether the current request renders an upsell page. + * + * Every genuine Core subpage has its own positive `is_*_view()` detection + * above; a subpage that matches none of them (Blueprints, Cloud Library, + * and any future Pro-only addition) has no real content of its own and + * falls through to the upsell placeholder. * * @return bool */ public function is_upsell_view(): bool { - return in_array( $this->get_current_subpage(), self::UPSELL_SUBPAGES, true ); + return ! $this->is_manage_table_view() && ! $this->is_cloud_community_view(); } /** From c64550edad221c9e1ca8357b77765f18dfdd0dcb Mon Sep 17 00:00:00 2001 From: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Date: Fri, 7 Aug 2026 16:49:10 +0000 Subject: [PATCH 43/49] fix: match upsell CTA hover border color --- src/css/common/_upsell.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/css/common/_upsell.scss b/src/css/common/_upsell.scss index 90a70dac0..f6039818a 100644 --- a/src/css/common/_upsell.scss +++ b/src/css/common/_upsell.scss @@ -100,9 +100,10 @@ // Matches the specificity of the native-control override in _wp-admin.scss // (.wrap .button-primary:not(.button-link):hover:not(:disabled)) so this - // upsell-specific hover colour isn't overridden by the shared button rule. + // upsell-specific hover styling isn't overridden by the shared button rule. &:hover:not(.button-link):not(:disabled) { background-color: #0ca0a9; + border-color: #0ca0a9; } } } From d905c643b0ff4037844145abd6e6ed6efd03efb0 Mon Sep 17 00:00:00 2001 From: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Date: Fri, 7 Aug 2026 16:49:10 +0000 Subject: [PATCH 44/49] fix: treat bundles tab as upsell view --- .../Menus/Manage/Manage_Menu_Screen_Options.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/php/Admin/Menus/Manage/Manage_Menu_Screen_Options.php b/src/php/Admin/Menus/Manage/Manage_Menu_Screen_Options.php index a9043449b..33d462993 100644 --- a/src/php/Admin/Menus/Manage/Manage_Menu_Screen_Options.php +++ b/src/php/Admin/Menus/Manage/Manage_Menu_Screen_Options.php @@ -113,10 +113,21 @@ public function is_manage_table_view(): bool { /** * Whether the current request renders Community Cloud. * + * The "Bundles" tab within Community Cloud is a Pro-only upsell rather than + * genuine Community Cloud content, so it is excluded here and picked up by + * {@see is_upsell_view()} instead. + * * @return bool */ public function is_cloud_community_view(): bool { - return 'cloud-community' === $this->get_current_subpage(); + if ( 'cloud-community' !== $this->get_current_subpage() ) { + return false; + } + + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only routing parameter. + $tab = isset( $_REQUEST['tab'] ) ? sanitize_key( wp_unslash( $_REQUEST['tab'] ) ) : ''; + + return 'bundles' !== $tab; } /** From 4f7583c81a5509648851955474b2db0ecf29a1df Mon Sep 17 00:00:00 2001 From: code-snippets-bot <139164393+code-snippets-bot@users.noreply.github.com> Date: Fri, 7 Aug 2026 16:58:44 +0000 Subject: [PATCH 45/49] fix: hide screen meta on bundles tab instead of unmounting --- .../ManageMenu/CommunityCloud/CommunityCloud.tsx | 4 ++-- src/js/components/common/ScreenMetaSlot.tsx | 12 ++++++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/js/components/ManageMenu/CommunityCloud/CommunityCloud.tsx b/src/js/components/ManageMenu/CommunityCloud/CommunityCloud.tsx index 289c26852..e6f8ee611 100644 --- a/src/js/components/ManageMenu/CommunityCloud/CommunityCloud.tsx +++ b/src/js/components/ManageMenu/CommunityCloud/CommunityCloud.tsx @@ -57,11 +57,11 @@ const CommunityCloudInner = () => { }} /> +