diff --git a/README.md b/README.md index 479298e..e0a5a43 100644 --- a/README.md +++ b/README.md @@ -326,6 +326,7 @@ Place a form with the shortcode the builder shows you: | Command | What it does | |---|---| | `npm run build` | Builds every bundle, dev and minified, then mirrors into a local site | +| `npm run build:titlebar` | Builds the small title-bar preview provider, dev and minified | | `npm run dev` | Rebuilds the builder bundle on save | | `npm run typecheck` | `tsc --noEmit` | | `npm test` | Vitest — the shared conformance suites | diff --git a/allterrain-forms.php b/allterrain-forms.php index 57d1c57..b624430 100644 --- a/allterrain-forms.php +++ b/allterrain-forms.php @@ -3,7 +3,7 @@ * Plugin Name: AllTerrain Forms * Plugin URI: https://github.com/AllTerrainDeveloper/forms * Description: Forms for WordPress with every premium feature free — conditional logic, calculations, multi-page, file uploads, signatures, repeaters, entry management, ten themes — built as an OpenStation desktop app with a drag-and-drop builder. - * Version: 1.2.1 + * Version: 1.2.2 * Requires at least: 6.9 * Requires PHP: 7.4 * Requires Plugins: desktop-mode @@ -55,7 +55,7 @@ defined( 'ABSPATH' ) || exit; -define( 'ALLTFO_VERSION', '1.2.1' ); +define( 'ALLTFO_VERSION', '1.2.2' ); define( 'ALLTFO_FILE', __FILE__ ); define( 'ALLTFO_DIR', plugin_dir_path( __FILE__ ) ); define( 'ALLTFO_URL', plugin_dir_url( __FILE__ ) ); diff --git a/docs/javascript.md b/docs/javascript.md index 4f2d5c9..555291c 100644 --- a/docs/javascript.md +++ b/docs/javascript.md @@ -131,20 +131,17 @@ widget subscribe to, so a new submission appears without a refresh. Registered through the shell's public `registerTitleBarButton` surface as `allterrain-forms/preview` — the same seam the shell's own editor→preview pairing -uses. - -```javascript -wp.os.registerTitleBarButton( { - id: 'allterrain-forms/preview', - label: 'Preview this form', - icon: 'dashicons-visibility', - placement: 'right', - order: 90, // just before the shell's Related button - match: ( w ) => w.id === 'allterrain-forms', - onClick: () => openPreview( source ), - owner: 'allterrain-forms-builder', -} ); -``` +uses. OpenStation loads the `allterrain-forms-titlebar` script at startup, so +the eye appears on restored builder windows without downloading the builder. +The button keeps its label, icon, right-hand placement, and order before the +shell's Related button. Its `owner` is `allterrain-forms-titlebar`, allowing +OpenStation to unregister it if the plugin is deactivated mid-session. + +When a builder opens, it calls `providePreviewSource()` with live access to its +current form, dirty state, and save method. The title-bar button uses the most +recently provided source; closing that builder withdraws it. This shared state lives +on `window` so the separately built title-bar and builder bundles can exchange +it without loading each other. Pressing it saves any unsaved work first — the preview is a render of the *stored* form, so previewing without saving would quietly show the last saved @@ -154,9 +151,8 @@ version and look like the builder had lost the edit — then opens Saving again refreshes that window rather than stacking a second copy, which is what makes the builder-and-preview-side-by-side loop work. -Everything degrades: with no shell there is no title bar, `registerPreviewButton` -returns a no-op teardown, and the builder's own Preview button opens the same URL -in a tab. +Everything degrades: with no shell there is no title bar, and the builder's own +Preview button opens the same URL in a tab. --- diff --git a/package-lock.json b/package-lock.json index d064590..8ed59a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "allterrain-forms", - "version": "1.2.1", + "version": "1.2.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "allterrain-forms", - "version": "1.2.1", + "version": "1.2.2", "license": "GPL-2.0-or-later", "dependencies": { "ajv": "^8.20.0", diff --git a/package.json b/package.json index 6475652..d7bc730 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "allterrain-forms", - "version": "1.2.1", + "version": "1.2.2", "private": true, "description": "Forms for WordPress with every premium feature free, as an OpenStation desktop app.", "license": "GPL-2.0-or-later", diff --git a/readme.txt b/readme.txt index 7c4f71b..7740980 100644 --- a/readme.txt +++ b/readme.txt @@ -5,7 +5,7 @@ Requires at least: 6.9 Tested up to: 7.1 Requires PHP: 7.4 Requires Plugins: desktop-mode -Stable tag: 1.2.1 +Stable tag: 1.2.2 License: GPL-2.0-or-later License URI: https://www.gnu.org/licenses/gpl-2.0.html @@ -327,6 +327,12 @@ API, importer hooks, and a REST namespace. Full reference in the repository. == Changelog == += 1.2.2 = + +* Loads a dedicated, 1 KB title-bar preview provider at OpenStation startup instead of the 505 KB form builder bundle. +* Keeps the Preview button available on builder windows and saves unsaved edits before opening the preview. +* Thanks to Juan Lentino for the performance improvement in PR #58. + = 1.2.1 = * Fixed the Recent submissions desktop widget failing to mount in OpenStation because its callback was absent from the widget registry. @@ -373,6 +379,9 @@ API, importer hooks, and a REST namespace. Full reference in the repository. == Upgrade Notice == += 1.2.2 = +Faster OpenStation startup when no form builder is open. Form and entry data are unchanged. + = 1.2.1 = Restores the Recent submissions desktop widget in OpenStation. No form or entry data changes. diff --git a/tests/vitest/titlebar.test.ts b/tests/vitest/titlebar.test.ts index 0baf327..f8057d7 100644 --- a/tests/vitest/titlebar.test.ts +++ b/tests/vitest/titlebar.test.ts @@ -33,6 +33,7 @@ describe( 'the title-bar provider', () => { describe( 'the preview button', () => { afterEach( () => { delete ( window as unknown as { wp?: unknown } ).wp; + delete ( window as unknown as { allTerrainFormsPreviewSources?: unknown } ).allTerrainFormsPreviewSources; } ); function stubShell() { @@ -74,4 +75,23 @@ describe( 'the preview button', () => { await Promise.resolve(); expect( shell.open ).not.toHaveBeenCalled(); } ); + + it( 'shares the preview source with the separately built titlebar bundle', async () => { + const shell = stubShell(); + const bundle = readFileSync( resolve( root, 'assets/js/titlebar.min.js' ), 'utf8' ); + + new Function( 'window', bundle )( window ); + + const withdraw = providePreviewSource( { + current: () => ( { id: 9, title: 'Quote', previewUrl: 'https://example.test/?p=9' } ), + isDirty: () => false, + save: async () => {}, + } ); + + shell.click(); + await Promise.resolve(); + expect( shell.open ).toHaveBeenCalledWith( expect.objectContaining( { id: 'allterrain-forms-preview-9' } ) ); + + withdraw(); + } ); } ); diff --git a/vite.config.js b/vite.config.js index db23744..8f09c4c 100644 --- a/vite.config.js +++ b/vite.config.js @@ -2,13 +2,14 @@ import { defineConfig } from 'vite'; import { resolve } from 'node:path'; /** - * Four bundles, eight passes. + * Separate bundles for the plugin's different loading schedules. * * They are separate because they load on entirely different schedules: `form` * ships to every visitor on a page with a form on it, `builder` only to somebody * editing one, `entries` only to somebody reading submissions, and `widget` only - * to somebody who has put the widget on their desktop. A single bundle would - * make every visitor download the form builder. + * to somebody who has put the widget on their desktop. The small `dock` and + * `titlebar` bundles load with OpenStation, without pulling in the builder. + * A single bundle would make every visitor download the form builder. * * Each target builds twice: `--mode development` emits the readable file * WordPress serves under `SCRIPT_DEBUG`, `--mode production` the minified one.