diff --git a/CHANGELOG.md b/CHANGELOG.md index 07f0791..7aa5429 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # RustBox changelog +### 0.2.15 +- Fixed HTML signature editor container semantics so clicks on whitespace/outside the editable area no longer implicitly activate the Bold toolbar button. + +- Fixed the HTML signature editor so toolbar actions preserve the active text selection and only run on an actual selection; collapsed-caret clicks no longer enable sticky bold/italic/underline typing modes. +- Fixed visible signatures in new messages by creating an editable body entry before the signature instead of placing the signature at the top of an otherwise empty editor. +- Refined the visible-signature separator with more breathing room and a subtler solid rule so it reads as a boundary without dominating the composer. +- Strips the editor-only body-entry marker from outgoing HTML while retaining the visible-signature marker used for de-duplication. +- No schema migration is required; V051 remains the next migration number. +- Bumped the application/source release to 0.2.15. + ### 0.2.14 - Made pane resizing tolerant of frontend/backend range mismatches: pane widths are normalized server-side, and a rejected width silently disables the in-view resizer for the current session instead of showing a validation error. diff --git a/Cargo.lock b/Cargo.lock index 32de63b..9147174 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3506,7 +3506,7 @@ dependencies = [ [[package]] name = "rustbox" -version = "0.2.14" +version = "0.2.15" dependencies = [ "aes-gcm", "async-imap", diff --git a/Cargo.toml b/Cargo.toml index 2781221..8f4c123 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustbox" -version = "0.2.14" +version = "0.2.15" edition = "2024" rust-version = "1.99.1" default-run = "rustbox" diff --git a/NOTICE.md b/NOTICE.md index 8dd6670..d6abdcf 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -1,6 +1,6 @@ # RustBox attribution / notice -RustBox 0.2.14 is derived from the Oxi webmail project: +RustBox 0.2.15 is derived from the Oxi webmail project: - Upstream: `c0h1b4/oxi` - Base snapshot: `7f1507c486b78cf33e54beb701dd460d3bd2bd5d` diff --git a/README.md b/README.md index cb37f9c..16b7782 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# RustBox 0.2.14 +# RustBox 0.2.15 RustBox is a webmail derived from `c0h1b4/oxi`. The consumed Oxi revision is recorded in `UPSTREAM_BASE`. @@ -34,7 +34,7 @@ rustbox/ `build.rs` combines the CSS modules from `src/web/css/manifest.txt` and JavaScript modules from `src/web/js/manifest.txt`. Cargo embeds the generated CSS/JS bundles, HTML and SVG sprite in the RustBox executable. There is no standalone source `src/web/app.js`; `src/web/` is the single source of truth and Cargo generates the embedded bundles in `OUT_DIR`. -## Storage and search in 0.2.14 +## Storage and search in 0.2.15 RustBox separates three concerns: SQL metadata, blob storage and search. The SQL engine does **not** decide whether `data_dir` may be used. A single-node PostgreSQL/MySQL deployment may still use filesystem blobs and local Tantivy. In cluster mode, local node state is rejected unless the configured backend is explicitly shared. @@ -237,7 +237,7 @@ Initialize Git after extracting the release archive: ```bash git init git add . -git commit -m "RustBox 0.2.14 release" +git commit -m "RustBox 0.2.15 release" ./scripts/setup-upstream.sh ``` diff --git a/RELEASE-MANIFEST.txt b/RELEASE-MANIFEST.txt index 6060d97..5da2568 100644 --- a/RELEASE-MANIFEST.txt +++ b/RELEASE-MANIFEST.txt @@ -1,6 +1,9 @@ -RustBox 0.2.14 release manifest +RustBox 0.2.15 release manifest -0.2.14 feature/configuration changes: +0.2.15 release changes and retained feature set: +- fixes HTML signature formatting so toolbar actions preserve the selected text and reliably toggle formatting +- gives new messages with visible signatures a real editable body entry above the signature and softens/spaces the composer-only separator +- strips the editor-only body-entry marker from serialized outgoing HTML while retaining signature de-duplication metadata - defaults wide message panes to compact single-line rows, with an Appearance setting to disable the behavior - adds per-identity signature insertion mode: silent append-on-send (default) or visible/editable in the composer - adds a durable SQL blob-deletion outbox used by filesystem/shared POSIX/NFS and S3 backends; attachment/draft cleanup is transactionally queued and retried until successful @@ -67,14 +70,14 @@ Docker examples: Operations/docs: - standalone `scripts/migrate_roundcube.py` imports Roundcube contacts/identities using RustBox config.toml and supports SQLite/MySQL/PostgreSQL targets -- README.md documents 0.2.14 configuration +- README.md documents 0.2.15 configuration - docker/README.md documents Compose variants - docs/STORAGE_SEARCH_OPERATIONS.md documents failure modes, shared-storage requirements and migration behavior - TODO.md leaves live SILO/MinIO/AWS/Ceph and real multi-node acceptance validation explicitly pending Build/release metadata: - `scripts/package-release.sh` runs the full Rust release gate before creating ZIP/SHA256 artifacts -- Cargo.toml version = 0.2.14 +- Cargo.toml version = 0.2.15 - rust-toolchain.toml pins Rust 1.99.1 - Cargo.lock is retained for reproducible application builds; the release gate generates it only when missing - CI and scripts/build-release.sh use the retained Cargo.lock (generating it only if missing), then run locked tests/Clippy/release build diff --git a/RELEASE-VALIDATION.txt b/RELEASE-VALIDATION.txt index 5008757..b9a0cbc 100644 --- a/RELEASE-VALIDATION.txt +++ b/RELEASE-VALIDATION.txt @@ -1,6 +1,9 @@ -RustBox 0.2.14 - final source validation +RustBox 0.2.15 - final source validation -0.2.14 scope: +0.2.15 scope: +- signature HTML formatting preserves selected text across toolbar clicks, including formatting removal, while collapsed-caret clicks cannot enable sticky bold/italic/underline typing modes +- the HTML signature editor is no longer wrapped in a label, preventing whitespace/outside clicks from implicitly activating the Bold toolbar button +- new messages with visible signatures provide an editable body area above a more subtle, better-spaced separator - wide message panes use compact one-line rows by default, with a persisted Mailbox preference to disable them - signatures support per-identity silent append-on-send (default) or visible/editable composer insertion - durable blob deletion outbox covers filesystem/shared POSIX/NFS and S3, pairing metadata deletion with cleanup enqueue in one SQL transaction and retrying storage failures without permanent dead-lettering @@ -25,15 +28,15 @@ RustBox 0.2.14 - final source validation - Composing shows only compact attachment/message size-limit values on one line - 0.2.9 realtime notification gating, browser shortcut preservation and 300-message defaults remain intact - runtime schema migration V050 adds compact-wide-row and signature insertion preferences; V051 remains next -- version metadata updated to 0.2.14 +- version metadata updated to 0.2.15 Completed in this environment: -- offline release validator: scripts/validate-release-static.py (409/409 checks passed) +- offline release validator: scripts/validate-release-static.py (416/416 checks passed) - browser JavaScript assembled in manifest order passes `node --check` - all JSON files parse successfully - Python helper scripts compile successfully - shell scripts pass `bash -n` -- release metadata consistently identifies 0.2.14 +- release metadata consistently identifies 0.2.15 - V048/V049/V050 migration files/catalog registration and SQLite upgrade execution are validated offline diff --git a/config.example.toml b/config.example.toml index 36f77ed..c5030a0 100644 --- a/config.example.toml +++ b/config.example.toml @@ -1,4 +1,4 @@ -# RustBox 0.2.14 +# RustBox 0.2.15 host = "127.0.0.1" port = 3001 environment = "production" diff --git a/docs/IMPLEMENTATION_STATUS.md b/docs/IMPLEMENTATION_STATUS.md index ba81053..20c3127 100644 --- a/docs/IMPLEMENTATION_STATUS.md +++ b/docs/IMPLEMENTATION_STATUS.md @@ -1,10 +1,12 @@ -# RustBox 0.2.14 - implementation status +# RustBox 0.2.15 - implementation status - Draft saves are realtime-silent: Drafts folder/list updates continue, but saving a draft does not surface as a new-mail notification. -This file tracks the `0.2.14` source snapshot. Storage/search scope and outstanding acceptance work are tracked in `TODO.md`. A feature is only listed as implemented when working code exists in this source tree; partial items are called out explicitly. +This file tracks the `0.2.15` source snapshot. Storage/search scope and outstanding acceptance work are tracked in `TODO.md`. A feature is only listed as implemented when working code exists in this source tree; partial items are called out explicitly. -## 0.2.14 browser mail UX +## 0.2.15 browser mail UX +- Signature HTML formatting now preserves the selected text when toolbar controls are used, so bold/italic/underline and remove-format can be toggled reliably. +- New messages with a visible signature always get an editable body line before the signature; the composer separator is spaced farther from the body and rendered as a subtle solid rule. - Wide message panes use a default-on compact single-line row layout (sender, subject/snippet, date and icons); users can disable it in Appearance settings. - Each identity can choose signature insertion: `silent` appends only during send and keeps the editor clean, while `visible` inserts an editable/removable signature into the composer. - Large To/Cc lists collapse to a compact recipient summary and expand on demand. diff --git a/docs/RELEASING.md b/docs/RELEASING.md index 831f0b9..7d05521 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -26,7 +26,7 @@ RUSTBOX_CONFIG=./config.dev.toml ./target/release/rustbox --check-config Browser JavaScript source lives in functional modules under `src/web/js/`, ordered by `src/web/js/manifest.txt`. `build.rs` concatenates those modules into the embedded `rustbox-ui.js` asset during every Cargo build. The repository has no duplicate `ui/` snapshot; `src/web/` is the only browser UI source. -## Updating an existing checkout to 0.2.14 +## Updating an existing checkout to 0.2.15 Do not overlay the release with `cp *` or another command that skips dotfiles. That can retain stale `.github`, `.env.example`, or `Cargo.lock` files. diff --git a/docs/STORAGE_SEARCH_OPERATIONS.md b/docs/STORAGE_SEARCH_OPERATIONS.md index 2d123af..89a2124 100644 --- a/docs/STORAGE_SEARCH_OPERATIONS.md +++ b/docs/STORAGE_SEARCH_OPERATIONS.md @@ -1,6 +1,6 @@ -# RustBox 0.2.14 storage/search operations +# RustBox 0.2.15 storage/search operations -RustBox 0.2.14 treats SQL, blob storage and search as independent capabilities. The database engine does not decide whether `data_dir` is allowed. +RustBox 0.2.15 treats SQL, blob storage and search as independent capabilities. The database engine does not decide whether `data_dir` is allowed. ## Deployment matrix diff --git a/migrations/README.md b/migrations/README.md index 88502d2..a9f7737 100644 --- a/migrations/README.md +++ b/migrations/README.md @@ -1,6 +1,6 @@ # Database schemas and migrations -RustBox 0.2.14 keeps SQL, blob storage and search as independent concerns: +RustBox 0.2.15 keeps SQL, blob storage and search as independent concerns: ```text database_backend = sqlite | mysql | pgsql diff --git a/packaging/rustbox.toml b/packaging/rustbox.toml index d36fc18..7272dc5 100644 --- a/packaging/rustbox.toml +++ b/packaging/rustbox.toml @@ -1,4 +1,4 @@ -# RustBox 0.2.14 - packaged production configuration (DEB/RPM) +# RustBox 0.2.15 - packaged production configuration (DEB/RPM) # # This file contains all supported RustBox configuration options. # Commented options are optional and do not change the packaged defaults. diff --git a/scripts/validate-release-static.py b/scripts/validate-release-static.py index f14b302..6e22703 100755 --- a/scripts/validate-release-static.py +++ b/scripts/validate-release-static.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Static/offline release checks for RustBox 0.2.14. +"""Static/offline release checks for RustBox 0.2.15. These checks intentionally avoid pretending to replace `cargo test`: they validate configuration, SQL migrations, Docker examples, query-catalog parity and release @@ -49,14 +49,14 @@ def extract_section(text: str, begin: str, end: str) -> str: # Release/toolchain metadata. cargo = tomllib.loads(read("Cargo.toml")) -require(cargo["package"]["version"] == "0.2.14", "Cargo package version is 0.2.14") +require(cargo["package"]["version"] == "0.2.15", "Cargo package version is 0.2.15") require(cargo["package"]["rust-version"] == "1.99.1", "Cargo rust-version is 1.99.1") toolchain = tomllib.loads(read("rust-toolchain.toml")) require(toolchain["toolchain"]["channel"] == "1.99.1", "rust-toolchain pins Rust 1.99.1") require((ROOT / "Cargo.lock").is_file(), "Cargo.lock is retained for reproducible application builds") lock = tomllib.loads(read("Cargo.lock")) lock_rustbox = next((pkg for pkg in lock.get("package", []) if pkg.get("name") == "rustbox"), {}) -require(lock_rustbox.get("version") == "0.2.14", "Cargo.lock RustBox package version is 0.2.14") +require(lock_rustbox.get("version") == "0.2.15", "Cargo.lock RustBox package version is 0.2.15") require(cargo["dependencies"].get("encoding_rs") == "0.8", "direct charset decoder dependency is pinned") require("encoding_rs" in lock_rustbox.get("dependencies", []), "Cargo.lock records direct charset decoder dependency") require("cargo generate-lockfile" in read("scripts/build-release.sh"), "release script generates Cargo.lock when missing") @@ -71,10 +71,10 @@ require("make_zip.py" in package_script and "sha256sum" in package_script, "rele require("cargo generate-lockfile" in read(".github/workflows/ci.yml"), "CI regenerates Cargo.lock before --locked checks") require("validate-release-static.py" in read("scripts/build-release.sh"), "release script runs offline/static release gate") require("validate-release-static.py" in read(".github/workflows/ci.yml"), "CI runs offline/static release gate") -require(read("README.md").startswith("# RustBox 0.2.14"), "README identifies RustBox 0.2.14") -require(read("RELEASE-MANIFEST.txt").startswith("RustBox 0.2.14"), "release manifest identifies RustBox 0.2.14") -require(read("RELEASE-VALIDATION.txt").startswith("RustBox 0.2.14"), "release validation identifies RustBox 0.2.14") -require(read("docs/IMPLEMENTATION_STATUS.md").startswith("# RustBox 0.2.14"), "implementation status identifies RustBox 0.2.14") +require(read("README.md").startswith("# RustBox 0.2.15"), "README identifies RustBox 0.2.15") +require(read("RELEASE-MANIFEST.txt").startswith("RustBox 0.2.15"), "release manifest identifies RustBox 0.2.15") +require(read("RELEASE-VALIDATION.txt").startswith("RustBox 0.2.15"), "release validation identifies RustBox 0.2.15") +require(read("docs/IMPLEMENTATION_STATUS.md").startswith("# RustBox 0.2.15"), "implementation status identifies RustBox 0.2.15") bootstrap_js = read("src/web/js/00-bootstrap.js") bindings_js = read("src/web/js/95-bindings-boot.js") realtime_js = read("src/web/js/80-settings-realtime.js") @@ -108,6 +108,12 @@ require("const attachments = mergeComposeAttachments(state.composeAttachments);" require('grid-template-columns: repeat(2, minmax(0, 1fr));' in mail_css and '.load-more {' in mail_css and 'width: 100%;' in mail_css.split('.load-more {', 1)[1].split('}', 1)[0], "message load actions share the full available list width") require('id="compose-fields-toggle"' in read("src/web/index.html") and 'function setComposeFieldsCollapsed' in compose_js and 'function toggleComposeFields' in compose_js and '.compose-dialog.compose-fields-collapsed .compose-addresses' in compose_css and 'setComposeFieldsCollapsed(false)' in bindings_js, "composer addressing fields can collapse to maximize editor space and reopen on invalid recipient input") require('id="compose-save-draft"' in read("src/web/index.html") and '$("#compose-save-draft").addEventListener("click", saveComposeDraftToImap);' in bindings_js and 'saveCurrentDraft(false, true, true)' in compose_js, "composer exposes an explicit manual IMAP draft save before cancel") + +require('rememberSignatureRange' in bindings_js and 'selectedSignatureRange' in bindings_js and 'if (!range || range.collapsed) return null;' in bindings_js and 'state.identitySignatureRange = null;' in bindings_js and '.signature-editor-toolbar button' in bindings_js and 'event.preventDefault();' in bindings_js and 'restoreEditorRange(range, signatureEditor);' in bindings_js, "signature formatting preserves real selections and refuses collapsed-caret commands that would enable sticky typing styles") +index_html = read("src/web/index.html") +require('