diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f23e84..0752aad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,13 +2,15 @@ ### 0.0.2 - Split the monolithic browser application JavaScript into functional modules under `src/web/js/`; `build.rs` now assembles the JavaScript manifest into the embedded fingerprinted bundle alongside the existing CSS build. -- Added a generated standalone `ui/app.js` workflow for preview/release snapshots while keeping `src/web/js/` as the only JavaScript source of truth. +- Removed the duplicate `ui/` snapshot; `src/web/` is the only browser UI source and Cargo assembles embedded CSS/JS bundles in `OUT_DIR`. - Removed the top-level Compact/Balanced/Comfortable/Reading appearance preset cards. - Expanded **Interface style** with RustBox (custom), Roundcube Classic, Roundcube Larry, Roundcube Elastic, Clean, Graphite and High contrast. - Fixed styles no longer overwrite the user's RustBox custom colors/layout values; switching back to RustBox restores the saved manual configuration. - Fixed interface styles lock palette/geometry controls while leaving Theme, language, date/time formatting and accessibility/user visibility toggles available. - Added dedicated light/dark palettes for Larry, Clean, Graphite and High contrast while retaining the existing Classic/Elastic compatibility skins. - Updated UI/build contract tests for the modular JavaScript build and expanded interface-style validation. +- Fixed the 0.0.2 release build regression by centralizing `AppConfig::default()` and updating stale `AccountSession` test fixtures for the new session metadata fields. +- Updated UI contract tests and visual fixtures after removal of the legacy `ui/` directory. ### 0.0.1 diff --git a/README.md b/README.md index 57ccb02..b0600e4 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ rustbox/ └── dev.sh ``` -`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`; `ui/app.js` is only a generated preview/release snapshot. +`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`. ## Configure mail servers diff --git a/docs/RELEASING.md b/docs/RELEASING.md index 31fac1d..dc2156b 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -25,4 +25,4 @@ RUSTBOX_CONFIG=./config.dev.toml ./target/release/rustbox --check-config ## Browser asset build -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. `ui/app.js` is a generated standalone preview/release snapshot and must not be edited as source. +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. diff --git a/docs/ROUNDCUBE_SKIN_VALIDATION.md b/docs/ROUNDCUBE_SKIN_VALIDATION.md index abdd299..36084a7 100644 --- a/docs/ROUNDCUBE_SKIN_VALIDATION.md +++ b/docs/ROUNDCUBE_SKIN_VALIDATION.md @@ -15,6 +15,6 @@ This snapshot maps RustBox components onto the official Roundcube Classic and El The test verifies all migrations on a blank SQLite database, `interface_skin`, saved-response `format`, unique HTML ids, SVG back-arrow markup, both skin/theme selector pairs, upstream palette tokens, CSS parseability and frontend/backend registration. -`scripts/bundle-ui-js.py --output /tmp/rustbox-ui.js` followed by `node --check /tmp/rustbox-ui.js` validates the JavaScript manifest as one build artifact. `ui/app.js` is the generated standalone snapshot and is checked separately. +`build.rs` consumes `src/web/js/manifest.txt` during Cargo builds. For a standalone syntax check, concatenate the manifest entries in order and run `node --check` on the resulting `.js` file. The browser fixture in `tests/theme_fixture.html` is rendered with the real bundled CSS. Validation screenshots in `tests/theme-shots/` cover Classic Light/Dark, Elastic Light/Dark and both mobile drawer variants. diff --git a/docs/releases/v0.0.2.md b/docs/releases/v0.0.2.md index 4933ab4..55dd612 100644 --- a/docs/releases/v0.0.2.md +++ b/docs/releases/v0.0.2.md @@ -6,7 +6,7 @@ RustBox 0.0.2 modularizes the browser JavaScript build and restructures appearan - Source JavaScript lives in `src/web/js/` and is ordered by `src/web/js/manifest.txt`. - Cargo `build.rs` concatenates the modules into `rustbox-ui.js`, fingerprints the combined CSS/JS asset set and embeds the generated bundle. -- `scripts/bundle-ui-js.py` generates `ui/app.js` only for standalone UI previews/release snapshots. +- `src/web/` is the single UI source; no duplicate `ui/` snapshot or standalone bundler script is kept in the repository. ## Interface styles diff --git a/src/config.rs b/src/config.rs index 89bbb75..afdc5f9 100644 --- a/src/config.rs +++ b/src/config.rs @@ -214,6 +214,44 @@ fn default_pgp_max_key_bytes() -> usize { 1024 * 1024 } +impl Default for AppConfig { + fn default() -> Self { + Self { + host: default_host(), + port: default_port(), + imap_host: None, + imap_port: default_imap_port(), + smtp_host: None, + smtp_port: default_smtp_port(), + tls_enabled: default_tls_enabled(), + data_dir: default_data_dir(), + session_timeout_hours: default_session_timeout_hours(), + remember_me_enabled: default_remember_me_enabled(), + remember_me_days: default_remember_me_days(), + environment: default_environment(), + base_path: None, + sieve_host: None, + sieve_port: default_sieve_port(), + sieve_tls_mode: default_sieve_tls_mode(), + sieve_allow_insecure_auth: false, + sieve_timeout_secs: default_sieve_timeout_secs(), + trusted_proxies: None, + outgoing_headers: Vec::new(), + pgp_wkd_enabled: default_pgp_wkd_enabled(), + pgp_hkp_enabled: false, + pgp_hkp_url: default_pgp_hkp_url(), + pgp_network_timeout_secs: default_pgp_network_timeout_secs(), + pgp_max_key_bytes: default_pgp_max_key_bytes(), + pgp_allow_private_export: false, + default_sent_folder: None, + default_drafts_folder: None, + default_trash_folder: None, + default_junk_folder: None, + default_archive_folder: None, + } + } +} + impl AppConfig { /// Use the root path when no reverse-proxy prefix is configured. pub fn normalized_base_path(&self) -> &str { diff --git a/src/routes/auth.rs b/src/routes/auth.rs index b9c1aed..1e12c3b 100644 --- a/src/routes/auth.rs +++ b/src/routes/auth.rs @@ -870,6 +870,7 @@ mod tests { sieve_timeout_secs: 10, trusted_proxies: None, outgoing_headers: Vec::new(), + ..AppConfig::default() } } diff --git a/src/routes/display_preferences.rs b/src/routes/display_preferences.rs index 8e95fc0..ef90a87 100644 --- a/src/routes/display_preferences.rs +++ b/src/routes/display_preferences.rs @@ -76,6 +76,7 @@ mod tests { sieve_timeout_secs: 10, trusted_proxies: None, outgoing_headers: Vec::new(), + ..AppConfig::default() }) } @@ -93,6 +94,9 @@ mod tests { smtp_tls: true, last_accessed: Instant::now(), timeout_override: None, + created_at_unix: 0, + last_accessed_unix: 0, + user_agent: String::new(), } } diff --git a/src/routes/health.rs b/src/routes/health.rs index 4d0dc58..37547cc 100644 --- a/src/routes/health.rs +++ b/src/routes/health.rs @@ -82,6 +82,7 @@ mod tests { sieve_timeout_secs: 10, trusted_proxies: None, outgoing_headers: Vec::new(), + ..AppConfig::default() }) } diff --git a/src/routes/messages/mod.rs b/src/routes/messages/mod.rs index 6733fe6..87ca447 100644 --- a/src/routes/messages/mod.rs +++ b/src/routes/messages/mod.rs @@ -1511,6 +1511,9 @@ mod sync_tests { smtp_tls: false, last_accessed: std::time::Instant::now(), timeout_override: None, + created_at_unix: 0, + last_accessed_unix: 0, + user_agent: String::new(), }; let mut client = MockImapClient::new().with_headers(vec![sync_header(7), sync_header(8)]); client.header_fetch_gate = Some((tokio::sync::Notify::new(), tokio::sync::Notify::new())); @@ -1686,6 +1689,9 @@ mod sync_tests { smtp_tls: false, last_accessed: std::time::Instant::now(), timeout_override: None, + created_at_unix: 0, + last_accessed_unix: 0, + user_agent: String::new(), }; let mut client = MockImapClient::new().with_headers((1..=900).map(sync_header).collect()); client.header_fetch_gate = Some((tokio::sync::Notify::new(), tokio::sync::Notify::new())); diff --git a/src/routes/tests.rs b/src/routes/tests.rs index ddc3637..5a603db 100644 --- a/src/routes/tests.rs +++ b/src/routes/tests.rs @@ -38,6 +38,7 @@ fn test_config(_static_dir: &str) -> Arc { sieve_timeout_secs: 10, trusted_proxies: None, outgoing_headers: Vec::new(), + ..AppConfig::default() }) } @@ -62,6 +63,7 @@ fn test_config_with_imap(_static_dir: &str, data_dir: &str) -> Arc { sieve_timeout_secs: 10, trusted_proxies: None, outgoing_headers: Vec::new(), + ..AppConfig::default() }) } @@ -86,6 +88,7 @@ fn test_config_with_smtp(_static_dir: &str, data_dir: &str) -> Arc { sieve_timeout_secs: 10, trusted_proxies: None, outgoing_headers: Vec::new(), + ..AppConfig::default() }) } diff --git a/tests/roundcube_skin_contract.py b/tests/roundcube_skin_contract.py index f930c82..d0a0d6e 100644 --- a/tests/roundcube_skin_contract.py +++ b/tests/roundcube_skin_contract.py @@ -42,19 +42,18 @@ def test_schema_and_response_templates(): def test_html_contract(): - for relative in ["src/web/index.html", "ui/index.html"]: - source = ROOT.joinpath(relative).read_text() - parser = IdCollector() - parser.feed(source) - duplicates = {value for value in parser.ids if parser.ids.count(value) > 1} - assert not duplicates - assert "folder-drawer-header" in source - assert "#icon-arrow-left" in source - assert "pref-interface-skin" in source - assert "compose-response-template" in source - assert "data-appearance-preset" not in source - for value in ["rustbox", "roundcube-classic", "roundcube-larry", "roundcube-elastic", "clean", "graphite", "high-contrast"]: - assert f'value="{value}"' in source + source = ROOT.joinpath("src/web/index.html").read_text() + parser = IdCollector() + parser.feed(source) + duplicates = {value for value in parser.ids if parser.ids.count(value) > 1} + assert not duplicates + assert "folder-drawer-header" in source + assert "#icon-arrow-left" in source + assert "pref-interface-skin" in source + assert "compose-response-template" in source + assert "data-appearance-preset" not in source + for value in ["rustbox", "roundcube-classic", "roundcube-larry", "roundcube-elastic", "clean", "graphite", "high-contrast"]: + assert f'value="{value}"' in source def test_roundcube_palette_contract(): diff --git a/tests/theme_fixture.html b/tests/theme_fixture.html index 996062c..cec9f4c 100644 --- a/tests/theme_fixture.html +++ b/tests/theme_fixture.html @@ -1,5 +1,5 @@ - +
✉RustBox
⌕☷