fix web build

This commit is contained in:
2026-10-24 19:37:04 +07:00
parent 8929e3137b
commit f829a2774d
5 changed files with 182 additions and 57 deletions
+113 -40
View File
@@ -1,22 +1,33 @@
use std::cell::RefCell;
use std::rc::Rc;
use gpui::*;
use universal_time::{Instant, MonotonicClock, SystemTime, WallClock, define_time_provider};
use wasm_bindgen::prelude::*;
struct CustomTimeProvider;
thread_local! {
/// Dropping the handle releases the app, so the page's lifetime must own it.
static APPLICATION: RefCell<Option<ApplicationHandle>> = const { RefCell::new(None) };
}
impl WallClock for CustomTimeProvider {
/// Serves `Date.now()` as the wall clock and `performance.now()` as the monotonic clock.
struct BrowserTimeProvider;
impl WallClock for BrowserTimeProvider {
fn system_time(&self) -> SystemTime {
SystemTime::from_unix_duration(instant::Duration::from_secs(0))
SystemTime::from_unix_duration(core::time::Duration::from_secs_f64(
js_sys::Date::now() / 1000.0,
))
}
}
impl MonotonicClock for CustomTimeProvider {
impl MonotonicClock for BrowserTimeProvider {
fn instant(&self) -> Instant {
Instant::from_ticks(instant::Duration::from_secs(0))
Instant::from_ticks(core::time::Duration::from_secs_f64(instant::now() / 1000.0))
}
}
define_time_provider!(CustomTimeProvider);
define_time_provider!(BrowserTimeProvider);
#[wasm_bindgen]
pub fn run() -> Result<(), JsValue> {
@@ -28,54 +39,116 @@ pub fn run() -> Result<(), JsValue> {
// Also initialize tracing for WASM
tracing_wasm::set_as_global_default();
#[cfg(target_family = "wasm")]
// Sets the panic hook and GPUI's console logger
gpui_platform::web_init();
#[cfg(not(target_family = "wasm"))]
let app = gpui_platform::application();
// A single-threaded platform that draws emoji and CJK graphemes missing
// from the bundled fonts with the browser's own fonts, plus a fetch-based
// HTTP client so remote images and content work like on desktop
let platform = std::rc::Rc::new(gpui_web::WebPlatform::new_with_backend_and_font_fallback(
false,
gpui_web::WebBackendPreference::Auto,
gpui_web::CanvasFontFallback::EmojiAndCjk,
));
let http_client = std::sync::Arc::new(platform.fetch_http_client());
let app = Application::with_platform(platform)
.with_http_client(http_client)
.with_assets(assets::CombinedAssets);
#[cfg(target_family = "wasm")]
let app = {
let app = gpui_platform::single_threaded_web();
APPLICATION.with(|application| {
*application.borrow_mut() = Some(app.run_embedded(|cx| {
// Initialize the framework before anything renders
gpui_kit::init(cx);
// Temporary fix: intentionally leak the `Rc<AppCell>` to keep the application alive
struct WasmApplication(std::rc::Rc<AppCell>);
let wasm_app = unsafe { std::mem::transmute::<Application, WasmApplication>(app) };
std::mem::forget(wasm_app.0.clone());
unsafe { std::mem::transmute::<WasmApplication, Application>(wasm_app) }
};
// The web text system starts with no fonts, and GPUI panics when
// it lays out text in a family that does not exist. Inter is both
// bundled and named on the theme (including both mode configs, so
// theme reloads keep it) before any text is laid out
load_embedded_fonts(cx);
apply_web_fonts(cx);
app.run(|cx| {
// Initialize the framework before anything renders
gpui_kit::init(cx);
// Initialize settings
settings::init(cx);
// Initialize settings
settings::init(cx);
// There is no native theme on the web; apply the persisted
// appearance directly, then the user's theme tuning
apply_appearance(settings::persisted_appearance_mode(), cx);
settings::AppSettings::apply_theme_style(cx);
// Initialize the nostr client
state::init(cx, None);
// Initialize the nostr client
state::init(cx, None);
// Initialize person registry
person::init(cx);
// Initialize person registry
person::init(cx);
// Initialize device signer
//
// NIP-4e: https://github.com/nostr-protocol/nips/blob/per-device-keys/4e.md
nip4e::init(cx);
// Initialize device signer
//
// NIP-4e: https://github.com/nostr-protocol/nips/blob/per-device-keys/4e.md
nip4e::init(cx);
// Initialize app registry
chat::init(cx);
// Initialize app registry
chat::init(cx);
// Initialize groups registry
nip29::init(cx);
// Initialize groups registry
nip29::init(cx);
gpui_kit::open_window(WindowOptions::default(), cx, |window, cx| {
workspace::init(window, cx)
})
.expect("Failed to open window. Please restart the application.");
gpui_kit::open_window(WindowOptions::default(), cx, |window, cx| {
workspace::init(window, cx)
})
.expect("Failed to open window. Please restart the application.");
cx.activate(true);
cx.activate(true);
}));
});
Ok(())
}
/// Register every bundled font with the text system before any text is laid out
fn load_embedded_fonts(cx: &mut App) {
let font_paths = assets::Assets::iter()
.filter(|path| path.starts_with("fonts/") && path.ends_with(".ttf"))
.collect::<Vec<_>>();
let fonts = font_paths
.iter()
.filter_map(|path| assets::Assets::get(path.as_ref()))
.map(|font| font.data)
.collect::<Vec<_>>();
cx.text_system()
.add_fonts(fonts)
.expect("Failed to load bundled fonts");
}
/// Name Inter on the theme and both mode configs so every text run renders in
/// it and theme reloads keep it. Without an explicit family the theme falls
/// back to `.SystemUIFont`, which the web platform resolves to a family that
/// is not bundled
fn apply_web_fonts(cx: &mut App) {
use gpui_kit::component::Theme;
const UI_FONT: &str = "Inter";
Theme::update(cx, |theme| {
theme.font_family = UI_FONT.into();
theme.mono_font_family = UI_FONT.into();
for config in [&mut theme.light_theme, &mut theme.dark_theme] {
let config = Rc::make_mut(config);
config.font_family = Some(UI_FONT.into());
config.mono_font_family = Some(UI_FONT.into());
}
});
}
/// Honor the appearance mode against the default palettes
fn apply_appearance(appearance: settings::AppearanceMode, cx: &mut App) {
use gpui_kit::component::{Theme, ThemeMode};
match appearance {
settings::AppearanceMode::System => Theme::sync_system_appearance(None, cx),
settings::AppearanceMode::Light => Theme::change(ThemeMode::Light, None, cx),
settings::AppearanceMode::Dark => Theme::change(ThemeMode::Dark, None, cx),
}
}