Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
7ad52a2
fix: restore snippet description hydration
imantsk Mar 10, 2026
25c28e9
fix: activate new snippets on first save
imantsk Mar 10, 2026
3bbb18e
fix: surface activation errors after save
imantsk Mar 10, 2026
9c93621
fix: restore bulk snippet export
imantsk Mar 10, 2026
0aec73f
fix: correct admin bar edit links
imantsk Mar 10, 2026
5501b7c
fix: remove the edit snippet submenu
imantsk Mar 10, 2026
09282f7
fix: avoid flat files option hook fatals
imantsk Mar 10, 2026
98db56c
fix: focus editor from edit menu item
imantsk Mar 10, 2026
df40f28
fix: restore pointer cursor on edit menu item
imantsk Mar 10, 2026
044f710
fix: move stack trace notices above snippet form
imantsk Mar 10, 2026
f346bf4
fix: polish traced activation notices
imantsk Mar 10, 2026
a407445
chore: centralize notice styling
imantsk Mar 10, 2026
7d3ec10
fix: update notice styling and add horizontal scroll hint
imantsk Mar 10, 2026
5560814
fix: emphasize activation failure message
imantsk Mar 10, 2026
ae1f96a
fix: remove editor back link
imantsk Mar 10, 2026
09e69d1
feat: add snippet table column screen options
imantsk Mar 10, 2026
746ecea
fix: left align modified column content
imantsk Mar 10, 2026
c25dde9
fix: match wordpress sorted header classes
imantsk Mar 10, 2026
a57c81d
feat: add snippets table truncation screen option
imantsk Mar 10, 2026
fb7d64c
fix: limit snippets table truncation to content columns
imantsk Mar 10, 2026
9e35d19
fix: scope snippets table truncation to name and description
imantsk Mar 10, 2026
9b67988
fix: correct snippets table name truncation
imantsk Mar 10, 2026
e57ed0e
fix: refactor cloud search form styles and remove redundant code
imantsk Mar 10, 2026
78b870e
fix: adjust max-inline-size for snippet name and description columns
imantsk Mar 10, 2026
bd05150
fix: remove description for truncation option in manage menu
imantsk Mar 10, 2026
c229fa5
fix: update legend text case in table options fieldset
imantsk Mar 10, 2026
228c25c
fix: apply snippets pagination to cloud search
imantsk Mar 10, 2026
ffac81c
fix: cap cloud search page size
imantsk Mar 10, 2026
b5f3ad2
fix: enhance manage menu to handle cloud community view and update sc…
imantsk Mar 10, 2026
7c4fd4d
fix: enhance manage menu tests for cloud community view and update sc…
imantsk Mar 10, 2026
73e4ed9
fix: add bulk snippet code downloads
imantsk Mar 10, 2026
4726db9
chore: reduce phpcs suppressions
imantsk Mar 10, 2026
a1f09ed
fix: refine bulk snippet downloads
imantsk Mar 10, 2026
b370ee9
fix: reuse bulk snippet downloads
imantsk Mar 10, 2026
e3494cb
fix: preserve selected bulk action
imantsk Mar 10, 2026
a5b118d
fix: disable apply for default bulk action
imantsk Mar 10, 2026
7922e1b
fix: restore table nav build compatibility
imantsk Mar 10, 2026
7fc3e48
fix: disable apply without bulk action
imantsk Mar 10, 2026
26b1988
fix: move edit menu link handling into edit app
imantsk Mar 10, 2026
f150858
fix: remove html from activation notice translation
imantsk Mar 10, 2026
9ffbee1
fix: localize stack trace hint
imantsk Mar 10, 2026
153db68
fix: validate network snippet downloads
imantsk Mar 10, 2026
9018653
fix: restore activation error reason
imantsk Mar 10, 2026
431e318
fix: emphasize activation error reason
imantsk Mar 10, 2026
6ae73d4
fix: show stack trace hint only on overflow
imantsk Mar 10, 2026
766ca16
fix: scope bulk actions to visible selections
imantsk Mar 10, 2026
e16e5ac
fix: keep cloud screen options from clearing truncation preference
imantsk Mar 10, 2026
27afcc2
fix: remove disabled aria from edit menu shortcut
imantsk Mar 10, 2026
d8cfd71
fix: standardize array syntax and improve download handling in Manage…
imantsk Mar 10, 2026
21aac95
fix: correct activation error notice assertions in E2E test
imantsk Mar 10, 2026
74a778d
fix: add comments to clarify column visibility fieldset selection logic
imantsk Mar 10, 2026
bac601a
fix: prioritize cloud search per page setting in ManageMenu
imantsk Mar 10, 2026
590ac1b
fix: add cloudSearchPerPage to CODE_SNIPPETS_MANAGE interface
imantsk Mar 10, 2026
4cc31c3
fix: add cloudSearchPerPage method and integrate into Manage_Menu
imantsk Mar 10, 2026
e279188
fix: optimize file handling in build_archive_download method
imantsk Mar 10, 2026
e1ac6ea
fix: add bulk export test for scoped snippet selection and enhance sc…
imantsk Mar 10, 2026
8784a8c
fix: hide edit menu item outside editor
imantsk Mar 10, 2026
1ffaf0d
fix: use current screen for edit menu visibility
imantsk Mar 10, 2026
09bd77a
fix: restore cloud pagination screen options
imantsk Mar 10, 2026
53577f2
fix: scope snippet helper list lookups
imantsk Mar 10, 2026
640a7d5
fix: normalize validator exceptions for pluggable declarations
imantsk Mar 10, 2026
397d49e
fix: avoid mbstring dependency for downloads
imantsk Mar 10, 2026
20afd76
fix: remove html from activation error notice
imantsk Mar 10, 2026
22caa81
fix: align snippet export schema and bulk export fields
imantsk Mar 10, 2026
3a36282
fix: bold activation error message without translating it
imantsk Mar 10, 2026
8200a7e
Add playwright-report to eslint ignores.
sheabunge Mar 11, 2026
323d791
fix: expand practical reviewer checks in code review instructions
imantsk Mar 12, 2026
72b6b0d
fix: simplify list table nav props
imantsk Mar 12, 2026
b8e442a
fix: inline sortable list table headings
imantsk Mar 12, 2026
19ed1ea
fix: use classnames for list table cell classes
imantsk Mar 12, 2026
ecfa584
fix: tighten list table bulk actions typing
imantsk Mar 12, 2026
14d0ba5
fix: simplify focus editor shortcut hook
imantsk Mar 12, 2026
92c7e01
fix: use undefined for optional edit menu attrs
imantsk Mar 12, 2026
3c7afe1
fix: switch activation error notice to jsx
imantsk Mar 12, 2026
c511d32
fix: simplify export default value checks
imantsk Mar 12, 2026
6f38fe3
fix: add filter for default snippets per page
imantsk Mar 12, 2026
0c70958
fix: clarify active shared network sync guard
imantsk Mar 12, 2026
b9ca72a
fix: call global wp_hash with early fallback
imantsk Mar 12, 2026
c11d819
fix: remove redundant ParseError catch blocks
imantsk Mar 12, 2026
2e57771
chore: align phpunit test docs with wpcs
imantsk Mar 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .github/instructions/code-review.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,20 @@ Severity labels used in this file:
- **MUST** — Flag as a blocking issue; must be resolved before merge.
- **SHOULD** — Flag as a recommendation; resolve before merge unless a risk-aware rationale is provided.

