Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Changelog

## [4.0.0] (UPCOMING)
## [4.0.0] (2026-10-12)

### Added
* Stats page for viewing snippet totals and various snippet breakdowns.
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "code-snippets",
"description": "Manage code snippets running on a WordPress-powered site through a graphical interface.",
"homepage": "https://codesnippets.pro",
"version": "4.0.0-beta.3",
"version": "4.0.0",
"main": "src/dist/edit.js",
"directories": {
"test": "tests"
Expand Down
6 changes: 3 additions & 3 deletions src/code-snippets.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@
* License: GPL-2.0-or-later
* License URI: license.txt
* Text Domain: code-snippets
* Version: 4.0.0-beta.3
* Version: 4.0.0
* Requires PHP: 7.4
* Requires at least: 5.5
*
* @version 4.0.0-beta.3
* @version 4.0.0
* @package Code_Snippets
* @author Shea Bunge <shea@codesnippets.pro>
* @copyright 2012-2026 Code Snippets Pro
Expand All @@ -37,7 +37,7 @@
*
* @const string
*/
define( 'CODE_SNIPPETS_VERSION', '4.0.0-beta.3' );
define( 'CODE_SNIPPETS_VERSION', '4.0.0' );

/**
* The full path to the main file of this plugin.
Expand Down
12 changes: 12 additions & 0 deletions src/css/common/_demo.scss
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,18 @@
}
}

.wp-core-ui .button.demo-callout__close {
position: absolute;
inset-block-start: 8px;
inset-inline-end: 8px;
color: var(--cs-color-text-secondary);
text-decoration: none;

&:hover:not(:disabled) {
color: var(--cs-color-text);
}
}

.demo-callout__step {
display: block;
margin-block-end: 6px;
Expand Down
4 changes: 2 additions & 2 deletions src/js/components/FeedbackReporter/FeedbackForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import type { FeedbackConfig, FeedbackDraft } from '../../types/Feedback'

const TYPE_OPTIONS = [
{ label: __('Choose one…', 'code-snippets'), value: '' },
{ label: __('Bug — something is broken', 'code-snippets'), value: 'bug' },
{ label: __('Feature request — something is missing', 'code-snippets'), value: 'feature' },
{ label: __('Bug: something is broken', 'code-snippets'), value: 'bug' },
{ label: __('Feature request: something is missing', 'code-snippets'), value: 'feature' },
{ label: __('General feedback', 'code-snippets'), value: 'feedback' }
]

Expand Down
2 changes: 1 addition & 1 deletion src/js/components/ManageMenu/AiAgentDemo/AiAgentDemo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ const AiAgentDemoPage: React.FC = () => {
<section className="ai-agent ai-agent-demo" aria-label={__('AI Agent demo', 'code-snippets')}>
<div className="screen-reader-text" aria-live="polite">{STAGE_ANNOUNCEMENTS[stage]}</div>

<DemoCallout callout={getCallout(stage)} />
<DemoCallout callout={getCallout(stage)} onClose={skip} />

<DemoSpotlight {...STAGE_SPOTLIGHTS[stage]} />

Expand Down
4 changes: 2 additions & 2 deletions src/js/components/ManageMenu/AiAgentDemo/DemoUpsell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { DemoUpsell } from '../../common/demo/DemoUpsell'

export const AiAgentDemoUpsell: React.FC<{ onReplay: VoidFunction }> = ({ onReplay }) =>
<DemoUpsell
title={__('That was a demo — the real agent writes for you', 'code-snippets')}
title={__('That was a demo: the real agent writes for you', 'code-snippets')}
onReplay={onReplay}
>
<p>{__('The whole walkthrough was scripted and ran inside this plugin. No prompt or snippet left your site, and none were added to it.', 'code-snippets')}</p>
<p>{__('In Code Snippets Pro the AI Agent does all of this for whatever you ask it to build, and the snippets it writes are real — saved to your site inactive, until you have read them and switched them on.', 'code-snippets')}</p>
<p>{__('In Code Snippets Pro, the AI Agent does all of this for whatever you ask it to build, and the snippets it writes are real. They are saved to your site switched off, so nothing runs until you have read them and turned them on.', 'code-snippets')}</p>
</DemoUpsell>
4 changes: 2 additions & 2 deletions src/js/components/ManageMenu/AiAgentDemo/callouts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ const STEPS: Record<string, CalloutContent> = {
ask: {
step: __('Step 1 of 5', 'code-snippets'),
title: __('Ask in plain English', 'code-snippets'),
body: __('No syntax to learn and no boilerplate to copy — describe what you want the site to do, and the agent works out what that means in code.', 'code-snippets')
body: __('No syntax to learn and no boilerplate to copy. Describe what you want the site to do, and the agent works out what that means in code.', 'code-snippets')
},
plan: {
step: __('Step 2 of 5', 'code-snippets'),
title: __('Sent for planning', 'code-snippets'),
body: __('Your request goes to Code Snippets Cloud, which works out what needs building — and whether that is one snippet or several — before writing anything.', 'code-snippets')
body: __('Your request goes to Code Snippets Cloud, which works out what needs building, whether that is one snippet or several, before writing anything.', 'code-snippets')
},
approve: {
step: __('Step 3 of 5', 'code-snippets'),
Expand Down
2 changes: 1 addition & 1 deletion src/js/components/ManageMenu/AiAgentDemo/demoScript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export const getRefinementPrompt = (siteName: string): string =>
siteName
)

export const DEMO_REFINEMENT_REPLY = __('Done — the banner now greets visitors by name and uses a warmer gradient.', 'code-snippets')
export const DEMO_REFINEMENT_REPLY = __('Done. The banner now greets visitors by name and uses a warmer gradient.', 'code-snippets')

/**
* Escape a value for embedding inside a single-quoted PHP string literal.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ const BlueprintsDemoPage: React.FC = () => {
<div className="blueprints-demo">
<div className="screen-reader-text" aria-live="polite">{STAGE_ANNOUNCEMENTS[stage]}</div>

<DemoCallout callout={getCallout(stage)} />
<DemoCallout callout={getCallout(stage)} onClose={skip} />

<DemoSpotlight {...STAGE_SPOTLIGHTS[stage]} />

Expand All @@ -124,11 +124,11 @@ const BlueprintsDemoPage: React.FC = () => {
{showGenerated && <GeneratedNotice ref={generatedRef} />}

{isFinished && <DemoUpsell
title={__('That was a demo — Blueprints build the code for you', 'code-snippets')}
title={__('That was a demo: Blueprints build the code for you', 'code-snippets')}
onReplay={replay}
>
<p>{__('The whole walkthrough was scripted and ran inside this plugin. No code was generated and nothing was saved.', 'code-snippets')}</p>
<p>{__('Code Snippets Pro ships blueprints for shortcodes, post types, taxonomies, settings pages, and more — fill in the form and it writes the snippet for you.', 'code-snippets')}</p>
<p>{__('Code Snippets Pro ships blueprints for shortcodes, post types, taxonomies, settings pages, and more. Fill in the form and it writes the snippet for you.', 'code-snippets')}</p>
</DemoUpsell>}
</div>
</>
Expand Down
4 changes: 2 additions & 2 deletions src/js/components/ManageMenu/BlueprintsDemo/callouts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const CALLOUTS: Partial<Record<DemoStage, CalloutContent>> = {
general: {
step: __('Step 1 of 3', 'code-snippets'),
title: __('Name the shortcode', 'code-snippets'),
body: __('Every blueprint starts with the basics — the tag you type into a post, and the function that powers it.', 'code-snippets')
body: __('Every blueprint starts with the basics: the tag you type into a post, and the function that powers it.', 'code-snippets')
},
attributes: {
step: __('Step 2 of 3', 'code-snippets'),
Expand All @@ -25,7 +25,7 @@ const CALLOUTS: Partial<Record<DemoStage, CalloutContent>> = {
generating: {
step: __('Generating', 'code-snippets'),
title: __('Building the snippet', 'code-snippets'),
body: __('Pro turns the whole form into a finished, working snippet — no boilerplate to write yourself.', 'code-snippets')
body: __('Pro turns the whole form into a finished, working snippet, with no boilerplate for you to write.', 'code-snippets')
},
generated: {
step: __('Done', 'code-snippets'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export const CloudLibraryDemo: React.FC = () => {
<div className="cloud-library-demo">
<div className="screen-reader-text" aria-live="polite">{STAGE_ANNOUNCEMENTS[stage]}</div>

<DemoCallout callout={getCallout(stage)} />
<DemoCallout callout={getCallout(stage)} onClose={skip} />

<DemoSpotlight {...STAGE_SPOTLIGHTS[stage]} />

Expand All @@ -93,7 +93,7 @@ export const CloudLibraryDemo: React.FC = () => {

{isFinished && <div ref={upsellRef} className="cloud-library-demo__closing">
<DemoUpsell
title={__('That was a demo — your cloud travels with you', 'code-snippets')}
title={__('That was a demo: your cloud travels with you', 'code-snippets')}
onReplay={replay}
>
<p>{__('The whole walkthrough was scripted and ran inside this plugin. These snippets are examples, and nothing was downloaded or saved.', 'code-snippets')}</p>
Expand Down
4 changes: 2 additions & 2 deletions src/js/components/ManageMenu/CloudLibraryDemo/callouts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const STEPS: Partial<Record<DemoStage, CalloutContent>> = {
library: {
step: __('Step 1 of 4', 'code-snippets'),
title: __('Your whole cloud, in one place', 'code-snippets'),
body: __('Every snippet and bundle saved to your Code Snippets Cloud account is listed here, on every site you connect — so work you did once is never more than a click away.', 'code-snippets')
body: __('Every snippet and bundle saved to your Code Snippets Cloud account is listed here, on every site you connect. Work you did once is never more than a click away.', 'code-snippets')
},
preview: {
step: __('Step 2 of 4', 'code-snippets'),
Expand All @@ -16,7 +16,7 @@ const STEPS: Partial<Record<DemoStage, CalloutContent>> = {
download: {
step: __('Step 3 of 4', 'code-snippets'),
title: __('Download in one click', 'code-snippets'),
body: __('The snippet lands in your snippets table straight away — and inactive, so nothing runs until you have looked it over and switched it on.', 'code-snippets')
body: __('The snippet lands in your snippets table straight away, switched off, so nothing runs until you have looked it over and switched it on.', 'code-snippets')
},
synced: {
step: __('Step 4 of 4', 'code-snippets'),
Expand Down
23 changes: 16 additions & 7 deletions src/js/components/common/demo/DemoCallout.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import React from 'react'
import { __ } from '@wordpress/i18n'
import { Button } from '../Button'

export interface CalloutContent {
step: string
Expand All @@ -10,17 +12,24 @@ export interface CalloutContent {
* Commentary on what a walkthrough is doing at the current stage, floating in
* the corner of the viewport so it stays out of the page's flow.
*
* The demo pages announce each stage through their own live region, so this is
* hidden from assistive technology rather than repeating it.
* The demo pages announce each stage through their own live region, so the
* commentary is hidden from assistive technology rather than repeating it. The
* close button stays exposed, since it is the walkthrough's exit.
*/
export const DemoCallout: React.FC<{ callout?: CalloutContent }> = ({ callout }) =>
export const DemoCallout: React.FC<{ callout?: CalloutContent, onClose: VoidFunction }> = ({ callout, onClose }) =>
callout
// Keyed on the content rather than the stage: consecutive stages can
// share a callout, and remounting between them replays the entrance
// animation as a flicker.
? <aside key={callout.title} className="demo-callout" aria-hidden="true">
<span className="demo-callout__step">{callout.step}</span>
<h3 className="demo-callout__title">{callout.title}</h3>
<p className="demo-callout__body">{callout.body}</p>
? <aside key={callout.title} className="demo-callout">
<Button small link className="demo-callout__close" onClick={onClose} aria-label={__('Exit demo', 'code-snippets')}>
<span className="dashicons dashicons-no-alt" aria-hidden="true"></span>
</Button>

<div aria-hidden="true">
<span className="demo-callout__step">{callout.step}</span>
<h3 className="demo-callout__title">{callout.title}</h3>
<p className="demo-callout__body">{callout.body}</p>
</div>
</aside>
: null
4 changes: 2 additions & 2 deletions src/js/utils/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ export const describeRequestError = (error: unknown): string => {

if (!error.response) {
return __(
'No response came back, so this could not be confirmed. The change may already have been saved — ' +
'check in another tab before trying again.',
'No response came back, so this could not be confirmed. The change may already be saved, ' +
'so check in another tab before trying again.',
'code-snippets'
)
}
Expand Down
83 changes: 82 additions & 1 deletion src/readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Donate link: https://codesnippets.pro
Tags: code, snippets, multisite, php, css
License: GPL-2.0-or-later
License URI: license.txt
Stable tag: 3.10.2
Stable tag: 4.0.0
Requires at least: 5.5
Tested up to: 7.1
Requires PHP: 7.4
Expand Down Expand Up @@ -106,6 +106,87 @@ You can report security bugs found in the source code of this plugin through the

== Changelog ==

= 4.0.0 (2026-10-12) =

__Added__

* Stats page for viewing snippet totals and various snippet breakdowns.
* AI Agent for building snippets from a description: it proposes a plan you can refine or approve before any code is
written, and can revise the snippets it created. (PRO)
* Snippet revisions, with a history of past versions, a side-by-side diff against the current code, and one-click
restore. (PRO)
* Display conditions for controlling where and when a snippet runs, without writing the checks by hand. (PRO)
* Per-role snippet permissions, so you can decide which roles may view, edit, activate, or delete snippets. (PRO)
* Blueprints for setting up a site from a saved collection of snippets and settings. (PRO)
* Flat file storage, for keeping snippets as files so they can be version-controlled alongside the rest of a site. (PRO)
* "Ran on this page" tracking, showing which snippets actually executed on the page you are viewing. (PRO)
* Natural-language search in Community Cloud, so you can describe what you need instead of guessing keywords. (PRO)
* Snippet deployment from Code Snippets Cloud to connected sites, including deploying several snippets under one shared
display condition. (PRO)
* Installing a cloud bundle onto a connected site as a standalone plugin. (PRO)
* Updating Code Snippets Pro on a connected site from the cloud dashboard. (PRO)
* Drift detection, which reports when a snippet on the site no longer matches the copy stored in the cloud. (PRO)
* New admin interface for managing snippets, with a cleaner layout, faster interactions, and a more consistent
experience across plugin screens. (PRO)
* Card view for browsing snippets, with a view switcher on the snippets table and Community Cloud. (PRO)
* Snippet preview modal for viewing snippet code from the snippets table without opening the editor. (PRO)
* Automatic hiding of unrelated admin notices from other plugins on Code Snippets screens. (PRO)
* Admin bar snippet drawer, based on the Deckerweb Snippets workflow, for quick access to snippets and Safe Mode from
the WordPress admin bar. (PRO)
* Snippet locking to help prevent accidental edits or deletion of important snippets. Props
to https://github.com/mgiannopoulos24. (PRO)
* Improved screen options on the main snippets table, including controls for visible columns and truncating long snippet
names or descriptions. (PRO)
* Bulk actions and bulk code download support in the redesigned snippets table. (PRO)
* Featured snippets and improved browsing in Community Cloud. (PRO)
* WordPress modern theme admin styling compatibility. (PRO)
* Clearer accessibility labels, headings, tab markup, table checkboxes, sort buttons, copy buttons, and drag-and-drop
upload controls. (PRO)
* Feedback reporter for sending bug reports, feature requests and general feedback from the plugin screens, with an
optional summary of the site environment so the team can reproduce the problem. (PRO)

__Changed__

* Redesigned the main snippets table with improved search, filtering, sorting, pagination, row actions, and bulk
selection. (PRO)
* Improved the snippet import and migration experience, including clearer file upload handling and third-party plugin
migration flows. (PRO)
* Improved snippet error handling so activation failures, validation errors, and stack traces are easier to understand.
(PRO)
* Improved Community Cloud search and filtering, including server-side filters, better result loading, and clearer empty
states. (PRO)
* Updated the welcome screen, toolbar, import screen, and cloud screens to match the new admin experience. (PRO)
* Updated internal plugin architecture to a cleaner PSR-4 structure for better long-term maintainability. (PRO)
* Improved accessibility across the snippets table, import screen, migration flow, Community Cloud, welcome screen,
toolbar, dialogs, tooltips, and code editor. (PRO)
* Improved colour contrast and reduced-motion support across admin screens. (PRO)
* Faster loading of large code vaults, which are now fetched a page at a time instead of all at once. (PRO)

__Fixed__

* Fixed AI conversations from one connected site being visible from another. (PRO)
* Fixed a snippet list stored in Code Snippets Cloud being emptied when the site was temporarily unable to read its own
snippets. (PRO)
* Fixed REST API server error responses on missing snippets. (PRO)
* Fixed redundant frontend logic, improving overall performance. (PRO)
* Fixed Community Cloud search results and pagination to respect WordPress screen options. (PRO)
* Fixed snippet saving and activation feedback to improve validation and runtime error display. (PRO)
* Fixed downloaded Community Cloud snippets appearing as not downloaded after a page reload. (PRO)
* Fixed network snippet lookups using the wrong database table on multisite. (PRO)
* Fixed the inactive snippets count including trashed snippets. (PRO)
* Fixed featured Community Cloud snippets failing to load with some cloud API responses. (PRO)
* Fixed bulk actions in Community Cloud running against an empty selection, so selected snippets were never downloaded.
(PRO)
* Fixed a fatal error in the Elementor editor on Elementor versions older than 3.19.
* Fixed snippet descriptions staying truncated after the "Truncate long snippet names and descriptions" screen option
was switched off.
* Fixed saving, activating or deactivating a snippet reporting its previous state, which could leave a deactivated
snippet running under file-based execution.
* Fixed the snippet editor reporting a save as failed and undelivered when no response was received, which it cannot
determine.
* Fixed translations being loaded too early, with a notice from WordPress, when posts were queried while plugins were
still loading.

= 3.10.2 (2026-09-01) =

__Added__
Expand Down
11 changes: 11 additions & 0 deletions tests/e2e/blueprints-demo.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,17 @@ test.describe('Blueprints demo', () => {
await expect(generated.locator('.badge.php-badge')).toBeVisible()
})

test('the callout close button exits the walkthrough mid-demo', async ({ page }) => {
await page.setViewportSize({ width: 1600, height: 900 })
await page.goto(URLS.BLUEPRINTS_ADMIN)
await page.locator('.demo-play').click()

await page.getByRole('button', { name: 'Exit demo' }).click()

await expect(page.locator('.demo-upsell')).toBeVisible()
await expect(page.locator('.demo-callout')).toHaveCount(0)
})

test('sections become browsable once the walkthrough finishes', async ({ page }) => {
await page.goto(URLS.BLUEPRINTS_ADMIN)

Expand Down
Loading