Replace the HTML preview with a worker-rendered PDF preview

Rendering moves into a module Web Worker behind a latest-wins RenderClient
with a watchdog and a handshake-based main-thread fallback. The preview
shows the real PDF through pdf.js at 2x with zoom, page indicator, audit
issues popover and a focus modal. A4/Letter and margin controls are frozen
into each invoice at issue and can be saved as the default (migration M3).
react-pdf and pdf.js are lazy-loaded; the main chunk shrinks from 1.8 MB
to 0.5 MB. The HTML twin and its styles are removed.
This commit is contained in:
2026-10-04 05:47:19 +05:30
parent a014a907e4
commit cedf4839bd
24 changed files with 1579 additions and 505 deletions
+24 -2
View File
@@ -6,7 +6,7 @@ use tauri::State;
pub(crate) const SETTINGS_COLS: &str = "vendor_name, vendor_address, vendor_email, vendor_phone, vendor_pan,
vendor_gstin, vendor_state_code, logo_path, signature_path, default_bank_id, default_tax_rate,
default_tax_type, payment_terms_days, currency, onboarded, theme, gst_registration, default_hsn_sac, signatory_name,
signatory_designation";
signatory_designation, render_prefs";
pub(crate) fn map_settings(row: &Row) -> rusqlite::Result<Settings> {
Ok(Settings {
@@ -30,6 +30,7 @@ pub(crate) fn map_settings(row: &Row) -> rusqlite::Result<Settings> {
default_hsn_sac: row.get(17)?,
signatory_name: row.get(18)?,
signatory_designation: row.get(19)?,
render_prefs: row.get(20)?,
})
}
@@ -54,7 +55,7 @@ pub fn save_settings(state: State<AppState>, settings: Settings) -> Result<Setti
signature_path = ?9, default_bank_id = ?10, default_tax_rate = ?11,
default_tax_type = ?12, payment_terms_days = ?13, currency = ?14,
onboarded = ?15, theme = ?16, gst_registration = ?17, default_hsn_sac = ?18,
signatory_name = ?19, signatory_designation = ?20
signatory_name = ?19, signatory_designation = ?20, render_prefs = ?21
WHERE id = 1"#,
params![
settings.vendor_name,
@@ -77,6 +78,7 @@ pub fn save_settings(state: State<AppState>, settings: Settings) -> Result<Setti
settings.default_hsn_sac,
settings.signatory_name,
settings.signatory_designation,
settings.render_prefs,
],
)
.map_err(|e| e.to_string())?;
@@ -230,6 +232,26 @@ mod tests {
(setting, flagged)
}
#[test]
fn settings_carry_render_prefs() {
let conn = crate::db::open_in_memory().unwrap();
let read = |conn: &Connection| {
conn.query_row(
&format!("SELECT {} FROM app_settings WHERE id = 1", SETTINGS_COLS),
[],
map_settings,
)
.unwrap()
};
assert_eq!(read(&conn).render_prefs, "");
conn.execute("UPDATE app_settings SET render_prefs = ?1 WHERE id = 1", params!["{\"version\":1}"])
.unwrap();
let settings = read(&conn);
assert_eq!(settings.render_prefs, "{\"version\":1}");
let json = serde_json::to_value(&settings).unwrap();
assert_eq!(json["renderPrefs"], "{\"version\":1}");
}
#[test]
fn deleting_the_default_bank_promotes_the_lowest_remaining() {
let mut conn = crate::db::open_in_memory().unwrap();