## Practical Reviewer Checks

- Don't silently widen types or drop generics when refactoring. If typing gets weaker, require a clear reason. [MUST]
- In namespaced PHP, call WordPress globals with `\function_name()` and do not assume pluggable/core functions exist on very early execution paths. If a callback can run during early bootstrap, guard availability appropriately. [MUST]
- Ensure hook callbacks that respond to options/actions are tightly gated to the intended option/action. Be suspicious of inverted or overly broad conditionals that can be triggered by other plugins. [MUST]
- Avoid no-op abstractions (pass-through helpers, one-liner wrappers) unless they materially improve readability, reuse, or testability. [SHOULD]
- Inline single-use extractions that do not clarify intent (local functions/variables used once). [SHOULD]
- Prefer `undefined` for absent optional values in TypeScript/React unless `null` has explicit semantics in that API. [SHOULD]
- For conditional class names, prefer the repository `classnames.classnames` helper over manual array filtering and joining. [SHOULD]
- Prefer JSX for React markup. If a hook/util needs to render elements, suggest moving the markup into a `.tsx` component instead of using `createElement` in a `.ts` file. [SHOULD]
- For simple key-to-value parsing/transforms, prefer a literal object/record map over a loop + `switch` when it improves clarity. [SHOULD]
- Do not stack redundant `catch` blocks (e.g., `ParseError` plus `Throwable`) unless the handlers differ. [SHOULD]
- When a screen is React-driven, question heavy PHP view logic. If PHP is used due to WordPress admin primitives (e.g., Screen Options, non-REST file streaming), require a short rationale. [SHOULD]

