mirror of
https://github.com/SECTL/SecScore.git
synced 2026-07-19 19:04:23 +08:00
fix: 字体选择列表加载不了的问题
feat: 整理项目文件
This commit is contained in:
Binary file not shown.
@@ -1,6 +1,8 @@
|
||||
use parking_lot::RwLock;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value as JsonValue;
|
||||
use std::collections::BTreeSet;
|
||||
use std::process::Command;
|
||||
use std::sync::Arc;
|
||||
use tauri::{AppHandle, Emitter, State};
|
||||
|
||||
@@ -158,3 +160,135 @@ pub async fn settings_set(
|
||||
|
||||
Ok(IpcResponse::success(()))
|
||||
}
|
||||
|
||||
fn fallback_font_families() -> Vec<String> {
|
||||
vec![
|
||||
"系统默认".to_string(),
|
||||
"Microsoft YaHei UI".to_string(),
|
||||
"Microsoft YaHei".to_string(),
|
||||
"PingFang SC".to_string(),
|
||||
"Noto Sans CJK SC".to_string(),
|
||||
"Source Han Sans SC".to_string(),
|
||||
"Segoe UI".to_string(),
|
||||
"Arial".to_string(),
|
||||
"Helvetica Neue".to_string(),
|
||||
]
|
||||
}
|
||||
|
||||
fn normalize_font_name(value: &str) -> String {
|
||||
value
|
||||
.trim()
|
||||
.trim_start_matches('@')
|
||||
.replace(" (TrueType)", "")
|
||||
.replace(" (OpenType)", "")
|
||||
.replace(" (All res)", "")
|
||||
.trim()
|
||||
.to_string()
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn query_system_fonts() -> Vec<String> {
|
||||
let output = Command::new("reg")
|
||||
.args([
|
||||
"query",
|
||||
r"HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts",
|
||||
])
|
||||
.output();
|
||||
|
||||
let Ok(output) = output else {
|
||||
return Vec::new();
|
||||
};
|
||||
if !output.status.success() {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let mut families = BTreeSet::new();
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
for line in stdout.lines() {
|
||||
if !line.contains("REG_") {
|
||||
continue;
|
||||
}
|
||||
let Some((left, _)) = line.split_once("REG_") else {
|
||||
continue;
|
||||
};
|
||||
let name = normalize_font_name(left);
|
||||
if !name.is_empty() {
|
||||
families.insert(name);
|
||||
}
|
||||
}
|
||||
|
||||
families.into_iter().collect()
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn query_system_fonts() -> Vec<String> {
|
||||
let output = Command::new("fc-list").args([":", "family"]).output();
|
||||
let Ok(output) = output else {
|
||||
return Vec::new();
|
||||
};
|
||||
if !output.status.success() {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let mut families = BTreeSet::new();
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
for line in stdout.lines() {
|
||||
for part in line.split(',') {
|
||||
let name = normalize_font_name(part);
|
||||
if !name.is_empty() {
|
||||
families.insert(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
families.into_iter().collect()
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn query_system_fonts() -> Vec<String> {
|
||||
let output = Command::new("system_profiler")
|
||||
.args(["SPFontsDataType", "-detailLevel", "mini"])
|
||||
.output();
|
||||
let Ok(output) = output else {
|
||||
return Vec::new();
|
||||
};
|
||||
if !output.status.success() {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let mut families = BTreeSet::new();
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
for line in stdout.lines() {
|
||||
let trimmed = line.trim();
|
||||
if let Some(name) = trimmed.strip_prefix("Full Name:") {
|
||||
let normalized = normalize_font_name(name);
|
||||
if !normalized.is_empty() {
|
||||
families.insert(normalized);
|
||||
}
|
||||
}
|
||||
}
|
||||
families.into_iter().collect()
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))]
|
||||
fn query_system_fonts() -> Vec<String> {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn settings_get_system_fonts() -> Result<IpcResponse<Vec<String>>, String> {
|
||||
let mut fonts = query_system_fonts();
|
||||
if fonts.is_empty() {
|
||||
fonts = fallback_font_families();
|
||||
} else {
|
||||
let mut merged = BTreeSet::new();
|
||||
for item in fallback_font_families() {
|
||||
merged.insert(item);
|
||||
}
|
||||
for item in fonts {
|
||||
merged.insert(item);
|
||||
}
|
||||
fonts = merged.into_iter().collect();
|
||||
}
|
||||
|
||||
Ok(IpcResponse::success(fonts))
|
||||
}
|
||||
|
||||
@@ -68,6 +68,7 @@ pub fn run() {
|
||||
settings_get_all,
|
||||
settings_get,
|
||||
settings_set,
|
||||
settings_get_system_fonts,
|
||||
auth_get_status,
|
||||
auth_login,
|
||||
auth_logout,
|
||||
@@ -155,7 +156,11 @@ fn setup_deep_link(app: &mut App) -> Result<(), Box<dyn std::error::Error>> {
|
||||
use tauri_plugin_deep_link::DeepLinkExt;
|
||||
|
||||
app.deep_link().on_open_url(move |event| {
|
||||
let url = event.urls().first().map(|u| u.to_string()).unwrap_or_default();
|
||||
let url = event
|
||||
.urls()
|
||||
.first()
|
||||
.map(|u| u.to_string())
|
||||
.unwrap_or_default();
|
||||
if !url.is_empty() {
|
||||
let _ = handle.emit("deep-link://new-url", url);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ pub struct SettingsSpec {
|
||||
pub window_zoom: f64,
|
||||
pub search_keyboard_layout: String,
|
||||
pub disable_search_keyboard: bool,
|
||||
pub font_family: String,
|
||||
pub themes_custom: JsonValue,
|
||||
pub auto_score_enabled: bool,
|
||||
pub auto_score_rules: JsonValue,
|
||||
@@ -28,6 +29,7 @@ impl Default for SettingsSpec {
|
||||
window_zoom: 1.0,
|
||||
search_keyboard_layout: "qwerty26".to_string(),
|
||||
disable_search_keyboard: false,
|
||||
font_family: "system".to_string(),
|
||||
themes_custom: JsonValue::Array(vec![]),
|
||||
auto_score_enabled: false,
|
||||
auto_score_rules: JsonValue::Array(vec![]),
|
||||
@@ -58,6 +60,7 @@ pub enum SettingsKey {
|
||||
WindowZoom,
|
||||
SearchKeyboardLayout,
|
||||
DisableSearchKeyboard,
|
||||
FontFamily,
|
||||
ThemesCustom,
|
||||
AutoScoreEnabled,
|
||||
AutoScoreRules,
|
||||
@@ -76,6 +79,7 @@ impl SettingsKey {
|
||||
SettingsKey::WindowZoom => "window_zoom",
|
||||
SettingsKey::SearchKeyboardLayout => "search_keyboard_layout",
|
||||
SettingsKey::DisableSearchKeyboard => "disable_search_keyboard",
|
||||
SettingsKey::FontFamily => "font_family",
|
||||
SettingsKey::ThemesCustom => "themes_custom",
|
||||
SettingsKey::AutoScoreEnabled => "auto_score_enabled",
|
||||
SettingsKey::AutoScoreRules => "auto_score_rules",
|
||||
@@ -94,6 +98,7 @@ impl SettingsKey {
|
||||
"window_zoom" => Some(SettingsKey::WindowZoom),
|
||||
"search_keyboard_layout" => Some(SettingsKey::SearchKeyboardLayout),
|
||||
"disable_search_keyboard" => Some(SettingsKey::DisableSearchKeyboard),
|
||||
"font_family" => Some(SettingsKey::FontFamily),
|
||||
"themes_custom" => Some(SettingsKey::ThemesCustom),
|
||||
"auto_score_enabled" => Some(SettingsKey::AutoScoreEnabled),
|
||||
"auto_score_rules" => Some(SettingsKey::AutoScoreRules),
|
||||
@@ -327,6 +332,22 @@ impl SettingsService {
|
||||
},
|
||||
);
|
||||
|
||||
defs.insert(
|
||||
SettingsKey::FontFamily,
|
||||
SettingDefinition {
|
||||
kind: SettingValueKind::String,
|
||||
default_value: SettingsValue::String("system".to_string()),
|
||||
write_permission: PermissionRequirement::Admin,
|
||||
validate: Some(|v| {
|
||||
if let SettingsValue::String(s) = v {
|
||||
!s.trim().is_empty()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
defs.insert(
|
||||
SettingsKey::ThemesCustom,
|
||||
SettingDefinition {
|
||||
@@ -549,6 +570,10 @@ impl SettingsService {
|
||||
SettingsValue::Boolean(b) => b,
|
||||
_ => false,
|
||||
},
|
||||
font_family: match self.get_value(SettingsKey::FontFamily) {
|
||||
SettingsValue::String(s) => s,
|
||||
_ => "system".to_string(),
|
||||
},
|
||||
themes_custom: match self.get_value(SettingsKey::ThemesCustom) {
|
||||
SettingsValue::Json(j) => j,
|
||||
_ => JsonValue::Array(vec![]),
|
||||
|
||||
+27
-4
@@ -29,8 +29,8 @@ import { useTranslation } from "react-i18next"
|
||||
import { Sidebar } from "./components/Sidebar"
|
||||
import { ContentArea } from "./components/ContentArea"
|
||||
import { OOBE } from "./components/OOBE/OOBE"
|
||||
import { OAuthLogin } from "./components/OAuthLogin"
|
||||
import { OAuthCallback } from "./components/OAuthCallback"
|
||||
import { OAuthLogin } from "./components/OAuth/OAuthLogin"
|
||||
import { OAuthCallback } from "./components/OAuth/OAuthCallback"
|
||||
import { ThemeProvider, useTheme } from "./contexts/ThemeContext"
|
||||
import { MOBILE_NAV_ITEMS, MobileNavKey, sanitizeMobileNavKeys } from "./shared/mobileNavigation"
|
||||
|
||||
@@ -39,6 +39,23 @@ const DEFAULT_MOBILE_BOTTOM_PRIMARY_KEYS: MobileNavKey[] = DEFAULT_MOBILE_BOTTOM
|
||||
0,
|
||||
4
|
||||
)
|
||||
const SYSTEM_FONT_STACK =
|
||||
'"PingFang SC", "PingFangTC-Regular", "Hiragino Sans GB", "Hiragino Sans", "STHeiti", "Heiti SC", "Noto Sans CJK SC", "Noto Sans SC", "Source Han Sans SC", "Microsoft YaHei UI", "Microsoft YaHei", "微软雅黑", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", sans-serif'
|
||||
|
||||
const resolveFontFamily = (fontValue?: string): string => {
|
||||
if (!fontValue || fontValue === "system") return SYSTEM_FONT_STACK
|
||||
if (fontValue.startsWith("system-")) {
|
||||
const family = fontValue.slice("system-".length).trim()
|
||||
if (family) return `"${family}", ${SYSTEM_FONT_STACK}`
|
||||
}
|
||||
return SYSTEM_FONT_STACK
|
||||
}
|
||||
|
||||
const applyGlobalFontFamily = (fontValue?: string) => {
|
||||
const fontFamily = resolveFontFamily(fontValue)
|
||||
document.documentElement.style.setProperty("--ss-font-family", fontFamily)
|
||||
document.body.style.fontFamily = fontFamily
|
||||
}
|
||||
|
||||
function MainContent(): React.JSX.Element {
|
||||
const { t } = useTranslation()
|
||||
@@ -163,6 +180,7 @@ function MainContent(): React.JSX.Element {
|
||||
const settingsRes = await (window as any).api.getAllSettings()
|
||||
if (settingsRes?.success && settingsRes.data) {
|
||||
setMobileBottomNavItems(normalizeStoredBottomKeys(settingsRes.data.mobile_bottom_nav_items))
|
||||
applyGlobalFontFamily(settingsRes.data.font_family)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,8 +196,13 @@ function MainContent(): React.JSX.Element {
|
||||
|
||||
api
|
||||
.onSettingChanged((change: { key?: string; value?: unknown }) => {
|
||||
if (change?.key !== "mobile_bottom_nav_items") return
|
||||
setMobileBottomNavItems(normalizeStoredBottomKeys(change.value))
|
||||
if (change?.key === "mobile_bottom_nav_items") {
|
||||
setMobileBottomNavItems(normalizeStoredBottomKeys(change.value))
|
||||
return
|
||||
}
|
||||
if (change?.key === "font_family") {
|
||||
applyGlobalFontFamily(String(change.value || "system"))
|
||||
}
|
||||
})
|
||||
.then((fn: () => void) => {
|
||||
if (disposed) {
|
||||
|
||||
@@ -18,8 +18,8 @@ import { type ImmutableTree } from "@react-awesome-query-builder/antd"
|
||||
import type { ColumnsType } from "antd/es/table"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { fetchAllTags } from "./TagEditorDialog"
|
||||
import { ActionEditor } from "./AutoScoreManagerPage/ActionEditor"
|
||||
import { TriggerRuleBuilder } from "./AutoScoreManagerPage/TriggerRuleBuilder"
|
||||
import { ActionEditor } from "./AutoScore/ActionEditor"
|
||||
import { TriggerRuleBuilder } from "./AutoScore/TriggerRuleBuilder"
|
||||
import {
|
||||
actionDraftsToPayload,
|
||||
actionsToDrafts,
|
||||
@@ -33,7 +33,7 @@ import {
|
||||
type ActionDraft,
|
||||
type AutoScoreRule,
|
||||
type AutoScoreTagOption,
|
||||
} from "./AutoScoreManagerPage/AutoScoreUtils"
|
||||
} from "./AutoScore/AutoScoreUtils"
|
||||
|
||||
interface StudentItem {
|
||||
id: number
|
||||
|
||||
+117
-36
@@ -16,7 +16,7 @@ import {
|
||||
Typography,
|
||||
} from "antd"
|
||||
import { ThemeQuickSettings } from "./ThemeQuickSettings"
|
||||
import { OAuthLogin } from "./OAuthLogin"
|
||||
import { OAuthLogin } from "./OAuth/OAuthLogin"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { changeLanguage, getCurrentLanguage, languageOptions, AppLanguage } from "../i18n"
|
||||
import { useResponsive } from "../hooks/useResponsive"
|
||||
@@ -39,6 +39,62 @@ interface FontOption {
|
||||
fontFamily: string
|
||||
}
|
||||
|
||||
const SYSTEM_FONT_STACK =
|
||||
'"PingFang SC", "PingFangTC-Regular", "Hiragino Sans GB", "Hiragino Sans", "STHeiti", "Heiti SC", "Noto Sans CJK SC", "Noto Sans SC", "Source Han Sans SC", "Microsoft YaHei UI", "Microsoft YaHei", "微软雅黑", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", sans-serif'
|
||||
|
||||
const defaultFontOptions: FontOption[] = [
|
||||
{
|
||||
value: "system",
|
||||
label: "系统默认",
|
||||
fontFamily: SYSTEM_FONT_STACK,
|
||||
},
|
||||
{
|
||||
value: "system-Microsoft YaHei UI",
|
||||
label: "Microsoft YaHei UI",
|
||||
fontFamily: '"Microsoft YaHei UI", "Microsoft YaHei", "微软雅黑", sans-serif',
|
||||
},
|
||||
{
|
||||
value: "system-Microsoft YaHei",
|
||||
label: "Microsoft YaHei",
|
||||
fontFamily: '"Microsoft YaHei", "微软雅黑", sans-serif',
|
||||
},
|
||||
{
|
||||
value: "system-PingFang SC",
|
||||
label: "PingFang SC",
|
||||
fontFamily: '"PingFang SC", "Hiragino Sans GB", sans-serif',
|
||||
},
|
||||
{
|
||||
value: "system-Noto Sans CJK SC",
|
||||
label: "Noto Sans CJK SC",
|
||||
fontFamily: '"Noto Sans CJK SC", "Noto Sans SC", "Source Han Sans SC", sans-serif',
|
||||
},
|
||||
{
|
||||
value: "system-Segoe UI",
|
||||
label: "Segoe UI",
|
||||
fontFamily: '"Segoe UI", sans-serif',
|
||||
},
|
||||
{
|
||||
value: "system-Arial",
|
||||
label: "Arial",
|
||||
fontFamily: "Arial, sans-serif",
|
||||
},
|
||||
]
|
||||
|
||||
const mergeFontOptions = (options: FontOption[]): FontOption[] => {
|
||||
const map = new Map<string, FontOption>()
|
||||
for (const option of options) {
|
||||
if (!map.has(option.value)) {
|
||||
map.set(option.value, option)
|
||||
}
|
||||
}
|
||||
return Array.from(map.values())
|
||||
}
|
||||
|
||||
const findFontOption = (options: FontOption[], value?: string): FontOption | undefined => {
|
||||
if (!value) return options.find((item) => item.value === "system") || options[0]
|
||||
return options.find((item) => item.value === value) || options.find((item) => item.value === "system")
|
||||
}
|
||||
|
||||
const applyFontFamily = (fontFamily: string) => {
|
||||
document.documentElement.style.setProperty("--ss-font-family", fontFamily)
|
||||
document.body.style.fontFamily = fontFamily
|
||||
@@ -76,8 +132,9 @@ export const Settings: React.FC<{
|
||||
search_keyboard_layout: "qwerty26",
|
||||
disable_search_keyboard: false,
|
||||
})
|
||||
const [fontOptions, setFontOptions] = useState<FontOption[]>([])
|
||||
const [isLoadingFonts, setIsLoadingFonts] = useState(true)
|
||||
const [fontOptions, setFontOptions] = useState<FontOption[]>(defaultFontOptions)
|
||||
const [isLoadingFonts, setIsLoadingFonts] = useState(false)
|
||||
const fontOptionsRef = useRef<FontOption[]>(defaultFontOptions)
|
||||
|
||||
const [securityStatus, setSecurityStatus] = useState<{
|
||||
permission: permissionLevel
|
||||
@@ -168,6 +225,10 @@ export const Settings: React.FC<{
|
||||
)
|
||||
}, [permission, t])
|
||||
|
||||
useEffect(() => {
|
||||
fontOptionsRef.current = fontOptions
|
||||
}, [fontOptions])
|
||||
|
||||
const emitDataUpdated = (category: "events" | "students" | "reasons" | "all") => {
|
||||
window.dispatchEvent(new CustomEvent("ss:data-updated", { detail: { category } }))
|
||||
}
|
||||
@@ -187,54 +248,73 @@ export const Settings: React.FC<{
|
||||
}
|
||||
}
|
||||
|
||||
const loadSystemFonts = async () => {
|
||||
if (!(window as any).api?.getSystemFonts) return
|
||||
|
||||
const loadSystemFonts = async (selectedFontValue?: string) => {
|
||||
setIsLoadingFonts(true)
|
||||
|
||||
const applySelectedFont = (options: FontOption[]) => {
|
||||
const current = findFontOption(options, selectedFontValue || settings.font_family)
|
||||
if (current) applyFontFamily(current.fontFamily)
|
||||
}
|
||||
|
||||
const api = (window as any).api
|
||||
if (!api?.getSystemFonts) {
|
||||
setFontOptions(defaultFontOptions)
|
||||
applySelectedFont(defaultFontOptions)
|
||||
setIsLoadingFonts(false)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await (window as any).api.getSystemFonts()
|
||||
const res = await api.getSystemFonts()
|
||||
if (res.success && Array.isArray(res.data)) {
|
||||
const fontList = res.data
|
||||
.map((name: string) => ({
|
||||
value: `system-${name}`,
|
||||
label: name,
|
||||
fontFamily: `"${name}"`,
|
||||
})) as FontOption[]
|
||||
const remoteOptions = res.data
|
||||
.map((name: string) => String(name || "").trim())
|
||||
.filter((name: string) => Boolean(name))
|
||||
.map(
|
||||
(name: string) =>
|
||||
({
|
||||
value: `system-${name}`,
|
||||
label: name,
|
||||
fontFamily: `"${name}", ${SYSTEM_FONT_STACK}`,
|
||||
}) satisfies FontOption
|
||||
)
|
||||
|
||||
setFontOptions(fontList)
|
||||
|
||||
if (settings.font_family) {
|
||||
const currentFontOpt = fontList.find((f) => f.value === settings.font_family)
|
||||
if (currentFontOpt) {
|
||||
applyFontFamily(currentFontOpt.fontFamily)
|
||||
}
|
||||
}
|
||||
const mergedOptions = mergeFontOptions([...defaultFontOptions, ...remoteOptions])
|
||||
setFontOptions(mergedOptions)
|
||||
applySelectedFont(mergedOptions)
|
||||
} else {
|
||||
setFontOptions(defaultFontOptions)
|
||||
applySelectedFont(defaultFontOptions)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to load system fonts:", error)
|
||||
setFontOptions(defaultFontOptions)
|
||||
applySelectedFont(defaultFontOptions)
|
||||
} finally {
|
||||
setIsLoadingFonts(false)
|
||||
}
|
||||
}
|
||||
|
||||
const loadAll = async () => {
|
||||
if (!(window as any).api) return
|
||||
const res = await (window as any).api.getAllSettings()
|
||||
const api = (window as any).api
|
||||
if (!api) {
|
||||
setFontOptions(defaultFontOptions)
|
||||
setIsLoadingFonts(false)
|
||||
return
|
||||
}
|
||||
|
||||
let savedFontFamily = settings.font_family || "system"
|
||||
const res = await api.getAllSettings()
|
||||
if (res.success && res.data) {
|
||||
setSettings(res.data)
|
||||
setPgConnectionString(res.data.pg_connection_string || "")
|
||||
setPgConnectionStatus(res.data.pg_connection_status || { connected: true, type: "sqlite" })
|
||||
if (res.data.font_family) {
|
||||
const fontOpt = fontOptions.find((f) => f.value === res.data.font_family)
|
||||
if (fontOpt) {
|
||||
applyFontFamily(fontOpt.fontFamily)
|
||||
}
|
||||
}
|
||||
savedFontFamily = res.data.font_family || "system"
|
||||
}
|
||||
const authRes = await (window as any).api.authGetStatus()
|
||||
const authRes = await api.authGetStatus()
|
||||
if (authRes.success && authRes.data) setSecurityStatus(authRes.data)
|
||||
await loadMcpStatus()
|
||||
await loadSystemFonts()
|
||||
await loadSystemFonts(savedFontFamily)
|
||||
}
|
||||
|
||||
const loadAboutContent = async () => {
|
||||
@@ -272,11 +352,12 @@ export const Settings: React.FC<{
|
||||
if (change?.key === "auto_score_enabled")
|
||||
return { ...prev, auto_score_enabled: change.value }
|
||||
if (change?.key === "font_family") {
|
||||
const fontOpt = fontOptions.find((f) => f.value === change.value)
|
||||
const value = String(change.value || "system")
|
||||
const fontOpt = findFontOption(fontOptionsRef.current, value)
|
||||
if (fontOpt) {
|
||||
applyFontFamily(fontOpt.fontFamily)
|
||||
}
|
||||
return { ...prev, font_family: change.value }
|
||||
return { ...prev, font_family: value }
|
||||
}
|
||||
return prev
|
||||
})
|
||||
@@ -726,12 +807,12 @@ export const Settings: React.FC<{
|
||||
<Select
|
||||
value={settings.font_family || "system"}
|
||||
onChange={async (v) => {
|
||||
const fontOpt = fontOptions.find((f) => f.value === v)
|
||||
const fontOpt = findFontOption(fontOptions, String(v))
|
||||
if (!fontOpt) return
|
||||
applyFontFamily(fontOpt.fontFamily)
|
||||
setSettings((prev) => ({ ...prev, font_family: v }))
|
||||
setSettings((prev) => ({ ...prev, font_family: String(v) }))
|
||||
if ((window as any).api) {
|
||||
const res = await (window as any).api.setSetting("font_family", v)
|
||||
const res = await (window as any).api.setSetting("font_family", String(v))
|
||||
if (res.success) {
|
||||
messageApi.success(t("settings.general.saved"))
|
||||
} else {
|
||||
|
||||
@@ -48,6 +48,7 @@ export type settingsKey =
|
||||
| "window_zoom"
|
||||
| "search_keyboard_layout"
|
||||
| "disable_search_keyboard"
|
||||
| "font_family"
|
||||
| "themes_custom"
|
||||
| "auto_score_enabled"
|
||||
| "auto_score_rules"
|
||||
@@ -63,6 +64,7 @@ export interface settingsSpec {
|
||||
window_zoom: number
|
||||
search_keyboard_layout: "t9" | "qwerty26"
|
||||
disable_search_keyboard: boolean
|
||||
font_family: string
|
||||
themes_custom: themeConfig[]
|
||||
auto_score_enabled: boolean
|
||||
auto_score_rules: autoScoreRule[]
|
||||
@@ -311,6 +313,8 @@ const api = {
|
||||
key: K,
|
||||
value: settingsSpec[K]
|
||||
): Promise<{ success: boolean }> => invoke("settings_set", { key, value }),
|
||||
getSystemFonts: (): Promise<{ success: boolean; data: string[]; message?: string }> =>
|
||||
invoke("settings_get_system_fonts"),
|
||||
onSettingChanged: (callback: (change: settingChange) => void): Promise<UnlistenFn> => {
|
||||
return listen<settingChange>("settings:changed", (event) => {
|
||||
callback(event.payload)
|
||||
|
||||
Reference in New Issue
Block a user