v0.0.2
RustBox CI / rustbox (push) Waiting to run

This commit is contained in:
Mateusz Gruszczyński committed 2026-10-29 19:47:11 +01:00
1 parent fff8521a17
commit 6c15491284
13 files changed
+73 -19

No files matched your search

+3 -1
View File
@@ -2,13 +2,15 @@
### 0.0.2 ### 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. - 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. - 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. - 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 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. - 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. - 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. - 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 ### 0.0.1
+1 -1
View File
@@ -33,7 +33,7 @@ rustbox/
└── dev.sh └── 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 ## Configure mail servers
+1 -1
View File
@@ -25,4 +25,4 @@ RUSTBOX_CONFIG=./config.dev.toml ./target/release/rustbox --check-config
## Browser asset build ## 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.
+1 -1
View File
@@ -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. 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. 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.
+1 -1
View File
@@ -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`. - 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. - 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 ## Interface styles
+38
View File
@@ -214,6 +214,44 @@ fn default_pgp_max_key_bytes() -> usize {
1024 * 1024 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 { impl AppConfig {
/// Use the root path when no reverse-proxy prefix is configured. /// Use the root path when no reverse-proxy prefix is configured.
pub fn normalized_base_path(&self) -> &str { pub fn normalized_base_path(&self) -> &str {
+1
View File
@@ -870,6 +870,7 @@ mod tests {
sieve_timeout_secs: 10, sieve_timeout_secs: 10,
trusted_proxies: None, trusted_proxies: None,
outgoing_headers: Vec::new(), outgoing_headers: Vec::new(),
..AppConfig::default()
} }
} }
+4
View File
@@ -76,6 +76,7 @@ mod tests {
sieve_timeout_secs: 10, sieve_timeout_secs: 10,
trusted_proxies: None, trusted_proxies: None,
outgoing_headers: Vec::new(), outgoing_headers: Vec::new(),
..AppConfig::default()
}) })
} }
@@ -93,6 +94,9 @@ mod tests {
smtp_tls: true, smtp_tls: true,
last_accessed: Instant::now(), last_accessed: Instant::now(),
timeout_override: None, timeout_override: None,
created_at_unix: 0,
last_accessed_unix: 0,
user_agent: String::new(),
} }
} }
+1
View File
@@ -82,6 +82,7 @@ mod tests {
sieve_timeout_secs: 10, sieve_timeout_secs: 10,
trusted_proxies: None, trusted_proxies: None,
outgoing_headers: Vec::new(), outgoing_headers: Vec::new(),
..AppConfig::default()
}) })
} }
+6
View File
@@ -1511,6 +1511,9 @@ mod sync_tests {
smtp_tls: false, smtp_tls: false,
last_accessed: std::time::Instant::now(), last_accessed: std::time::Instant::now(),
timeout_override: None, 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)]); 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())); client.header_fetch_gate = Some((tokio::sync::Notify::new(), tokio::sync::Notify::new()));
@@ -1686,6 +1689,9 @@ mod sync_tests {
smtp_tls: false, smtp_tls: false,
last_accessed: std::time::Instant::now(), last_accessed: std::time::Instant::now(),
timeout_override: None, 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()); 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())); client.header_fetch_gate = Some((tokio::sync::Notify::new(), tokio::sync::Notify::new()));
+3
View File
@@ -38,6 +38,7 @@ fn test_config(_static_dir: &str) -> Arc<AppConfig> {
sieve_timeout_secs: 10, sieve_timeout_secs: 10,
trusted_proxies: None, trusted_proxies: None,
outgoing_headers: Vec::new(), outgoing_headers: Vec::new(),
..AppConfig::default()
}) })
} }
@@ -62,6 +63,7 @@ fn test_config_with_imap(_static_dir: &str, data_dir: &str) -> Arc<AppConfig> {
sieve_timeout_secs: 10, sieve_timeout_secs: 10,
trusted_proxies: None, trusted_proxies: None,
outgoing_headers: Vec::new(), outgoing_headers: Vec::new(),
..AppConfig::default()
}) })
} }
@@ -86,6 +88,7 @@ fn test_config_with_smtp(_static_dir: &str, data_dir: &str) -> Arc<AppConfig> {
sieve_timeout_secs: 10, sieve_timeout_secs: 10,
trusted_proxies: None, trusted_proxies: None,
outgoing_headers: Vec::new(), outgoing_headers: Vec::new(),
..AppConfig::default()
}) })
} }
+12 -13
View File
@@ -42,19 +42,18 @@ def test_schema_and_response_templates():
def test_html_contract(): def test_html_contract():
for relative in ["src/web/index.html", "ui/index.html"]: source = ROOT.joinpath("src/web/index.html").read_text()
source = ROOT.joinpath(relative).read_text() parser = IdCollector()
parser = IdCollector() parser.feed(source)
parser.feed(source) duplicates = {value for value in parser.ids if parser.ids.count(value) > 1}
duplicates = {value for value in parser.ids if parser.ids.count(value) > 1} assert not duplicates
assert not duplicates assert "folder-drawer-header" in source
assert "folder-drawer-header" in source assert "#icon-arrow-left" in source
assert "#icon-arrow-left" in source assert "pref-interface-skin" in source
assert "pref-interface-skin" in source assert "compose-response-template" in source
assert "compose-response-template" in source assert "data-appearance-preset" not in source
assert "data-appearance-preset" not in source for value in ["rustbox", "roundcube-classic", "roundcube-larry", "roundcube-elastic", "clean", "graphite", "high-contrast"]:
for value in ["rustbox", "roundcube-classic", "roundcube-larry", "roundcube-elastic", "clean", "graphite", "high-contrast"]: assert f'value="{value}"' in source
assert f'value="{value}"' in source
def test_roundcube_palette_contract(): def test_roundcube_palette_contract():
+1 -1
View File
@@ -1,5 +1,5 @@
<!doctype html><html lang="en" data-theme="light" data-interface-skin="roundcube-elastic" data-density="compact" data-preview="right" data-scale="normal" data-color-preset="sage" data-font-family="system" data-contrast="normal" data-show-borders="true" data-toolbar-density="normal" data-unread-badge="dot" data-scrollbar-density="thin"> <!doctype html><html lang="en" data-theme="light" data-interface-skin="roundcube-elastic" data-density="compact" data-preview="right" data-scale="normal" data-color-preset="sage" data-font-family="system" data-contrast="normal" data-show-borders="true" data-toolbar-density="normal" data-unread-badge="dot" data-scrollbar-density="thin">
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="stylesheet" href="../ui/app.css"><style>:root{--folder-pane-width:238px;--message-pane-width:430px;--row-height:52px;--sidebar-item-height:35px;--ui-size:14px;--control-height:30px;--appbar-height:44px;--icon-size:18px;--button-pad-x:10px;--button-pad-y:5px;--field-pad-x:8px;--field-pad-y:5px} .fixture-icon{width:18px;height:18px}</style></head> <head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="stylesheet" href="../src/web/css/00-tokens.css"><link rel="stylesheet" href="../src/web/css/10-base.css"><link rel="stylesheet" href="../src/web/css/20-shell.css"><link rel="stylesheet" href="../src/web/css/30-mail.css"><link rel="stylesheet" href="../src/web/css/40-compose.css"><link rel="stylesheet" href="../src/web/css/50-settings.css"><link rel="stylesheet" href="../src/web/css/55-roundcube-skins.css"><link rel="stylesheet" href="../src/web/css/56-interface-styles.css"><link rel="stylesheet" href="../src/web/css/60-responsive.css"><style>:root{--folder-pane-width:238px;--message-pane-width:430px;--row-height:52px;--sidebar-item-height:35px;--ui-size:14px;--control-height:30px;--appbar-height:44px;--icon-size:18px;--button-pad-x:10px;--button-pad-y:5px;--field-pad-x:8px;--field-pad-y:5px} .fixture-icon{width:18px;height:18px}</style></head>
<body><div class="app-view"> <body><div class="app-view">
<header class="appbar"><div class="app-brand"><span class="brand-icon">✉</span><strong>RustBox</strong></div><div class="search-field"><span>⌕</span><input value="Search mail"><span></span><span>☷</span></div><div class="appbar-actions"><button class="icon-button">⚙</button><div class="account-summary"><span>linuxiarz@linuxiarz.pl</span><span>Connected</span></div></div></header> <header class="appbar"><div class="app-brand"><span class="brand-icon">✉</span><strong>RustBox</strong></div><div class="search-field"><span>⌕</span><input value="Search mail"><span></span><span>☷</span></div><div class="appbar-actions"><button class="icon-button">⚙</button><div class="account-summary"><span>linuxiarz@linuxiarz.pl</span><span>Connected</span></div></div></header>
<div class="mail-shell"><button class="folder-drawer-scrim open"></button><aside class="folder-pane open"> <div class="mail-shell"><button class="folder-drawer-scrim open"></button><aside class="folder-pane open">