## Scope and Diff Hygiene

- Flag PRs that mix behavior changes with refactors, renames, or formatting-only edits. Each change should be single-purpose. [MUST]
Expand Down
6 changes: 5 additions & 1 deletion eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ export default eslintTs.config(
rules: reactHooks.configs.recommended.rules,
},
{
ignores: ['bundle/*', 'src/dist/*', 'src/vendor/*', 'svn/*', '*.config.mjs', '*.config.js', '.*/*', 'tmp/*']
ignores: [
'bundle/*', 'src/dist/*', 'src/vendor/*', 'svn/*',
'*.config.mjs', '*.config.js',
'.*/*', 'tmp/*', 'playwright-report/*'
]
},
{
languageOptions: {
Expand Down
27 changes: 27 additions & 0 deletions src/css/common/_notices.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
.code-snippets-notice {
.notice-dismiss {
position: absolute;
transform: initial;
inset-inline-end: 0;
inset-block-start: 0;
}

details {
margin: .5em 0;
padding: 2px;

summary {
cursor: pointer;
}

pre {
max-inline-size: 100%;
overflow: auto hidden;
white-space: pre;
}

.stack-trace-hint {
opacity: 0.5;
}
}
}
15 changes: 5 additions & 10 deletions src/css/edit.scss
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
@use 'common/select';
@use 'common/tooltips';
@use 'common/modal';
@use 'common/notices';
@use 'common/upsell';
@use 'common/toolbar';
@use 'edit/form';
Expand All @@ -28,6 +29,10 @@
margin: 0;
}

#adminmenu a.code-snippets-edit-menu-link {
cursor: pointer;
}

.snippet-description-container {
.wp-editor-tools {
padding-block-start: 5px;
Expand Down Expand Up @@ -96,13 +101,3 @@
form.condition-snippet .snippet-code-container {
display: none;
}

.cs-back {
cursor: pointer;

&::before {
content: '<';
color: #2271b1;
margin-inline-end: 3px;
}
}
5 changes: 1 addition & 4 deletions src/css/manage.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
@use 'common/tooltips';
@use 'common/direction';
@use 'common/select';
@use 'common/notices';
@use 'common/upsell';
@use 'common/toolbar';
@use 'prism';
Expand Down Expand Up @@ -61,10 +62,6 @@
}
}

.code-snippets-notice a.notice-dismiss {
text-decoration: none;
}

.refresh-button-container {
display: flex;
align-items: center;
Expand Down
102 changes: 51 additions & 51 deletions src/css/manage/_cloud-community.scss
Original file line number Diff line number Diff line change
@@ -1,12 +1,61 @@
@use '../common/theme';
@use '../common/banners';

.cloud-search-form {
display: flex;
gap: 8px;
margin-block: 31px 47px;
block-size: 54px;

> select {
flex: 0 0 250px;
}

.button {
flex: 0 0 165px;
}

.cloud-search-query {
flex: 1;
position: relative;

input {
inline-size: 100%;
block-size: 100%;
}

> .components-spinner {
position: absolute;
inset-inline-end: 1.5em;
inset-block-start: 25%;
}
}
}

.cloud-search {
@include banners.banners;

.banner {
justify-content: center;
}

.tablenav.top {
display: flex;
gap: 20px;
block-size: 40px;
margin-block-end: 31px;
align-items: center;

select {
inline-size: 245px;
block-size: 100%;
}

.tablenav-pages {
margin: 0;
margin-inline-start: auto;
}
}
}

.cloud-search-results {
Expand Down Expand Up @@ -79,10 +128,6 @@
padding-block: 12px;
align-items: center;

.components-spinner {
margin: 0;
}

.dashicons-warning {
color: #b32d2e;
}
Expand All @@ -100,51 +145,6 @@
}
}

.cloud-search-form {
display: flex;
gap: 8px;
margin-block: 31px 47px;
block-size: 54px;

select {
flex: 0 0 250px;
}

.button {
flex: 0 0 165px;
}

.cloud-search-query {
flex: 1;
position: relative;

input {
inline-size: 100%;
block-size: 100%;
}

.components-spinner {
position: absolute;
inset-inline-end: 1.5em;
inset-block-start: 25%;
}
}
}

