mirror of
https://github.com/nimbold/Firelink.git
synced 2026-07-27 20:40:12 +00:00
feat(settings): add curated multilingual fonts
Bundle Vazirmatn, Noto Sans Hebrew, Noto Sans SC, and Roboto with locale-aware fallbacks while keeping custom font import out of scope. Refs #31
This commit is contained in:
@@ -4,6 +4,21 @@ Firelink distributes separate executable tools. Firelink's MIT license does not
|
||||
|
||||
Exact versions, target hashes, sources, and build descriptions are pinned in `engines.lock.json`.
|
||||
|
||||
## Bundled fonts
|
||||
|
||||
Firelink bundles variable WOFF2 web fonts through Fontsource. Each font is
|
||||
licensed under the SIL Open Font License, Version 1.1. The corresponding
|
||||
license and author information are available from the linked project sources.
|
||||
|
||||
- Inter: <https://github.com/rsms/inter>
|
||||
- Outfit: <https://github.com/Outfitio/Outfit-Fonts>
|
||||
- Roboto: <https://github.com/googlefonts/roboto-classic>
|
||||
- Vazirmatn: <https://github.com/rastikerdar/vazirmatn>
|
||||
- Noto Sans Hebrew: <https://github.com/notofonts/hebrew>
|
||||
- Noto Sans SC: <https://github.com/notofonts/noto-cjk>
|
||||
|
||||
Fontsource distribution: <https://github.com/fontsource/font-files>
|
||||
|
||||
## aria2
|
||||
|
||||
- Project: <https://aria2.github.io/>
|
||||
|
||||
Generated
+40
@@ -10,7 +10,11 @@
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@fontsource-variable/inter": "^5.3.0",
|
||||
"@fontsource-variable/noto-sans-hebrew": "^5.3.0",
|
||||
"@fontsource-variable/noto-sans-sc": "^5.3.0",
|
||||
"@fontsource-variable/outfit": "^5.3.0",
|
||||
"@fontsource-variable/roboto": "^5.3.0",
|
||||
"@fontsource-variable/vazirmatn": "^5.3.0",
|
||||
"@formkit/auto-animate": "^0.10.0",
|
||||
"@tailwindcss/vite": "^4.3.3",
|
||||
"@tauri-apps/api": "^2.11.1",
|
||||
@@ -92,6 +96,24 @@
|
||||
"url": "https://github.com/sponsors/ayuhito"
|
||||
}
|
||||
},
|
||||
"node_modules/@fontsource-variable/noto-sans-hebrew": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@fontsource-variable/noto-sans-hebrew/-/noto-sans-hebrew-5.3.0.tgz",
|
||||
"integrity": "sha512-jGIZ2EOCTtT1ZhLthz6nEpPXGMniSPI/0tv5CDFDZ7MPYyoj6FskW7WJ0YTyesFADdzjx7F2Pg5W1PWD6y9UFw==",
|
||||
"license": "OFL-1.1",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ayuhito"
|
||||
}
|
||||
},
|
||||
"node_modules/@fontsource-variable/noto-sans-sc": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@fontsource-variable/noto-sans-sc/-/noto-sans-sc-5.3.0.tgz",
|
||||
"integrity": "sha512-lNar1dF7Ik/lHNPo/7JWG0TolXY29LtsqYgMvEysooZ5bsO9uH4shJmRrwyJ3PjyTPljhpMJEK0jDuLSU4vJ1w==",
|
||||
"license": "OFL-1.1",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ayuhito"
|
||||
}
|
||||
},
|
||||
"node_modules/@fontsource-variable/outfit": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@fontsource-variable/outfit/-/outfit-5.3.0.tgz",
|
||||
@@ -101,6 +123,24 @@
|
||||
"url": "https://github.com/sponsors/ayuhito"
|
||||
}
|
||||
},
|
||||
"node_modules/@fontsource-variable/roboto": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@fontsource-variable/roboto/-/roboto-5.3.0.tgz",
|
||||
"integrity": "sha512-BoaaZiQf8fgtxv07KXkTwx1bKz/EoqIlTXq2OOKKXINxp6qXWdnlChqV+hSaDANS49yyUASFl3OVBMMDTCIHFA==",
|
||||
"license": "OFL-1.1",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ayuhito"
|
||||
}
|
||||
},
|
||||
"node_modules/@fontsource-variable/vazirmatn": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@fontsource-variable/vazirmatn/-/vazirmatn-5.3.0.tgz",
|
||||
"integrity": "sha512-ZA68W8JHbMdNzzwjYHUWKB2PH22QpxNEnk0ukcXM+XiqYMV6ZWJZAKuNXcrMR2JjuIAwsfAU3KknCXPI6wXUvw==",
|
||||
"license": "OFL-1.1",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ayuhito"
|
||||
}
|
||||
},
|
||||
"node_modules/@formkit/auto-animate": {
|
||||
"version": "0.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@formkit/auto-animate/-/auto-animate-0.10.0.tgz",
|
||||
|
||||
@@ -40,7 +40,11 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@fontsource-variable/inter": "^5.3.0",
|
||||
"@fontsource-variable/noto-sans-hebrew": "^5.3.0",
|
||||
"@fontsource-variable/noto-sans-sc": "^5.3.0",
|
||||
"@fontsource-variable/outfit": "^5.3.0",
|
||||
"@fontsource-variable/roboto": "^5.3.0",
|
||||
"@fontsource-variable/vazirmatn": "^5.3.0",
|
||||
"@formkit/auto-animate": "^0.10.0",
|
||||
"@tailwindcss/vite": "^4.3.3",
|
||||
"@tauri-apps/api": "^2.11.1",
|
||||
|
||||
@@ -215,12 +215,16 @@ pub enum Theme {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, TS)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[ts(export, export_to = "../../src/bindings/")]
|
||||
pub enum FontFamily {
|
||||
System,
|
||||
Inter,
|
||||
Outfit,
|
||||
Vazirmatn,
|
||||
NotoSansHebrew,
|
||||
NotoSansSc,
|
||||
Roboto,
|
||||
Serif,
|
||||
Monospace,
|
||||
}
|
||||
|
||||
@@ -196,7 +196,17 @@ fn sanitize_persisted_setting_values(state: &mut Value) {
|
||||
sanitize_allowed_string(
|
||||
state,
|
||||
"fontFamily",
|
||||
&["system", "inter", "outfit", "serif", "monospace"],
|
||||
&[
|
||||
"system",
|
||||
"inter",
|
||||
"outfit",
|
||||
"vazirmatn",
|
||||
"noto-sans-hebrew",
|
||||
"noto-sans-sc",
|
||||
"roboto",
|
||||
"serif",
|
||||
"monospace",
|
||||
],
|
||||
);
|
||||
sanitize_allowed_string(
|
||||
state,
|
||||
@@ -704,6 +714,20 @@ mod tests {
|
||||
assert!(matches!(settings.font_family, FontFamily::System));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn curated_font_families_are_preserved() {
|
||||
for value in ["vazirmatn", "noto-sans-hebrew", "noto-sans-sc", "roboto"] {
|
||||
let stored = json!({
|
||||
"state": {"fontFamily": value},
|
||||
"version": 5
|
||||
});
|
||||
|
||||
let settings = decode_stored_settings(&Value::String(stored.to_string())).unwrap();
|
||||
|
||||
assert_eq!(serde_json::to_value(settings.font_family).unwrap(), json!(value));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_window_control_style_uses_automatic_layout() {
|
||||
let stored = json!({
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type FontFamily = "system" | "inter" | "outfit" | "serif" | "monospace";
|
||||
export type FontFamily = "system" | "inter" | "outfit" | "vazirmatn" | "noto-sans-hebrew" | "noto-sans-sc" | "roboto" | "serif" | "monospace";
|
||||
|
||||
@@ -711,6 +711,10 @@ runEngineChecks(false);
|
||||
{ value: 'system', label: t($ => $.settings.lookAndFeel.fontFamilySystem) },
|
||||
{ value: 'inter', label: t($ => $.settings.lookAndFeel.fontFamilyInter) },
|
||||
{ value: 'outfit', label: t($ => $.settings.lookAndFeel.fontFamilyOutfit) },
|
||||
{ value: 'vazirmatn', label: t($ => $.settings.lookAndFeel.fontFamilyVazirmatn) },
|
||||
{ value: 'noto-sans-hebrew', label: t($ => $.settings.lookAndFeel.fontFamilyNotoSansHebrew) },
|
||||
{ value: 'noto-sans-sc', label: t($ => $.settings.lookAndFeel.fontFamilyNotoSansSc) },
|
||||
{ value: 'roboto', label: t($ => $.settings.lookAndFeel.fontFamilyRoboto) },
|
||||
{ value: 'serif', label: t($ => $.settings.lookAndFeel.fontFamilySerif) },
|
||||
{ value: 'monospace', label: t($ => $.settings.lookAndFeel.fontFamilyMonospace) },
|
||||
];
|
||||
|
||||
@@ -623,6 +623,10 @@ const common = {
|
||||
fontFamilySystem: 'System UI (recommended)',
|
||||
fontFamilyInter: 'Inter',
|
||||
fontFamilyOutfit: 'Outfit',
|
||||
fontFamilyVazirmatn: 'Vazirmatn (Persian)',
|
||||
fontFamilyNotoSansHebrew: 'Noto Sans Hebrew',
|
||||
fontFamilyNotoSansSc: 'Noto Sans SC (Simplified Chinese)',
|
||||
fontFamilyRoboto: 'Roboto',
|
||||
fontFamilySerif: 'Serif',
|
||||
fontFamilyMonospace: 'Monospace',
|
||||
fontSize: 'Font size',
|
||||
|
||||
@@ -623,6 +623,10 @@ const fa = {
|
||||
fontFamilySystem: 'رابط کاربری سیستم (پیشنهادشده)',
|
||||
fontFamilyInter: 'Inter',
|
||||
fontFamilyOutfit: 'Outfit',
|
||||
fontFamilyVazirmatn: 'Vazirmatn (فارسی)',
|
||||
fontFamilyNotoSansHebrew: 'Noto Sans Hebrew (عبری)',
|
||||
fontFamilyNotoSansSc: 'Noto Sans SC (چینی سادهشده)',
|
||||
fontFamilyRoboto: 'Roboto',
|
||||
fontFamilySerif: 'سریف',
|
||||
fontFamilyMonospace: 'تکفاصله',
|
||||
fontSize: 'اندازه قلم',
|
||||
|
||||
@@ -623,6 +623,10 @@ const he = {
|
||||
fontFamilySystem: 'ממשק המערכת (מומלץ)',
|
||||
fontFamilyInter: 'Inter',
|
||||
fontFamilyOutfit: 'Outfit',
|
||||
fontFamilyVazirmatn: 'Vazirmatn (פרסית)',
|
||||
fontFamilyNotoSansHebrew: 'Noto Sans Hebrew (עברית)',
|
||||
fontFamilyNotoSansSc: 'Noto Sans SC (סינית מפושטת)',
|
||||
fontFamilyRoboto: 'Roboto',
|
||||
fontFamilySerif: 'סריף',
|
||||
fontFamilyMonospace: 'רוחב קבוע',
|
||||
fontSize: 'גודל גופן',
|
||||
|
||||
@@ -623,6 +623,10 @@ const ru = {
|
||||
fontFamilySystem: 'Системный интерфейс (рекомендуется)',
|
||||
fontFamilyInter: 'Inter',
|
||||
fontFamilyOutfit: 'Outfit',
|
||||
fontFamilyVazirmatn: 'Vazirmatn (персидский)',
|
||||
fontFamilyNotoSansHebrew: 'Noto Sans Hebrew (иврит)',
|
||||
fontFamilyNotoSansSc: 'Noto Sans SC (упрощённый китайский)',
|
||||
fontFamilyRoboto: 'Roboto',
|
||||
fontFamilySerif: 'С засечками',
|
||||
fontFamilyMonospace: 'Моноширинный',
|
||||
fontSize: 'Размер шрифта',
|
||||
|
||||
@@ -623,6 +623,10 @@ const uk = {
|
||||
fontFamilySystem: 'Системний інтерфейс (рекомендовано)',
|
||||
fontFamilyInter: 'Inter',
|
||||
fontFamilyOutfit: 'Outfit',
|
||||
fontFamilyVazirmatn: 'Vazirmatn (перська)',
|
||||
fontFamilyNotoSansHebrew: 'Noto Sans Hebrew (іврит)',
|
||||
fontFamilyNotoSansSc: 'Noto Sans SC (спрощена китайська)',
|
||||
fontFamilyRoboto: 'Roboto',
|
||||
fontFamilySerif: 'Із зарубками',
|
||||
fontFamilyMonospace: 'Моноширинний',
|
||||
fontSize: 'Розмір шрифту',
|
||||
|
||||
@@ -623,6 +623,10 @@ const zhCN = {
|
||||
fontFamilySystem: '系统界面字体(推荐)',
|
||||
fontFamilyInter: 'Inter',
|
||||
fontFamilyOutfit: 'Outfit',
|
||||
fontFamilyVazirmatn: 'Vazirmatn(波斯语)',
|
||||
fontFamilyNotoSansHebrew: 'Noto Sans Hebrew(希伯来语)',
|
||||
fontFamilyNotoSansSc: 'Noto Sans SC(简体中文)',
|
||||
fontFamilyRoboto: 'Roboto',
|
||||
fontFamilySerif: '衬线',
|
||||
fontFamilyMonospace: '等宽',
|
||||
fontSize: '字体大小',
|
||||
|
||||
@@ -96,6 +96,7 @@ describe('translation catalogs', () => {
|
||||
'settings.lookAndFeel.nord',
|
||||
'settings.lookAndFeel.fontFamilyInter',
|
||||
'settings.lookAndFeel.fontFamilyOutfit',
|
||||
'settings.lookAndFeel.fontFamilyRoboto',
|
||||
'settings.lookAndFeel.windowControlStyleWindows',
|
||||
'settings.lookAndFeel.windowControlStyleGnome',
|
||||
'settings.network.chromeWindows',
|
||||
|
||||
+23
-3
@@ -232,21 +232,25 @@
|
||||
--font-stack-system: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Roboto, Helvetica, Arial, var(--font-locale-fallback), "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
||||
--font-stack-inter: "Inter Variable", -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Roboto, Helvetica, Arial, var(--font-locale-fallback), "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
||||
--font-stack-outfit: "Outfit Variable", -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Roboto, Helvetica, Arial, var(--font-locale-fallback), "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
||||
--font-stack-vazirmatn: "Vazirmatn Variable", var(--font-stack-system);
|
||||
--font-stack-noto-sans-hebrew: "Noto Sans Hebrew Variable", var(--font-stack-system);
|
||||
--font-stack-noto-sans-sc: "Noto Sans SC Variable", var(--font-stack-system);
|
||||
--font-stack-roboto: "Roboto Variable", var(--font-stack-system);
|
||||
--font-stack-serif: Georgia, "Times New Roman", var(--font-locale-fallback), serif;
|
||||
--font-stack-monospace: ui-monospace, "SF Mono", Monaco, "Cascadia Code", "Fira Code", "JetBrains Mono", var(--font-locale-fallback), monospace;
|
||||
--font-sans: var(--font-stack-system);
|
||||
}
|
||||
|
||||
html[lang="fa"] {
|
||||
--font-locale-fallback: "Vazirmatn", "Noto Sans Arabic", "Segoe UI", Tahoma, Arial, sans-serif;
|
||||
--font-locale-fallback: "Geeza Pro", "Noto Sans Arabic", "Segoe UI", Tahoma, Arial, sans-serif;
|
||||
}
|
||||
|
||||
html[lang="he"] {
|
||||
--font-locale-fallback: "Arial Hebrew", "Noto Sans Hebrew", "Segoe UI", Arial, sans-serif;
|
||||
--font-locale-fallback: "Arial Hebrew", "Segoe UI", Arial, sans-serif;
|
||||
}
|
||||
|
||||
html[lang="zh-CN"] {
|
||||
--font-locale-fallback: "PingFang SC", "Microsoft YaHei UI", "Noto Sans CJK SC", "Noto Sans SC", sans-serif;
|
||||
--font-locale-fallback: "PingFang SC", "Microsoft YaHei UI", "Noto Sans CJK SC", sans-serif;
|
||||
}
|
||||
|
||||
html[lang="ru"],
|
||||
@@ -266,6 +270,22 @@ html[data-font-family="outfit"] {
|
||||
--font-sans: var(--font-stack-outfit);
|
||||
}
|
||||
|
||||
html[data-font-family="vazirmatn"] {
|
||||
--font-sans: var(--font-stack-vazirmatn);
|
||||
}
|
||||
|
||||
html[data-font-family="noto-sans-hebrew"] {
|
||||
--font-sans: var(--font-stack-noto-sans-hebrew);
|
||||
}
|
||||
|
||||
html[data-font-family="noto-sans-sc"] {
|
||||
--font-sans: var(--font-stack-noto-sans-sc);
|
||||
}
|
||||
|
||||
html[data-font-family="roboto"] {
|
||||
--font-sans: var(--font-stack-roboto);
|
||||
}
|
||||
|
||||
html[data-font-family="serif"] {
|
||||
--font-sans: var(--font-stack-serif);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import { StrictMode } from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import "@fontsource-variable/inter/wght.css";
|
||||
import "@fontsource-variable/noto-sans-hebrew/wght.css";
|
||||
import "@fontsource-variable/noto-sans-sc/wght.css";
|
||||
import "@fontsource-variable/outfit/wght.css";
|
||||
import "@fontsource-variable/roboto/wght.css";
|
||||
import "@fontsource-variable/vazirmatn/wght.css";
|
||||
import "./index.css";
|
||||
import App from "./App";
|
||||
import { i18nReady } from "./i18n";
|
||||
|
||||
@@ -75,7 +75,17 @@ const notifySettingsPersistenceError = () => {
|
||||
};
|
||||
|
||||
const THEME_VALUES = ['system', 'light', 'dark', 'dracula', 'nord'] as const;
|
||||
const FONT_FAMILY_VALUES = ['system', 'inter', 'outfit', 'serif', 'monospace'] as const;
|
||||
const FONT_FAMILY_VALUES = [
|
||||
'system',
|
||||
'inter',
|
||||
'outfit',
|
||||
'vazirmatn',
|
||||
'noto-sans-hebrew',
|
||||
'noto-sans-sc',
|
||||
'roboto',
|
||||
'serif',
|
||||
'monospace',
|
||||
] as const;
|
||||
const WINDOW_CONTROL_STYLE_VALUES = ['auto', 'macos', 'windows', 'gnome', 'minimal'] as const;
|
||||
const APP_FONT_SIZE_VALUES = ['small', 'standard', 'large'] as const;
|
||||
const LIST_ROW_DENSITY_VALUES = ['compact', 'standard', 'relaxed'] as const;
|
||||
|
||||
Reference in New Issue
Block a user