T44d Web · What a browser cannot do says why; fonts resolve; the bundle builds

Demos that open a second window, take files, move the window, read the
clipboard or set native menus show a note on the web in the blocked style.
IBM Plex Sans Regular joins the assets, unmodified, where gpui's svg
renderer loads it: gpui_web answers .SystemUIFont with it. The gallery's
web entry registers a Hebrew cut of Noto Sans Hebrew. scripts/web.sh builds
the bundle with wasm-bindgen and wasm-opt -Oz, 20.0 MB, and fails past 25 MiB.
This commit is contained in:
ZacharyZhang-NY committed 2026-09-29 19:16:41 -04:00
1 parent 48c7b09d8d
commit 2cd2ca8bc1
26 files changed
+381 -76

No files matched your search

@@ -0,0 +1,92 @@
Copyright 2022 The Noto Project Authors (https://github.com/notofonts/hebrew)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION AND CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
Binary file not shown.
+2 -1
View File
@@ -12,7 +12,7 @@ use gpui::{App, IntoElement, ParentElement, SharedString, Styled, Window, div, p
use crate::{
probe::probe,
ui::{change, keep, section},
ui::{NO_FILES, change, keep, section, web_note},
};
/// The switchers' demo: the account and workspace in use.
@@ -115,6 +115,7 @@ pub fn profile(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> {
"Someone at a glance, and their profile to edit: Save waits for a change, a name and an email that reads; Discard puts the saved profile back.",
cx,
)
.children(web_note(NO_FILES, cx))
.child(
div()
.flex()
+2 -1
View File
@@ -15,7 +15,7 @@ use web_time::Instant;
use crate::{
probe::probe,
ui::{keep, noise, section, set},
ui::{NO_FILES, keep, noise, section, set, web_note},
};
const SYSTEM: &str = "You answer about color in plain words. Cite sources.";
@@ -160,6 +160,7 @@ pub fn composer(
"Where a message is written: attachments and context above the field, tools below, send at the end; Enter sends and Shift-Enter breaks the line. A slash at the start offers commands, an at sign offers context, and files dropped on it attach.",
cx,
)
.children(web_note(NO_FILES, cx))
.child(probe(
"chat-composer",
div()
+2 -1
View File
@@ -15,7 +15,7 @@ use gpui::{App, IntoElement, ParentElement, SharedString, Styled, Window, div};
use crate::{
probe::probe,
ui::{keep, picture, row, section, set, specimen, specimens},
ui::{NO_FILES, keep, picture, row, section, set, specimen, specimens, web_note},
};
const DUNES: &str = asset!("dunes-square.jpg");
@@ -119,6 +119,7 @@ pub fn avatars(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> {
"A picture, or initials on a tone the name keeps, or an icon. Presence sits on the corner. Hover the last one, then press it to choose a picture, or drop one on it.",
cx,
)
.children(web_note(NO_FILES, cx))
.child(specimens().children(sizes.map(|(size, name)| {
specimen(name, Avatar::new(SharedString::from(format!("avatar-{name}")), "Kofi Mensah").size(size), cx)
})))
+2 -1
View File
@@ -10,7 +10,7 @@ use jiff::Timestamp;
use crate::{
probe::probe,
ui::{change, keep, section},
ui::{NO_FILES, change, keep, section, web_note},
};
pub fn context_menu(cx: &mut App) -> impl IntoElement + use<> {
@@ -84,6 +84,7 @@ pub fn drop_files(window: &mut Window, cx: &mut App) -> impl IntoElement + use<>
"Files dropped in, as forms::DropZone takes them; each lands in the list below.",
cx,
)
.children(web_note(NO_FILES, cx))
.child(probe(
"files-drop",
div()
+2 -1
View File
@@ -5,7 +5,7 @@ use gpui::{App, IntoElement, ParentElement, Styled, Window, div, px};
use crate::{
probe::probe,
ui::{keep, section, set, specimen, specimens},
ui::{NO_FILES, keep, section, set, specimen, specimens, web_note},
};
fn names(paths: &[PathBuf]) -> String {
@@ -44,6 +44,7 @@ pub fn files(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> {
"Click a field or Browse for the system's dialog, or drop files from Finder. A folder, or two files on a one-file target, turns it red.",
cx,
)
.children(web_note(NO_FILES, cx))
.child(
specimens()
.child(specimen(
+2 -1
View File
@@ -10,7 +10,7 @@ use gpui::{App, IntoElement, Keystroke, ParentElement, SharedString, Styled, Win
use super::text::field;
use crate::{
probe::probe,
ui::{code, section},
ui::{NO_FILES, code, section, web_note},
};
pub fn mention(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> {
@@ -99,6 +99,7 @@ pub fn path(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> {
"Type a path, or Browse with the system's dialog.",
cx,
)
.children(web_note(NO_FILES, cx))
.child(probe(
"path",
div()
+5 -1
View File
@@ -9,7 +9,7 @@ use gpui::{App, IntoElement, ParentElement, SharedString, Styled, Window, div, p
use crate::{
probe::probe,
ui::{keep, picture, section, set},
ui::{keep, picture, section, set, web_note},
};
const ATRIUM: &str = asset!("atrium.jpg");
@@ -106,6 +106,10 @@ pub fn upload(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> {
"A picture to send: drop one or browse for it, then crop it. Replace picks another; Remove empties it.",
cx,
)
.children(web_note(
"A browser hands gpui no files: on the web the file dialog fails, drops never arrive, and the picture this starts with, a file on disk, cannot be read.",
cx,
))
.child(probe(
"media-upload",
div().w(px(420.)).child(
+13 -2
View File
@@ -9,16 +9,27 @@ use gpui::{App, IntoElement, ParentElement, Styled, Window, div, px};
use super::rows::{ran, run};
use crate::{
probe::probe,
ui::{section, specimen},
ui::{section, specimen, web_note},
};
pub fn menu_bar(cx: &mut App) -> impl IntoElement + use<> {
let menus = cx.get_menus().expect("the gallery sets its menus in main");
let menus = match cx.get_menus() {
Some(menus) => menus,
None if cfg!(target_family = "wasm") => crate::app_menus()
.into_iter()
.map(gpui::Menu::owned)
.collect(),
None => panic!("the gallery sets its menus in main"),
};
section(
"MenuBar Menu",
"The app's own menus, the ones cx.set_menus gave the system, drawn in the window. Picking a row dispatches its action.",
cx,
)
.children(web_note(
"A browser page has no menu bar, and gpui on the web keeps no cx.set_menus: this bar draws the gallery's own list of the same menus.",
cx,
))
.child(specimen(
"hover moves between menus while one is open",
probe("menubar", MenuBar::new("app-bar", menus)),
@@ -8,7 +8,7 @@ use gpui::{App, Entity, IntoElement, ParentElement, SharedString, Window};
use crate::{
probe::{Opened, probe},
ui::{keep, row, section, set, specimen},
ui::{keep, own_window, row, section, set, specimen},
};
/// Moves `value` to the front of a recent list, keeping `size` entries.
@@ -398,7 +398,7 @@ pub fn search_palette(window: &mut Window, cx: &mut App) -> impl IntoElement + u
))
.child(specimen(
"a window above all others",
probe(
own_window(probe(
"launcher",
Button::new("launcher-button", "Open the launcher")
.variant(ButtonVariant::Ghost)
@@ -415,7 +415,7 @@ pub fn search_palette(window: &mut Window, cx: &mut App) -> impl IntoElement + u
Err(error) => log::error!("gallery: launcher: {error:#}"),
}
}),
),
), cx),
cx,
)),
)
+5 -1
View File
@@ -13,7 +13,7 @@ use gpui::{
use crate::{
probe::probe,
ui::{code, row, section},
ui::{code, row, section, web_note},
};
pub fn boxes(cx: &App) -> impl IntoElement + use<> {
@@ -445,6 +445,10 @@ pub fn clipboard(window: &mut Window, cx: &mut App) -> impl IntoElement + use<>
"write_to_clipboard and read_from_clipboard on App.",
cx,
)
.children(web_note(
"A browser reads its clipboard only as it pastes, into a field: on the web gpui's read_from_clipboard finds nothing, so Paste reports no text. Copy works.",
cx,
))
.child(
row()
.gap_3()
+2 -1
View File
@@ -11,7 +11,7 @@ use ely_gpui_component::{
use gpui::{AnyElement, App, Entity, IntoElement, ParentElement, Styled, div, px};
use super::window::{Fields, Prefs, SIZE};
use crate::ui::change;
use crate::ui::{NO_FILES, change, web_note};
/// The open section's page.
pub(super) fn page(prefs: &Entity<Prefs>, fields: &Fields, section: &str, cx: &App) -> AnyElement {
@@ -198,6 +198,7 @@ fn storage(prefs: &Entity<Prefs>, cx: &App) -> AnyElement {
}),
),
)
.children(web_note(NO_FILES, cx))
.child(
SettingsSection::new("Defaults")
.description("A larger font size shows here.")
+17 -1
View File
@@ -7,7 +7,7 @@ use ely_gpui_component::{
};
use gpui::{App, Div, IntoElement, ParentElement, Styled, Window, div, px};
use crate::ui::{code, row, section, specimen, specimens};
use crate::ui::{code, row, section, specimen, specimens, web_note};
/// A window drawn inside the page.
pub fn window_frame(width: f32, height: f32, cx: &App) -> Div {
@@ -116,6 +116,10 @@ pub fn window_controls(cx: &App) -> impl IntoElement + use<> {
"Minimize and zoom act on this window. Close here only logs.",
cx,
))
.children(web_note(
"A browser owns the page's window: gpui on the web only logs minimize and zoom.",
cx,
))
}
pub fn drag_area(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> {
@@ -142,6 +146,10 @@ pub fn drag_area(window: &mut Window, cx: &mut App) -> impl IntoElement + use<>
.child("Double-click to zoom this window"),
)
.child(code("drag_region(id, window, cx)", cx))
.children(web_note(
"A browser owns the page's window: gpui on the web neither moves nor zooms it.",
cx,
))
}
pub fn resize_border(cx: &App) -> impl IntoElement + use<> {
@@ -154,6 +162,10 @@ pub fn resize_border(cx: &App) -> impl IntoElement + use<> {
"Linux draws its own edges. This macOS window keeps the system's, so the border lays out only.",
))
.child(code("ResizeBorder::new().child(app)", cx))
.children(web_note(
"A browser sizes the page's canvas itself: gpui on the web resizes no window from its edges.",
cx,
))
}
pub fn menus(cx: &App) -> impl IntoElement + use<> {
@@ -177,4 +189,8 @@ pub fn menus(cx: &App) -> impl IntoElement + use<> {
.child(Caption::new(
"MenuBar draws the same menus inside the window. It lands with Menus, chapter 8.",
))
.children(web_note(
"A browser page has no menu bar and no Dock: gpui on the web keeps neither cx.set_menus nor cx.set_dock_menu.",
cx,
))
}
+23 -17
View File
@@ -11,7 +11,7 @@ use gpui::{App, IntoElement, ParentElement, Styled, Window, div};
use super::chrome::window_frame;
use crate::{
probe::{Opened, probe},
ui::{row, section, specimen},
ui::{own_window, row, section, specimen},
};
fn log_action(name: &'static str) -> impl Fn(&mut Window, &mut App) + 'static {
@@ -59,14 +59,17 @@ pub fn update_window(cx: &App) -> impl IntoElement + use<> {
.child(update_dialog().on_later(log_action("later"))),
cx,
))
.child(probe(
"update-open",
Button::new("update-open", "Open update window")
.variant(ButtonVariant::Outline)
.on_click(|_, _, cx| match update_dialog().open(cx) {
Ok(handle) => Opened::insert("update", handle.into(), cx),
Err(error) => log::error!("gallery: update window failed: {error:#}"),
}),
.child(own_window(
probe(
"update-open",
Button::new("update-open", "Open update window")
.variant(ButtonVariant::Outline)
.on_click(|_, _, cx| match update_dialog().open(cx) {
Ok(handle) => Opened::insert("update", handle.into(), cx),
Err(error) => log::error!("gallery: update window failed: {error:#}"),
}),
),
cx,
))
}
@@ -101,14 +104,17 @@ pub fn crash_reporter(cx: &App) -> impl IntoElement + use<> {
.child(reporter(true)),
cx,
))
.child(probe(
"crash-open",
Button::new("crash-open", "Open crash window")
.variant(ButtonVariant::Outline)
.on_click(move |_, _, cx| match reporter(false).open(cx) {
Ok(handle) => Opened::insert("crash", handle.into(), cx),
Err(error) => log::error!("gallery: crash window failed: {error:#}"),
}),
.child(own_window(
probe(
"crash-open",
Button::new("crash-open", "Open crash window")
.variant(ButtonVariant::Outline)
.on_click(move |_, _, cx| match reporter(false).open(cx) {
Ok(handle) => Opened::insert("crash", handle.into(), cx),
Err(error) => log::error!("gallery: crash window failed: {error:#}"),
}),
),
cx,
))
}
+14 -2
View File
@@ -7,7 +7,7 @@ use gpui::{App, IntoElement, ParentElement, SharedString, Window};
use crate::{
probe::probe,
ui::{blocked, row, section},
ui::{blocked, row, section, web_note},
};
const ITEMS: [&str; 3] = ["Open gallery", "Toggle theme", "Quit"];
@@ -23,6 +23,10 @@ pub fn tray(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> {
"An SF Symbol in the menu bar with a menu. macOS only.",
cx,
)
.children(web_note(
"A browser page has no menu bar to hold an icon, so showing one fails and logs why.",
cx,
))
.child(
row()
.child(probe(
@@ -113,6 +117,10 @@ pub fn dock(cx: &mut App) -> impl IntoElement + use<> {
"A count on the app's Dock icon. macOS only.",
cx,
)
.children(web_note(
"A browser page has no Dock icon to badge, so setting one fails and logs why.",
cx,
))
.child(
row()
.child(probe(
@@ -138,7 +146,11 @@ pub fn notification(cx: &App) -> impl IntoElement + use<> {
cx,
)
.child(blocked(
"Not yet shown: gpui posts a system notification only from an app bundle, and the gallery runs unbundled.",
if cfg!(target_family = "wasm") {
"Not shown: gpui on the web posts no system notification."
} else {
"Not yet shown: gpui posts a system notification only from an app bundle, and the gallery runs unbundled."
},
cx,
))
}
+2 -1
View File
@@ -4,7 +4,7 @@ use ely_gpui_component::{
};
use gpui::{App, IntoElement, ParentElement, SharedString, Styled, Window, div, px};
use crate::ui::{change, keep, section, specimen, specimens};
use crate::ui::{NO_FILES, change, keep, section, specimen, specimens, web_note};
/// Which code palette the demo last chose.
struct Chosen(SharedString);
@@ -17,6 +17,7 @@ pub fn importing(window: &mut Window, cx: &mut App) -> impl IntoElement + use<>
"A VS Code color theme dropped here reads into Ely's palette, the colors it leaves out kept from Ely's; Apply sets it on the whole gallery. Code palettes choose the colors of code for the mode shown.",
cx,
)
.children(web_note(NO_FILES, cx))
.child(
specimens()
.child(specimen(
+13 -9
View File
@@ -14,7 +14,7 @@ use gpui::{
use crate::{
probe::{Opened, probe},
ui::{change, keep, section, specimen, specimens},
ui::{change, keep, native_only, section, specimen, specimens},
};
const PLATFORMS: [(Platform, &str); 3] = [
@@ -38,10 +38,14 @@ pub fn platform(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> {
.child(specimen("IconTheme", icons(window, cx), cx))
.child(specimen(
"Vibrancy",
probe(
"frosted-open",
Button::new("frosted-open", "Open a frosted window")
.on_click(|_, _, cx| frosted(cx)),
native_only(
probe(
"frosted-open",
Button::new("frosted-open", "Open a frosted window")
.on_click(|_, _, cx| frosted(cx)),
),
"A browser page is a single window, and gpui leaves what lies behind it to the browser, so no frosted window opens and nothing blurs through.",
cx,
),
cx,
)),
@@ -141,10 +145,10 @@ fn frosted(cx: &mut App) {
window_background: WindowBackgroundAppearance::Blurred,
..Default::default()
};
let handle = cx
.open_window(options, |_, cx| cx.new(|_| Frosted))
.expect("the frosted window opens");
Opened::insert("frosted", handle.into(), cx);
match cx.open_window(options, |_, cx| cx.new(|_| Frosted)) {
Ok(handle) => Opened::insert("frosted", handle.into(), cx),
Err(error) => log::error!("gallery: frosted window failed: {error:#}"),
}
}
/// A window whose sidebar lets the desktop through.
+22 -18
View File
@@ -227,6 +227,19 @@ impl Gallery {
}
}
/// The window's root: the focus scope, over Ely's ground, ink and type.
fn root(&self, cx: &App) -> FocusScope {
let theme = cx.theme();
FocusScope::new(&self.focus)
.root()
.size_full()
.flex()
.bg(theme.colors.bg)
.text_color(theme.colors.fg)
.font_family(theme.font_family.clone())
.text_size(theme.text_size(TextSize::Base))
}
/// The page's demos, or the story's alone; why not, once the start or the story failed.
fn body(
&mut self,
@@ -259,24 +272,10 @@ impl Gallery {
impl Render for Gallery {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let page = &pages::ALL[self.page];
let body = self.body(page, window, cx);
let sidebar = self.sidebar(cx);
let switcher = self.switcher(cx);
let theme = cx.theme();
let colors = &theme.colors;
let root = FocusScope::new(&self.focus)
.root()
.size_full()
.flex()
.bg(colors.bg)
.text_color(colors.fg)
.font_family(theme.font_family.clone())
.text_size(theme.text_size(TextSize::Base));
let body = match body {
let body = match self.body(page, window, cx) {
Ok(body) => body,
Err(failure) => {
return root.child(
return self.root(cx).child(
div().w_full().p_6().child(
Alert::new("gallery-failure", Severity::Danger, "Nothing to show")
.body(failure),
@@ -285,7 +284,7 @@ impl Render for Gallery {
}
};
if self.story.is_some() {
return root.child(
return self.root(cx).child(
on_axis(div().id(("story", self.pass)))
.size_full()
.overflow_y_scroll()
@@ -293,7 +292,12 @@ impl Render for Gallery {
.child(div().p_6().child(body)),
);
}
root.child(sidebar).child(
let sidebar = self.sidebar(cx);
let switcher = self.switcher(cx);
let theme = cx.theme();
let colors = &theme.colors;
self.root(cx).child(sidebar).child(
on_axis(div().id(("page", self.pass)))
.flex_1()
.h_full()
+15 -6
View File
@@ -163,20 +163,29 @@ pub fn blocked(text: impl Into<SharedString>, cx: &App) -> impl IntoElement {
.child(div().flex_1().min_w_0().child(text.into()))
}
/// Why a demo that takes files takes none on the web.
pub const NO_FILES: &str = "A browser hands gpui no files: on the web the file dialog fails and dropped files never arrive, so nothing is taken here.";
/// What a browser cannot do here, and why: a note on the web, nothing natively.
pub fn web_note(text: &'static str, cx: &App) -> Option<AnyElement> {
cfg!(target_family = "wasm").then(|| blocked(text, cx).into_any_element())
}
/// A button that opens a window of its own; a browser page is one window, so there a note stands in.
/// A demo a browser cannot run: on the web its note stands in.
pub fn native_only(element: impl IntoElement, note: &'static str, cx: &App) -> AnyElement {
match web_note(note, cx) {
Some(note) => note,
None => element.into_any_element(),
}
}
/// A button that opens a window of its own, which a browser page cannot.
pub fn own_window(button: impl IntoElement, cx: &App) -> AnyElement {
match web_note(
native_only(
button,
"A browser page is a single window: gpui opens no second window or popup on the web, so this one cannot open here.",
cx,
) {
Some(note) => note,
None => button.into_any_element(),
}
)
}
/// A demo's state, kept across frames under `key`.
+7 -1
View File
@@ -1,4 +1,4 @@
use std::{cell::RefCell, rc::Rc, sync::Arc};
use std::{borrow::Cow, cell::RefCell, rc::Rc, sync::Arc};
use anyhow::{Context as _, Result, anyhow, bail};
use ely_gpui_component::Assets;
@@ -9,6 +9,9 @@ use web_sys::UrlSearchParams;
use crate::{Start, choose, launch, pages, shell::Choice, shell::Gallery};
/// The i18n page's Hebrew: gpui_web asks the browser for emoji and CJK alone.
const HEBREW: &[u8] = include_bytes!("fonts/NotoSansHebrew-Regular.ttf");
thread_local! {
/// The running app and its window, for the host page's calls.
static HOST: RefCell<Option<(AsyncApp, WindowHandle<Gallery>)>> = const { RefCell::new(None) };
@@ -95,6 +98,9 @@ pub fn start() {
.with_http_client(http)
.with_assets(Assets)
.run(move |cx| {
cx.text_system()
.add_fonts(vec![Cow::Borrowed(HEBREW)])
.expect("the gallery's Hebrew face registers");
let window = launch(start, cx);
HOST.with(|host| *host.borrow_mut() = Some((cx.to_async(), window)));
});