.tablenav.top {
display: flex;
gap: 20px;
block-size: 40px;
margin-block-end: 31px;
align-items: center;

select {
inline-size: 245px;
block-size: 100%;
}

.tablenav-pages {
margin: 0;
margin-inline-start: auto;
}
.cloud-search-results .cloud-search-result footer > .components-spinner {
margin: 0;
}
23 changes: 22 additions & 1 deletion src/css/manage/_snippets-table.scss
Original file line number Diff line number Diff line change
Expand Up @@ -166,10 +166,31 @@
min-inline-size: 130px;
max-inline-size: 130px;
text-align: end;
padding-inline: 8px;
overflow: hidden;
text-overflow: ellipsis;
}

td.column-date .modified-column-content {
display: block;
text-align: start;
}
}

.wp-list-table.truncate-row-values {
td.column-name > .snippet-name,
td.column-desc .snippet-description-content {
display: block;
overflow: hidden;
text-overflow: ellipsis;
}

td.column-name > .snippet-name {
max-inline-size: min(15rem, 30vw);
}

td.column-desc .snippet-description-content {
max-inline-size: min(25rem, 45vw);
}
}

.wp-core-ui .button.clear-filters {
Expand Down
119 changes: 116 additions & 3 deletions src/js/components/EditMenu/EditMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,118 @@
import React from 'react'
import React, { useEffect } from 'react'
import { SnippetForm } from './SnippetForm'

export const EditMenu = () =>
<SnippetForm />
const EVENT_NAME = 'code_snippets_focus_editor'

interface EditMenuLinkBinding {
menuLink: HTMLAnchorElement
originalHref: string | undefined
originalRole: string | undefined
originalTabIndex: string | undefined
handleClick: (event: MouseEvent) => void
handleKeyDown: (event: KeyboardEvent) => void
}

const focusCodeEditor = () => {
window.dispatchEvent(new CustomEvent(EVENT_NAME))
}

const restoreAttribute = (menuLink: HTMLAnchorElement, name: string, value: string | undefined) => {
if (undefined !== value) {
menuLink.setAttribute(name, value)
return
}

menuLink.removeAttribute(name)
}

const getEditPage = (): string | undefined => {
const editUrl = window.CODE_SNIPPETS?.urls.edit

return editUrl ? new URL(editUrl, window.location.origin).searchParams.get('page') ?? undefined : undefined
}

const bindEditMenuLink = (menuLink: HTMLAnchorElement, page: string): EditMenuLinkBinding | undefined => {
const menuUrl = new URL(menuLink.href, window.location.origin)

if (page !== menuUrl.searchParams.get('page')) {
return undefined
}

const handleClick = (event: MouseEvent) => {
event.preventDefault()
focusCodeEditor()
}

const handleKeyDown = (event: KeyboardEvent) => {
if ('Enter' !== event.key && ' ' !== event.key) {
return
}

event.preventDefault()
focusCodeEditor()
}

const binding = {
menuLink,
originalHref: menuLink.getAttribute('href') ?? undefined,
originalRole: menuLink.getAttribute('role') ?? undefined,
originalTabIndex: menuLink.getAttribute('tabindex') ?? undefined,
handleClick,
handleKeyDown
}

menuLink.dataset.codeSnippetsDisabled = 'true'
menuLink.setAttribute('role', 'button')
menuLink.setAttribute('tabindex', '0')
menuLink.classList.add('code-snippets-edit-menu-link')
menuLink.removeAttribute('href')
menuLink.addEventListener('click', handleClick)
menuLink.addEventListener('keydown', handleKeyDown)

return binding
}

const unbindEditMenuLink = ({
menuLink,
originalHref,
originalRole,
originalTabIndex,
handleClick,
handleKeyDown
}: EditMenuLinkBinding) => {
menuLink.removeEventListener('click', handleClick)
menuLink.removeEventListener('keydown', handleKeyDown)
menuLink.classList.remove('code-snippets-edit-menu-link')
delete menuLink.dataset.codeSnippetsDisabled

restoreAttribute(menuLink, 'href', originalHref)
restoreAttribute(menuLink, 'role', originalRole)
restoreAttribute(menuLink, 'tabindex', originalTabIndex)
}

const useEditMenuLinkFocus = () => {
useEffect(() => {
const page = getEditPage()

if (!page) {
return
}

const editMenuLinks = Array.from(document.querySelectorAll<HTMLAnchorElement>('#adminmenu a[href]'))
.flatMap(menuLink => {
const binding = bindEditMenuLink(menuLink, page)

return binding ? [binding] : []
})

return () => {
editMenuLinks.forEach(unbindEditMenuLink)
}
}, [])
}

export const EditMenu = () => {
useEditMenuLinkFocus()

return <SnippetForm />
}
Loading
Loading