Host: Google Fonts commands (gfonts_*) are served for every app that sets AppConfig::with_fonts; SiteHooks::fonts_changed; commands::GFONTS, testkit::assert_fonts_guards

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
CRTkafa 2026-10-01 04:11:46 +07:00
parent da9ac11b24
commit 7f9e172196
5 changed files with 105 additions and 2 deletions

View file

@ -637,7 +637,9 @@ pub fn rebuild_tabs(app: &AppHandle) {
#[tauri::command]
async fn shell_focus(webview: Webview) -> Result<(), String> {
shell_only(&webview)?;
webview.set_focus().map_err(|error| format!("FOCUS: {error}"))
let result = webview.set_focus().map_err(|error| format!("FOCUS: {error}"));
crate::diag::log("host", &format!("shell_focus -> {}", if result.is_ok() { "ok" } else { "error" }));
result
}
/// The shell calls this after its first DOM commit, so startup never exposes an empty webview.
@ -669,7 +671,24 @@ async fn shell_raise(webview: Webview, app: AppHandle, on: bool) -> Result<bool,
host_state(&app).hooks.shell_raised(&app, on);
}
SHELL_RAISED.store(on, Ordering::SeqCst);
Ok(host::raise_shell(&webview, on))
let supported = host::raise_shell(&webview, on);
crate::diag::log("host", &format!("shell_raise on={on} -> supported={supported}"));
Ok(supported)
}
/// Whether focus and layering diagnostics are on for this run ([`crate::diag`]).
#[tauri::command]
async fn diag_get(webview: Webview) -> Result<bool, String> {
shell_only(&webview)?;
Ok(crate::diag::active())
}
/// One diagnostic line from the shell (plain short text; nothing is written unless diagnostics are on).
#[tauri::command]
async fn diag_log(webview: Webview, line: String) -> Result<(), String> {
shell_only(&webview)?;
crate::diag::log("shell", &line);
Ok(())
}
#[tauri::command]
@ -1056,6 +1075,8 @@ fn host_handler() -> impl Fn(tauri::ipc::Invoke<Wry>) -> bool + Send + Sync + 's
tab_find,
safe_mode_get,
safe_mode_set,
diag_get,
diag_log,
open_external_url,
shell_focus,
shell_ready,
@ -1213,6 +1234,10 @@ impl App {
crate::media_commands::init(&handle);
// Safe mode for this run: the launch argument, or Shift held while starting (Windows).
crate::safe::set(crate::safe::at_launch(std::env::args(), host::shift_held()));
// Focus and layering diagnostics for this run: `--diag` or WAYBETTER_DIAG=1 (app log folder, diag.log).
if crate::diag::asked(std::env::args(), std::env::var(crate::diag::ENV).ok().as_deref()) {
crate::diag::start(handle.path().app_log_dir().ok());
}
#[cfg(debug_assertions)]
crate::devloop::start(&handle);
if let Some(setup) = setup {

View file

@ -15,6 +15,8 @@ pub const SHELL: &[&str] = &[
"tab_find",
"safe_mode_get",
"safe_mode_set",
"diag_get",
"diag_log",
"open_external_url",
"shell_focus",
"shell_ready",

View file

@ -0,0 +1,66 @@
//! Google Fonts, native side, for every app that sets [`crate::app::AppConfig::fonts`]: the
//! shell-only `gfonts_*` commands ([`crate::commands::GFONTS`]) over [`crate::fonts`]'s downloads
//! and cache (`fonts.googleapis.com` and `fonts.gstatic.com` only, app-data `fonts/`). Site tabs
//! never request fonts and gain no command: native code hands them cached bytes
//! ([`crate::fonts::page_script_for`]).
//!
//! When a font is installed or removed the app's [`crate::app::SiteHooks::fonts_changed`] runs, so
//! it can give every site tab its theme (and the font bytes) again.
use crate::fonts::{self, InstalledFont};
use crate::gfonts::Weights;
use crate::host::shell_only;
use tauri::{AppHandle, Manager, Webview};
/// Opens the font cache in app data. Does nothing unless the app set [`crate::app::AppConfig::fonts`].
pub(crate) fn init(app: &AppHandle) {
if !crate::app::config(app).fonts {
return;
}
if let Ok(dir) = app.path().app_data_dir() {
fonts::init(&dir);
}
}
fn changed(app: &AppHandle) {
crate::app::hooks(app).fonts_changed(app);
}
#[tauri::command]
pub(crate) async fn gfonts_installed(webview: Webview) -> Result<Vec<InstalledFont>, String> {
shell_only(&webview)?;
fonts::installed()
}
#[tauri::command]
pub(crate) async fn gfonts_install(webview: Webview, app: AppHandle, family: String, weights: Weights) -> Result<InstalledFont, String> {
shell_only(&webview)?;
let installed = fonts::install(family, weights).await?;
changed(&app);
Ok(installed)
}
#[tauri::command]
pub(crate) async fn gfonts_remove(webview: Webview, app: AppHandle, family: String) -> Result<(), String> {
shell_only(&webview)?;
fonts::remove(&family)?;
changed(&app);
Ok(())
}
#[tauri::command]
pub(crate) async fn gfonts_faces(webview: Webview, family: String) -> Result<serde_json::Value, String> {
shell_only(&webview)?;
fonts::faces(&family)
}
#[tauri::command]
pub(crate) async fn gfonts_preview(webview: Webview, family: String, weights: Weights, text: String) -> Result<Vec<serde_json::Value>, String> {
shell_only(&webview)?;
fonts::preview(family, weights, text).await
}
/// The fonts commands ([`crate::commands::GFONTS`]).
pub(crate) fn handler() -> impl Fn(tauri::ipc::Invoke<tauri::Wry>) -> bool + Send + Sync + 'static {
tauri::generate_handler![gfonts_installed, gfonts_install, gfonts_remove, gfonts_faces, gfonts_preview]
}

View file

@ -54,6 +54,7 @@ pub mod backup;
pub mod bridge;
pub mod commands;
pub mod devmode;
pub mod diag;
pub mod find;
#[cfg(feature = "net")]
pub mod fonts;

View file

@ -227,6 +227,14 @@ pub fn assert_media_guards() {
}
}
/// The fonts commands' wrappers check their caller first. Runs against the host's source.
pub fn assert_fonts_guards() {
let source = include_str!("fonts_commands.rs");
for command in commands::GFONTS {
assert_guarded(source, command, "shell_only(&webview)?");
}
}
#[cfg(test)]
mod tests {
use super::*;
@ -235,6 +243,7 @@ mod tests {
fn the_store_commands_check_their_caller_first() {
assert_store_guards();
assert_media_guards();
assert_fonts_guards();
}
fn caps_with(shell_extra: &str, tab_extra: &str, site_extra: &str) -> Capabilities {