diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index fb89fbb..8381196 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -57,7 +57,7 @@ jobs:
runs-on: ubuntu-latest
defaults:
run:
- working-directory: plugin/remote-editor-bridge
+ working-directory: plugin/createpress-bridge
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 0b0c709..d8d0055 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -82,7 +82,7 @@ jobs:
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
with:
tagName: ${{ inputs.tag || github.ref_name }}
- releaseName: WordPress Remote Editor ${{ inputs.tag || github.ref_name }}
+ releaseName: CreatePress ${{ inputs.tag || github.ref_name }}
releaseDraft: true
prerelease: false
args: ${{ matrix.args }}
@@ -94,9 +94,9 @@ jobs:
- name: Package the companion plugin
run: |
cd plugin
- zip -r ../remote-editor-bridge.zip remote-editor-bridge -x 'remote-editor-bridge/vendor/*' 'remote-editor-bridge/composer.*' 'remote-editor-bridge/phpcs.xml.dist'
+ zip -r ../createpress-bridge.zip createpress-bridge -x 'createpress-bridge/vendor/*' 'createpress-bridge/composer.*' 'createpress-bridge/phpcs.xml.dist'
- uses: softprops/action-gh-release@v2
if: startsWith(github.ref, 'refs/tags/')
with:
draft: true
- files: remote-editor-bridge.zip
+ files: createpress-bridge.zip
diff --git a/.gitignore b/.gitignore
index 469cc42..7e6d5fd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,4 +4,4 @@ src-tauri/target/
src-tauri/gen/schemas/
*.log
.DS_Store
-plugin/remote-editor-bridge/vendor/
+plugin/createpress-bridge/vendor/
diff --git a/README.md b/README.md
index b40b6dc..ffd4986 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# WordPress Remote Editor
+# CreatePress
A native desktop app for macOS, Windows and Linux that lets you write and publish posts and pages on any WordPress site, using the site's full block editor (Gutenberg), with every block plugin installed on that site working exactly as it does in wp-admin.
@@ -16,7 +16,7 @@ On the WordPress site:
- WordPress 6.4 or newer, served over HTTPS.
- Application Passwords enabled (the default in WordPress core; some security plugins or hosts turn them off).
-- Optional but recommended: the **Remote Editor Bridge** plugin from [`plugin/remote-editor-bridge`](plugin/remote-editor-bridge), which removes the second sign-in when opening the editor.
+- Optional but recommended: the **CreatePress Bridge** plugin from [`plugin/createpress-bridge`](plugin/createpress-bridge), which removes the second sign-in when opening the editor.
On your computer: macOS 14+, Windows 10/11 (WebView2 is installed automatically if missing), or a Debian/Ubuntu-based Linux with WebKitGTK 4.1.
@@ -39,14 +39,14 @@ Live tests against a real site (for example a local one on `http://localhost:888
```sh
cd src-tauri
-WPRE_TEST_SITE=https://example.com WPRE_TEST_USER=me WPRE_TEST_APP_PASSWORD="xxxx xxxx xxxx xxxx xxxx xxxx" \
+CREATEPRESS_TEST_SITE=https://example.com CREATEPRESS_TEST_USER=me CREATEPRESS_TEST_APP_PASSWORD="xxxx xxxx xxxx xxxx xxxx xxxx" \
cargo test live_tests -- --ignored --nocapture
```
Companion plugin checks:
```sh
-cd plugin/remote-editor-bridge
+cd plugin/createpress-bridge
composer install
vendor/bin/phpcs
```
@@ -68,12 +68,16 @@ Pushing a `v*` tag runs [`.github/workflows/release.yml`](.github/workflows/rele
| `src/` | Platform-adaptive React shell UI |
| `src-tauri/src/` | Rust core: REST client, sign-in, keychain, editor webview, menus |
| `src-tauri/capabilities/` | Least-privilege permission grants for each webview |
-| `plugin/remote-editor-bridge/` | Optional companion WordPress plugin |
+| `plugin/createpress-bridge/` | Optional companion WordPress plugin |
| `docs/` | Architecture and security notes |
-## Name and trademark
+## Trademark
-"WordPress" is a registered trademark of the WordPress Foundation. Its trademark policy restricts using "WordPress" in product names, so the app name should be cleared (or changed to something like "Remote Editor for WordPress") before public distribution. The companion plugin already uses a compliant name.
+"WordPress" is a registered trademark of the WordPress Foundation. CreatePress is an independent app and is not affiliated with or endorsed by the WordPress Foundation.
+
+## About
+
+CreatePress is developed by [Eli Linares](https://realact.net).
## License
diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md
index 8dc6e37..98a316a 100644
--- a/docs/ARCHITECTURE.md
+++ b/docs/ARCHITECTURE.md
@@ -1,4 +1,4 @@
-# WordPress Remote Editor: Architecture
+# CreatePress: Architecture
## Goals
@@ -68,7 +68,7 @@ The shell UI is React, but it is *platform-adaptive*, not a single web look:
WordPress **Application Passwords** (core since 5.6) are used for all REST traffic.
1. **Discovery**: fetch the site, follow the `Link: <…>; rel="https://api.w.org/"` header to the REST index, read `authentication["application-passwords"].endpoints.authorization`. Works with pretty and plain permalinks (`?rest_route=`).
-2. **Authorise**: open WordPress's own `authorize-application.php` in an isolated, incognito auth window. The user signs in with their normal login (2FA, SSO and security plugins keep working) and approves "WordPress Remote Editor". `success_url` is `wpremoteeditor://auth/callback?state=…`; the core intercepts that navigation inside its own webview (so no other app can register the scheme and steal it), checks `state`, and stores the password.
+2. **Authorise**: open WordPress's own `authorize-application.php` in an isolated, incognito auth window. The user signs in with their normal login (2FA, SSO and security plugins keep working) and approves "CreatePress". `success_url` is `createpress://auth/callback?state=…`; the core intercepts that navigation inside its own webview (so no other app can register the scheme and steal it), checks `state`, and stores the password.
3. **Manual fallback**: paste a username and an Application Password created under Users → Profile.
4. **Storage**: the password goes straight into the OS keychain (macOS Keychain, Windows Credential Manager, Secret Service/libsecret on Linux) and is held in memory only in zeroizing buffers.
5. **Removal**: removing a site revokes its Application Password on the server (using `/application-passwords/introspect` to find its UUID) and deletes the keychain entry.
@@ -77,7 +77,7 @@ WordPress **Application Passwords** (core since 5.6) are used for all REST traff
wp-admin uses cookie authentication, which Application Passwords can't provide. Two modes:
-- **With the companion plugin** (`plugin/wp-remote-editor-bridge`, recommended): the core calls `POST /wpre/v1/handoff` authenticated by the Application Password. The plugin returns a single-use, 60-second, hashed token bound to that user; the editor webview opens `wp-login.php?action=wpre_handoff&token=…`, which sets the normal auth cookie and redirects into the editor. No second login.
+- **With the companion plugin** (`plugin/createpress-bridge`, recommended): the core calls `POST /createpress/v1/handoff` authenticated by the Application Password. The plugin returns a single-use, 60-second, hashed token bound to that user; the editor webview opens `wp-login.php?action=createpress_handoff&token=…`, which sets the normal auth cookie and redirects into the editor. No second login.
- **Without the plugin**: the user signs in once inside the editor webview using the site's own login form. The cookie lives in that site's isolated store.
## Roles and capabilities
@@ -92,7 +92,7 @@ wp-admin uses cookie authentication, which Application Passwords can't provide.
src/ React shell (platform-adaptive UI)
src-tauri/ Rust core: REST client, auth, keychain, editor webview
capabilities/ Tauri capability files (least privilege)
-plugin/wp-remote-editor-bridge/ Optional companion WordPress plugin (GPL-2.0-or-later, WPCS)
+plugin/createpress-bridge/ Optional companion WordPress plugin (GPL-2.0-or-later, WPCS)
.github/workflows/ CI (lint, test, phpcs) and signed release builds
docs/ Architecture and security notes
```
diff --git a/docs/SECURITY.md b/docs/SECURITY.md
index ffe4405..1a6958f 100644
--- a/docs/SECURITY.md
+++ b/docs/SECURITY.md
@@ -1,6 +1,6 @@
# Security Model
-WordPress Remote Editor holds credentials for websites and renders remote content, so it is designed around least privilege and isolation.
+CreatePress holds credentials for websites and renders remote content, so it is designed around least privilege and isolation.
## Credentials
@@ -21,7 +21,7 @@ WordPress Remote Editor holds credentials for websites and renders remote conten
- Authorization happens on the site's own `authorize-application.php` page, so 2FA, SSO and security plugins work unchanged.
- The sign-in window is incognito (no persistent cookies) and has no IPC access.
-- The callback (`wpremoteeditor://…`) is intercepted inside that window, not registered with the OS, so another app cannot hijack it. A 256-bit `state` value is checked in constant time.
+- The callback (`createpress://…`) is intercepted inside that window, not registered with the OS, so another app cannot hijack it. A 256-bit `state` value is checked in constant time.
## Webviews
@@ -34,8 +34,9 @@ WordPress Remote Editor holds credentials for websites and renders remote conten
- The editor webview's navigation is locked to the site's origins; other links open in the system browser.
- Each site has its own cookie/data store, deleted when the site is removed.
- The core controls the editor only by evaluating fixed, built-in scripts. It never evaluates strings that come from a page.
+- `Object.prototype` is frozen in the shell only (an initialization script on that webview). Tauri's app-wide `freezePrototype` option is off because it would also apply to the editor webview, where it breaks WordPress's own scripts and leaves the editor blank.
-## Companion plugin (`remote-editor-bridge`)
+## Companion plugin (`createpress-bridge`)
- Handoff links can only be requested over REST with Application Password authentication by users who can edit content.
- Tokens are 48 random characters, single use, valid for 60 seconds, stored only as an HMAC-SHA256 hash, rate limited to 10/minute per user, and can only redirect to `post.php`/`post-new.php`.
diff --git a/index.html b/index.html
index fb0ac86..ebc0619 100644
--- a/index.html
+++ b/index.html
@@ -4,7 +4,7 @@
-
WordPress Remote Editor
+ CreatePress
diff --git a/package-lock.json b/package-lock.json
index 6d4dd81..805d46f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
- "name": "wordpress-remote-editor",
- "version": "0.1.0",
+ "name": "createpress",
+ "version": "0.2.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
- "name": "wordpress-remote-editor",
- "version": "0.1.0",
+ "name": "createpress",
+ "version": "0.2.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@tauri-apps/api": "^2.8.0",
diff --git a/package.json b/package.json
index 721b90e..c273330 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
- "name": "wordpress-remote-editor",
+ "name": "createpress",
"private": true,
- "version": "0.1.0",
+ "version": "0.2.0",
"type": "module",
"license": "GPL-2.0-or-later",
"scripts": {
diff --git a/plugin/createpress-bridge/assets/embedded.css b/plugin/createpress-bridge/assets/embedded.css
new file mode 100644
index 0000000..478fa07
--- /dev/null
+++ b/plugin/createpress-bridge/assets/embedded.css
@@ -0,0 +1,21 @@
+/* Hide wp-admin chrome around the block editor inside the desktop app. */
+html.wp-toolbar:has(body.createpress-embedded) {
+ padding-top: 0;
+}
+
+body.createpress-embedded #wpadminbar,
+body.createpress-embedded #adminmenumain,
+body.createpress-embedded #wpfooter,
+body.createpress-embedded .edit-post-fullscreen-mode-close,
+body.createpress-embedded .editor-header__back-button {
+ display: none !important;
+}
+
+body.createpress-embedded #wpcontent {
+ margin-left: 0;
+}
+
+body.createpress-embedded .interface-interface-skeleton {
+ top: 0;
+ left: 0;
+}
diff --git a/plugin/remote-editor-bridge/composer.json b/plugin/createpress-bridge/composer.json
similarity index 75%
rename from plugin/remote-editor-bridge/composer.json
rename to plugin/createpress-bridge/composer.json
index 645328b..334cfad 100644
--- a/plugin/remote-editor-bridge/composer.json
+++ b/plugin/createpress-bridge/composer.json
@@ -1,6 +1,6 @@
{
- "name": "realact/remote-editor-bridge",
- "description": "Companion plugin for the WordPress Remote Editor desktop app.",
+ "name": "realact/createpress-bridge",
+ "description": "Companion plugin for the CreatePress desktop app.",
"type": "wordpress-plugin",
"license": "GPL-2.0-or-later",
"require": {
diff --git a/plugin/remote-editor-bridge/composer.lock b/plugin/createpress-bridge/composer.lock
similarity index 99%
rename from plugin/remote-editor-bridge/composer.lock
rename to plugin/createpress-bridge/composer.lock
index 9682f87..a2263aa 100644
--- a/plugin/remote-editor-bridge/composer.lock
+++ b/plugin/createpress-bridge/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "1baf6cf078b38181c98f694d660844a7",
+ "content-hash": "5f0a5864871973ac2546ef3fe6259f19",
"packages": [],
"packages-dev": [
{
diff --git a/plugin/createpress-bridge/createpress-bridge.php b/plugin/createpress-bridge/createpress-bridge.php
new file mode 100644
index 0000000..7bc7ea4
--- /dev/null
+++ b/plugin/createpress-bridge/createpress-bridge.php
@@ -0,0 +1,30 @@
+ 401 )
);
}
if ( ! current_user_can( 'edit_posts' ) && ! current_user_can( 'edit_pages' ) ) {
return new \WP_Error(
- 'wpre_forbidden',
- __( 'Your role cannot edit content on this site.', 'remote-editor-bridge' ),
+ 'createpress_forbidden',
+ __( 'Your role cannot edit content on this site.', 'createpress-bridge' ),
array( 'status' => 403 )
);
}
@@ -123,8 +123,8 @@ public static function issue( \WP_REST_Request $request ) {
$redirect = self::safe_editor_url( (string) $request->get_param( 'redirect_to' ) );
if ( null === $redirect ) {
return new \WP_Error(
- 'wpre_bad_redirect',
- __( 'The editor address is not allowed.', 'remote-editor-bridge' ),
+ 'createpress_bad_redirect',
+ __( 'The editor address is not allowed.', 'createpress-bridge' ),
array( 'status' => 400 )
);
}
@@ -133,8 +133,8 @@ public static function issue( \WP_REST_Request $request ) {
$count = (int) get_transient( $rate_key );
if ( $count >= self::MAX_PER_MINUTE ) {
return new \WP_Error(
- 'wpre_rate_limited',
- __( 'Too many sign-in requests. Please wait a minute.', 'remote-editor-bridge' ),
+ 'createpress_rate_limited',
+ __( 'Too many sign-in requests. Please wait a minute.', 'createpress-bridge' ),
array( 'status' => 429 )
);
}
@@ -153,7 +153,7 @@ public static function issue( \WP_REST_Request $request ) {
$url = add_query_arg(
array(
- 'action' => 'wpre_handoff',
+ 'action' => 'createpress_handoff',
'token' => rawurlencode( $token ),
),
wp_login_url()
@@ -165,7 +165,7 @@ public static function issue( \WP_REST_Request $request ) {
}
/**
- * Runs on wp-login.php?action=wpre_handoff: verifies the token, signs the
+ * Runs on wp-login.php?action=createpress_handoff: verifies the token, signs the
* user in and redirects to the editor.
*/
public static function consume() {
@@ -223,8 +223,8 @@ public static function consume() {
*/
private static function fail() {
wp_die(
- esc_html__( 'This sign-in link has expired or was already used. Return to WordPress Remote Editor and try again.', 'remote-editor-bridge' ),
- esc_html__( 'Sign-in link expired', 'remote-editor-bridge' ),
+ esc_html__( 'This sign-in link has expired or was already used. Return to CreatePress and try again.', 'createpress-bridge' ),
+ esc_html__( 'Sign-in link expired', 'createpress-bridge' ),
array( 'response' => 403 )
);
}
diff --git a/plugin/remote-editor-bridge/phpcs.xml.dist b/plugin/createpress-bridge/phpcs.xml.dist
similarity index 80%
rename from plugin/remote-editor-bridge/phpcs.xml.dist
rename to plugin/createpress-bridge/phpcs.xml.dist
index 80b9c91..de0b258 100644
--- a/plugin/remote-editor-bridge/phpcs.xml.dist
+++ b/plugin/createpress-bridge/phpcs.xml.dist
@@ -1,5 +1,5 @@
-
+WordPress Coding Standards for the companion plugin../vendor/*
@@ -11,7 +11,7 @@
-
+
@@ -19,6 +19,6 @@
- remote-editor-bridge.php
+ createpress-bridge.php
diff --git a/plugin/remote-editor-bridge/readme.txt b/plugin/createpress-bridge/readme.txt
similarity index 80%
rename from plugin/remote-editor-bridge/readme.txt
rename to plugin/createpress-bridge/readme.txt
index 1bc6c18..03c689e 100644
--- a/plugin/remote-editor-bridge/readme.txt
+++ b/plugin/createpress-bridge/readme.txt
@@ -1,18 +1,18 @@
-=== Remote Editor Bridge ===
+=== CreatePress Bridge ===
Contributors: realact
Tags: block editor, gutenberg, desktop, application passwords, remote editing
Requires at least: 6.4
Tested up to: 6.8
Requires PHP: 7.4
-Stable tag: 0.1.0
+Stable tag: 0.2.0
License: GPL-2.0-or-later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
-Companion plugin for the WordPress Remote Editor desktop app.
+Companion plugin for the CreatePress desktop app.
== Description ==
-WordPress Remote Editor is a desktop app for macOS, Windows and Linux that edits your site with its own block editor, so every block plugin you use keeps working.
+CreatePress is a desktop app for macOS, Windows and Linux that edits your site with its own block editor, so every block plugin you use keeps working.
This optional plugin improves that experience:
diff --git a/plugin/remote-editor-bridge/uninstall.php b/plugin/createpress-bridge/uninstall.php
similarity index 75%
rename from plugin/remote-editor-bridge/uninstall.php
rename to plugin/createpress-bridge/uninstall.php
index a1b4318..f5be021 100644
--- a/plugin/remote-editor-bridge/uninstall.php
+++ b/plugin/createpress-bridge/uninstall.php
@@ -3,7 +3,7 @@
* Cleans up on uninstall. Handoff tokens are short-lived transients, so there
* is no persistent data to remove beyond any that remain.
*
- * @package RemoteEditorBridge
+ * @package CreatePressBridge
*/
defined( 'WP_UNINSTALL_PLUGIN' ) || exit;
@@ -13,7 +13,7 @@
$wpdb->query(
$wpdb->prepare(
"DELETE FROM {$wpdb->options} WHERE option_name LIKE %s OR option_name LIKE %s",
- $wpdb->esc_like( '_transient_wpre_handoff_' ) . '%',
- $wpdb->esc_like( '_transient_timeout_wpre_handoff_' ) . '%'
+ $wpdb->esc_like( '_transient_createpress_handoff_' ) . '%',
+ $wpdb->esc_like( '_transient_timeout_createpress_handoff_' ) . '%'
)
);
diff --git a/plugin/remote-editor-bridge/assets/embedded.css b/plugin/remote-editor-bridge/assets/embedded.css
deleted file mode 100644
index 2ec47bf..0000000
--- a/plugin/remote-editor-bridge/assets/embedded.css
+++ /dev/null
@@ -1,21 +0,0 @@
-/* Hide wp-admin chrome around the block editor inside the desktop app. */
-html.wp-toolbar:has(body.wpre-embedded) {
- padding-top: 0;
-}
-
-body.wpre-embedded #wpadminbar,
-body.wpre-embedded #adminmenumain,
-body.wpre-embedded #wpfooter,
-body.wpre-embedded .edit-post-fullscreen-mode-close,
-body.wpre-embedded .editor-header__back-button {
- display: none !important;
-}
-
-body.wpre-embedded #wpcontent {
- margin-left: 0;
-}
-
-body.wpre-embedded .interface-interface-skeleton {
- top: 0;
- left: 0;
-}
diff --git a/plugin/remote-editor-bridge/remote-editor-bridge.php b/plugin/remote-editor-bridge/remote-editor-bridge.php
deleted file mode 100644
index 2c5b9f7..0000000
--- a/plugin/remote-editor-bridge/remote-editor-bridge.php
+++ /dev/null
@@ -1,29 +0,0 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src-tauri/permissions/autogenerated/editor_set_hidden.toml b/src-tauri/permissions/autogenerated/editor_set_hidden.toml
new file mode 100644
index 0000000..c4eb963
--- /dev/null
+++ b/src-tauri/permissions/autogenerated/editor_set_hidden.toml
@@ -0,0 +1,11 @@
+# Automatically generated - DO NOT EDIT!
+
+[[permission]]
+identifier = "allow-editor-set-hidden"
+description = "Enables the editor_set_hidden command without any pre-configured scope."
+commands.allow = ["editor_set_hidden"]
+
+[[permission]]
+identifier = "deny-editor-set-hidden"
+description = "Denies the editor_set_hidden command without any pre-configured scope."
+commands.deny = ["editor_set_hidden"]
diff --git a/src-tauri/src/auth.rs b/src-tauri/src/auth.rs
index 2db19a2..972dc47 100644
--- a/src-tauri/src/auth.rs
+++ b/src-tauri/src/auth.rs
@@ -3,7 +3,7 @@
//! WordPress's own `authorize-application.php` screen is opened in an
//! isolated, incognito window with no IPC access. The user signs in with
//! their normal login (2FA/SSO keep working) and approves the app. WordPress
-//! then redirects to `wpremoteeditor://auth/callback?...`, which we intercept
+//! then redirects to `createpress://auth/callback?...`, which we intercept
//! inside our own webview, so no other app can claim the scheme.
use crate::error::{AppError, AppResult};
@@ -19,10 +19,10 @@ use url::Url;
use uuid::Uuid;
use zeroize::Zeroizing;
-pub const CALLBACK_SCHEME: &str = "wpremoteeditor";
+pub const CALLBACK_SCHEME: &str = "createpress";
/// Stable app ID so WordPress groups all passwords created by this app.
pub const APP_ID: &str = "8f4d7f3e-6b0e-4c1a-9a58-2f0b8f7f9b21";
-pub const APP_NAME: &str = "WordPress Remote Editor";
+pub const APP_NAME: &str = "CreatePress";
const AUTH_WINDOW: &str = "auth";
pub struct PendingAuth {
@@ -254,25 +254,15 @@ mod tests {
#[test]
fn builds_authorize_url_with_state() {
let ep = Url::parse("https://example.com/wp-admin/authorize-application.php").unwrap();
- let u = authorize_url(
- &ep,
- "abc",
- "WordPress Remote Editor (laptop, 2026-10-13 09:00)",
- );
+ let u = authorize_url(&ep, "abc", "CreatePress (laptop, 2026-10-13 09:00)");
let pairs: std::collections::HashMap<_, _> = u.query_pairs().into_owned().collect();
- assert_eq!(
- pairs["app_name"],
- "WordPress Remote Editor (laptop, 2026-10-13 09:00)"
- );
+ assert_eq!(pairs["app_name"], "CreatePress (laptop, 2026-10-13 09:00)");
assert_eq!(pairs["app_id"], APP_ID);
assert_eq!(
pairs["success_url"],
- "wpremoteeditor://auth/callback?state=abc"
- );
- assert_eq!(
- pairs["reject_url"],
- "wpremoteeditor://auth/rejected?state=abc"
+ "createpress://auth/callback?state=abc"
);
+ assert_eq!(pairs["reject_url"], "createpress://auth/rejected?state=abc");
}
#[test]
@@ -283,12 +273,9 @@ mod tests {
.unwrap();
assert_eq!(
password_name("eli-mbp", t),
- "WordPress Remote Editor (eli-mbp, 2026-10-13 09:05)"
- );
- assert_eq!(
- password_name(" ", t),
- "WordPress Remote Editor (2026-10-13 09:05)"
+ "CreatePress (eli-mbp, 2026-10-13 09:05)"
);
+ assert_eq!(password_name(" ", t), "CreatePress (2026-10-13 09:05)");
assert!(!password_name("bad\nname", t).contains('\n'));
}
diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs
index 622d45d..4581e71 100644
--- a/src-tauri/src/commands.rs
+++ b/src-tauri/src/commands.rs
@@ -245,6 +245,13 @@ pub fn editor_set_bounds(app: AppHandle, bounds: Bounds) -> AppResult<()> {
editor::set_bounds(&app, bounds)
}
+/// Hides the editor pane while a shell sheet (such as About) is shown, since
+/// the pane is a separate webview drawn above the shell.
+#[tauri::command]
+pub fn editor_set_hidden(app: AppHandle, hidden: bool) -> AppResult<()> {
+ editor::set_hidden(&app, hidden)
+}
+
#[tauri::command]
pub fn editor_command(app: AppHandle, command: EditorCommand) -> AppResult<()> {
editor::command(&app, command)
diff --git a/src-tauri/src/editor.rs b/src-tauri/src/editor.rs
index 08c4faa..a00661c 100644
--- a/src-tauri/src/editor.rs
+++ b/src-tauri/src/editor.rs
@@ -68,14 +68,14 @@ pub enum EditorCommand {
impl EditorCommand {
fn script(self) -> &'static str {
match self {
- Self::Save => "window.__wpre && window.__wpre.save()",
- Self::Publish => "window.__wpre && window.__wpre.publish()",
- Self::Undo => "window.__wpre && window.__wpre.undo()",
- Self::Redo => "window.__wpre && window.__wpre.redo()",
- Self::ToggleSettings => "window.__wpre && window.__wpre.toggleSettings()",
- Self::DeviceDesktop => "window.__wpre && window.__wpre.device('Desktop')",
- Self::DeviceTablet => "window.__wpre && window.__wpre.device('Tablet')",
- Self::DeviceMobile => "window.__wpre && window.__wpre.device('Mobile')",
+ Self::Save => "window.__createpress && window.__createpress.save()",
+ Self::Publish => "window.__createpress && window.__createpress.publish()",
+ Self::Undo => "window.__createpress && window.__createpress.undo()",
+ Self::Redo => "window.__createpress && window.__createpress.redo()",
+ Self::ToggleSettings => "window.__createpress && window.__createpress.toggleSettings()",
+ Self::DeviceDesktop => "window.__createpress && window.__createpress.device('Desktop')",
+ Self::DeviceTablet => "window.__createpress && window.__createpress.device('Tablet')",
+ Self::DeviceMobile => "window.__createpress && window.__createpress.device('Mobile')",
}
}
}
@@ -186,7 +186,7 @@ async fn handoff_url(site: &SiteRecord, target: &Url) -> AppResult {
let client = WpClient::new(site.api_root.clone(), &creds, site.allow_insecure_local)?;
let h: Handoff = client
.post(
- "wpre/v1/handoff",
+ "createpress/v1/handoff",
&serde_json::json!({ "redirect_to": target.as_str() }),
)
.await?;
@@ -240,7 +240,7 @@ pub async fn open(
.map(|u| u.origin().ascii_serialization())
.collect();
let config = serde_json::json!({ "origins": origins }).to_string();
- let script = BRIDGE_JS.replace("__WPRE_CONFIG__", &config);
+ let script = BRIDGE_JS.replace("__CREATEPRESS_CONFIG__", &config);
// Grant the remote page exactly one command, only on this site's origins.
let mut cap = CapabilityBuilder::new(format!("editor-remote-{site_id}"))
@@ -306,6 +306,17 @@ pub fn set_bounds(app: &AppHandle, bounds: Bounds) -> AppResult<()> {
Ok(())
}
+pub fn set_hidden(app: &AppHandle, hidden: bool) -> AppResult<()> {
+ if let Some(w) = app.get_webview(EDITOR_LABEL) {
+ if hidden {
+ w.hide()?;
+ } else {
+ w.show()?;
+ }
+ }
+ Ok(())
+}
+
/// On Linux, Tauri stacks a window's webviews in a vertical GTK box and
/// ignores explicit positions. The shell keeps the editor full-width below its
/// toolbar (focus mode, the same layout as macOS and Windows), so on Linux the
diff --git a/src-tauri/src/editor_bridge.js b/src-tauri/src/editor_bridge.js
index d35fe5a..ab9f582 100644
--- a/src-tauri/src/editor_bridge.js
+++ b/src-tauri/src/editor_bridge.js
@@ -3,7 +3,7 @@
// editor state and reports a small, fixed-shape object to the native shell.
(function () {
"use strict";
- var CONFIG = __WPRE_CONFIG__;
+ var CONFIG = __CREATEPRESS_CONFIG__;
if (window.top !== window) return;
if (CONFIG.origins.indexOf(window.location.origin) === -1) return;
@@ -16,12 +16,12 @@
".interface-interface-skeleton{top:0!important;left:0!important}" +
"body.is-fullscreen-mode .interface-interface-skeleton{top:0!important}";
function injectStyle() {
- if (document.getElementById("wpre-embedded-style")) return;
+ if (document.getElementById("createpress-embedded-style")) return;
var style = document.createElement("style");
- style.id = "wpre-embedded-style";
+ style.id = "createpress-embedded-style";
style.textContent = css;
(document.head || document.documentElement).appendChild(style);
- document.documentElement.classList.add("wpre-embedded");
+ document.documentElement.classList.add("createpress-embedded");
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", injectStyle);
@@ -107,7 +107,7 @@
return false;
}
- Object.defineProperty(window, "__wpre", {
+ Object.defineProperty(window, "__createpress", {
configurable: false,
writable: false,
value: Object.freeze({
diff --git a/src-tauri/src/keychain.rs b/src-tauri/src/keychain.rs
index ec12424..6eb2175 100644
--- a/src-tauri/src/keychain.rs
+++ b/src-tauri/src/keychain.rs
@@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize};
use uuid::Uuid;
use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
-const SERVICE: &str = "com.wpremoteeditor.app";
+const SERVICE: &str = "net.realact.createpress";
#[derive(Serialize, Deserialize, Zeroize, ZeroizeOnDrop)]
pub struct Credentials {
diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs
index 52753e1..d882c2b 100644
--- a/src-tauri/src/lib.rs
+++ b/src-tauri/src/lib.rs
@@ -54,13 +54,16 @@ pub fn run() {
commands::open_editor,
commands::close_editor,
commands::editor_set_bounds,
+ commands::editor_set_hidden,
commands::editor_command,
commands::editor_report,
])
.run(tauri::generate_context!())
- .expect("error while running WordPress Remote Editor");
+ .expect("error while running CreatePress");
}
+const SHELL_FREEZE_PROTOTYPE: &str = "Object.freeze(Object.prototype);";
+
/// The main window is a plain window hosting two webviews: the shell UI
/// (resized with the window) and, when open, the editor placed over the
/// shell's editor area. Building it this way keeps child-webview layout
@@ -78,10 +81,29 @@ fn create_main_window(app: &mut tauri::App) -> tauri::Result<()> {
let size = window
.inner_size()?
.to_logical::(window.scale_factor()?);
+ // Freeze Object.prototype in the shell only. Tauri's app-wide
+ // `freezePrototype` option also injects into the editor webview, where it
+ // breaks WordPress's own scripts (moment, @wordpress/components) and the
+ // block editor never renders, leaving a blank pane.
window.add_child(
- tauri::webview::WebviewBuilder::from_config(&config).auto_resize(),
+ tauri::webview::WebviewBuilder::from_config(&config)
+ .initialization_script(SHELL_FREEZE_PROTOTYPE)
+ .auto_resize(),
LogicalPosition::new(0.0, 0.0),
size,
)?;
Ok(())
}
+
+#[cfg(test)]
+mod tests {
+ /// Regression: the app-wide option reaches the editor webview and leaves
+ /// New Post / New Page blank. The shell freezes its own prototype instead.
+ #[test]
+ fn prototype_freeze_is_shell_only() {
+ let conf: serde_json::Value =
+ serde_json::from_str(include_str!("../tauri.conf.json")).unwrap();
+ assert_eq!(conf["app"]["security"]["freezePrototype"], false);
+ assert!(super::SHELL_FREEZE_PROTOTYPE.contains("Object.freeze(Object.prototype)"));
+ }
+}
diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs
index 06a51b6..7dc4981 100644
--- a/src-tauri/src/main.rs
+++ b/src-tauri/src/main.rs
@@ -2,5 +2,5 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
fn main() {
- wpre_lib::run();
+ createpress_lib::run();
}
diff --git a/src-tauri/src/menu.rs b/src-tauri/src/menu.rs
index ef49862..c31cf1e 100644
--- a/src-tauri/src/menu.rs
+++ b/src-tauri/src/menu.rs
@@ -1,7 +1,7 @@
//! Native application menu. Standard Edit items matter on macOS: without them
//! clipboard shortcuts don't reach the webviews.
-use tauri::menu::{AboutMetadata, Menu, MenuBuilder, MenuItemBuilder, SubmenuBuilder};
+use tauri::menu::{Menu, MenuBuilder, MenuItemBuilder, SubmenuBuilder};
use tauri::{AppHandle, Emitter, Runtime};
pub fn build(app: &AppHandle) -> tauri::Result