add: 增加任务完成的提示音
This commit is contained in:
BIN
public/sounds/notification.wav
Normal file
BIN
public/sounds/notification.wav
Normal file
Binary file not shown.
@@ -6,7 +6,7 @@ use crate::app_error::AppCommandError;
|
|||||||
use crate::db::service::app_metadata_service;
|
use crate::db::service::app_metadata_service;
|
||||||
#[cfg(feature = "tauri-runtime")]
|
#[cfg(feature = "tauri-runtime")]
|
||||||
use crate::db::AppDatabase;
|
use crate::db::AppDatabase;
|
||||||
use crate::models::{SystemLanguageSettings, SystemProxySettings};
|
use crate::models::{SystemLanguageSettings, SystemNotificationSettings, SystemProxySettings};
|
||||||
#[cfg(feature = "tauri-runtime")]
|
#[cfg(feature = "tauri-runtime")]
|
||||||
use crate::models::SystemRenderingSettings;
|
use crate::models::SystemRenderingSettings;
|
||||||
#[cfg(feature = "tauri-runtime")]
|
#[cfg(feature = "tauri-runtime")]
|
||||||
@@ -16,6 +16,7 @@ use crate::network::proxy;
|
|||||||
|
|
||||||
const SYSTEM_PROXY_SETTINGS_KEY: &str = "system_proxy_settings";
|
const SYSTEM_PROXY_SETTINGS_KEY: &str = "system_proxy_settings";
|
||||||
const SYSTEM_LANGUAGE_SETTINGS_KEY: &str = "system_language_settings";
|
const SYSTEM_LANGUAGE_SETTINGS_KEY: &str = "system_language_settings";
|
||||||
|
const SYSTEM_NOTIFICATION_SETTINGS_KEY: &str = "system_notification_settings";
|
||||||
#[cfg(feature = "tauri-runtime")]
|
#[cfg(feature = "tauri-runtime")]
|
||||||
const LANGUAGE_SETTINGS_UPDATED_EVENT: &str = "app://language-settings-updated";
|
const LANGUAGE_SETTINGS_UPDATED_EVENT: &str = "app://language-settings-updated";
|
||||||
|
|
||||||
@@ -174,3 +175,46 @@ pub async fn update_system_rendering_settings(
|
|||||||
})?;
|
})?;
|
||||||
Ok(settings)
|
Ok(settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn load_system_notification_settings(
|
||||||
|
conn: &DatabaseConnection,
|
||||||
|
) -> Result<SystemNotificationSettings, AppCommandError> {
|
||||||
|
let raw = app_metadata_service::get_value(conn, SYSTEM_NOTIFICATION_SETTINGS_KEY)
|
||||||
|
.await
|
||||||
|
.map_err(AppCommandError::from)?;
|
||||||
|
|
||||||
|
let Some(raw) = raw else {
|
||||||
|
return Ok(SystemNotificationSettings::new_enabled());
|
||||||
|
};
|
||||||
|
|
||||||
|
serde_json::from_str::<SystemNotificationSettings>(&raw).map_err(|e| {
|
||||||
|
AppCommandError::configuration_invalid("Failed to parse stored notification settings")
|
||||||
|
.with_detail(e.to_string())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "tauri-runtime")]
|
||||||
|
#[cfg_attr(feature = "tauri-runtime", tauri::command)]
|
||||||
|
pub async fn get_system_notification_settings(
|
||||||
|
db: State<'_, AppDatabase>,
|
||||||
|
) -> Result<SystemNotificationSettings, AppCommandError> {
|
||||||
|
load_system_notification_settings(&db.conn).await
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "tauri-runtime")]
|
||||||
|
#[cfg_attr(feature = "tauri-runtime", tauri::command)]
|
||||||
|
pub async fn update_system_notification_settings(
|
||||||
|
settings: SystemNotificationSettings,
|
||||||
|
db: State<'_, AppDatabase>,
|
||||||
|
) -> Result<SystemNotificationSettings, AppCommandError> {
|
||||||
|
let serialized = serde_json::to_string(&settings).map_err(|e| {
|
||||||
|
AppCommandError::invalid_input("Failed to serialize notification settings")
|
||||||
|
.with_detail(e.to_string())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
app_metadata_service::upsert_value(&db.conn, SYSTEM_NOTIFICATION_SETTINGS_KEY, &serialized)
|
||||||
|
.await
|
||||||
|
.map_err(AppCommandError::from)?;
|
||||||
|
|
||||||
|
Ok(settings)
|
||||||
|
}
|
||||||
|
|||||||
@@ -312,6 +312,8 @@ mod tauri_app {
|
|||||||
system_settings::update_system_language_settings,
|
system_settings::update_system_language_settings,
|
||||||
system_settings::get_system_rendering_settings,
|
system_settings::get_system_rendering_settings,
|
||||||
system_settings::update_system_rendering_settings,
|
system_settings::update_system_rendering_settings,
|
||||||
|
system_settings::get_system_notification_settings,
|
||||||
|
system_settings::update_system_notification_settings,
|
||||||
version_control::detect_git,
|
version_control::detect_git,
|
||||||
version_control::test_git_path,
|
version_control::test_git_path,
|
||||||
version_control::get_git_settings,
|
version_control::get_git_settings,
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ pub use message::{
|
|||||||
};
|
};
|
||||||
pub use system::{
|
pub use system::{
|
||||||
GitCredentials, GitDetectResult, GitHubAccountsSettings, GitHubTokenValidation, GitSettings,
|
GitCredentials, GitDetectResult, GitHubAccountsSettings, GitHubTokenValidation, GitSettings,
|
||||||
SystemLanguageSettings, SystemProxySettings,
|
SystemLanguageSettings, SystemNotificationSettings, SystemProxySettings,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "tauri-runtime")]
|
#[cfg(feature = "tauri-runtime")]
|
||||||
pub use system::SystemRenderingSettings;
|
pub use system::SystemRenderingSettings;
|
||||||
|
|||||||
@@ -44,6 +44,22 @@ pub struct SystemRenderingSettings {
|
|||||||
pub disable_hardware_acceleration: bool,
|
pub disable_hardware_acceleration: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||||
|
#[serde(default)]
|
||||||
|
pub struct SystemNotificationSettings {
|
||||||
|
pub task_completion: bool,
|
||||||
|
pub sound_enabled: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SystemNotificationSettings {
|
||||||
|
pub fn new_enabled() -> Self {
|
||||||
|
Self {
|
||||||
|
task_completion: true,
|
||||||
|
sound_enabled: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// --- Version Control ---
|
// --- Version Control ---
|
||||||
|
|
||||||
/// Explicit credentials for a single git remote operation.
|
/// Explicit credentials for a single git remote operation.
|
||||||
|
|||||||
@@ -3,11 +3,13 @@
|
|||||||
import { useCallback, useEffect, useMemo, useState } from "react"
|
import { useCallback, useEffect, useMemo, useState } from "react"
|
||||||
import {
|
import {
|
||||||
ArrowUpCircle,
|
ArrowUpCircle,
|
||||||
|
Bell,
|
||||||
CheckCircle2,
|
CheckCircle2,
|
||||||
Languages,
|
Languages,
|
||||||
Loader2,
|
Loader2,
|
||||||
MonitorCog,
|
MonitorCog,
|
||||||
RefreshCw,
|
RefreshCw,
|
||||||
|
Volume2,
|
||||||
Wifi,
|
Wifi,
|
||||||
} from "lucide-react"
|
} from "lucide-react"
|
||||||
import { Github } from "@lobehub/icons"
|
import { Github } from "@lobehub/icons"
|
||||||
@@ -31,9 +33,11 @@ import {
|
|||||||
import {
|
import {
|
||||||
getSystemProxySettings,
|
getSystemProxySettings,
|
||||||
getSystemRenderingSettings,
|
getSystemRenderingSettings,
|
||||||
|
getSystemNotificationSettings,
|
||||||
updateSystemLanguageSettings,
|
updateSystemLanguageSettings,
|
||||||
updateSystemProxySettings,
|
updateSystemProxySettings,
|
||||||
updateSystemRenderingSettings,
|
updateSystemRenderingSettings,
|
||||||
|
updateSystemNotificationSettings,
|
||||||
} from "@/lib/api"
|
} from "@/lib/api"
|
||||||
import { isDesktop, openUrl } from "@/lib/platform"
|
import { isDesktop, openUrl } from "@/lib/platform"
|
||||||
import type { AppLocale } from "@/lib/types"
|
import type { AppLocale } from "@/lib/types"
|
||||||
@@ -96,6 +100,9 @@ export function SystemNetworkSettings() {
|
|||||||
)
|
)
|
||||||
const renderingDirty =
|
const renderingDirty =
|
||||||
processStartLoaded && persistedDisableHwAccel !== processStartDisableHwAccel
|
processStartLoaded && persistedDisableHwAccel !== processStartDisableHwAccel
|
||||||
|
const [notifTaskCompletion, setNotifTaskCompletion] = useState(true)
|
||||||
|
const [notifSoundEnabled, setNotifSoundEnabled] = useState(true)
|
||||||
|
const [savingNotification, setSavingNotification] = useState(false)
|
||||||
const [currentVersion, setCurrentVersion] = useState<string>("")
|
const [currentVersion, setCurrentVersion] = useState<string>("")
|
||||||
const [availableUpdate, setAvailableUpdate] = useState<Update | null>(null)
|
const [availableUpdate, setAvailableUpdate] = useState<Update | null>(null)
|
||||||
const [checkingUpdate, setCheckingUpdate] = useState(false)
|
const [checkingUpdate, setCheckingUpdate] = useState(false)
|
||||||
@@ -171,13 +178,15 @@ export function SystemNetworkSettings() {
|
|||||||
setLoadError(null)
|
setLoadError(null)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const [proxySettings, version, renderingSettings] = await Promise.all([
|
const [proxySettings, version, renderingSettings, notificationSettings] =
|
||||||
getSystemProxySettings(),
|
await Promise.all([
|
||||||
getCurrentAppVersion(),
|
getSystemProxySettings(),
|
||||||
renderingSettingsLoadable
|
getCurrentAppVersion(),
|
||||||
? getSystemRenderingSettings()
|
renderingSettingsLoadable
|
||||||
: Promise.resolve(null),
|
? getSystemRenderingSettings()
|
||||||
])
|
: Promise.resolve(null),
|
||||||
|
getSystemNotificationSettings(),
|
||||||
|
])
|
||||||
|
|
||||||
setEnabled(proxySettings.enabled)
|
setEnabled(proxySettings.enabled)
|
||||||
setProxyUrl(proxySettings.proxy_url ?? "")
|
setProxyUrl(proxySettings.proxy_url ?? "")
|
||||||
@@ -191,6 +200,10 @@ export function SystemNetworkSettings() {
|
|||||||
setProcessStartLoaded(true)
|
setProcessStartLoaded(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (notificationSettings) {
|
||||||
|
setNotifTaskCompletion(notificationSettings.task_completion)
|
||||||
|
setNotifSoundEnabled(notificationSettings.sound_enabled)
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const message = err instanceof Error ? err.message : String(err)
|
const message = err instanceof Error ? err.message : String(err)
|
||||||
setLoadError(message)
|
setLoadError(message)
|
||||||
@@ -291,6 +304,26 @@ export function SystemNetworkSettings() {
|
|||||||
[languageSettings.language, setLanguageSettings, t]
|
[languageSettings.language, setLanguageSettings, t]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const saveNotificationSettings = useCallback(
|
||||||
|
async (taskCompletion: boolean, soundEnabled: boolean) => {
|
||||||
|
setSavingNotification(true)
|
||||||
|
try {
|
||||||
|
const result = await updateSystemNotificationSettings({
|
||||||
|
task_completion: taskCompletion,
|
||||||
|
sound_enabled: soundEnabled,
|
||||||
|
})
|
||||||
|
setNotifTaskCompletion(result.task_completion)
|
||||||
|
setNotifSoundEnabled(result.sound_enabled)
|
||||||
|
} catch (err) {
|
||||||
|
const message = err instanceof Error ? err.message : String(err)
|
||||||
|
toast.error(t("notificationSaveFailed", { message }))
|
||||||
|
} finally {
|
||||||
|
setSavingNotification(false)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[t]
|
||||||
|
)
|
||||||
|
|
||||||
const formatUpdateError = useCallback(
|
const formatUpdateError = useCallback(
|
||||||
(error: unknown, action: UpdateAction): string => {
|
(error: unknown, action: UpdateAction): string => {
|
||||||
const { kind, rawMessage } = normalizeAppUpdateError(error)
|
const { kind, rawMessage } = normalizeAppUpdateError(error)
|
||||||
@@ -716,6 +749,46 @@ export function SystemNetworkSettings() {
|
|||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section className="rounded-xl border bg-card p-4 space-y-4">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Bell className="h-4 w-4 text-muted-foreground" />
|
||||||
|
<h2 className="text-sm font-semibold">{t("notificationTitle")}</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p className="text-xs text-muted-foreground leading-5">
|
||||||
|
{t("notificationDescription")}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<label className="inline-flex items-center gap-2 text-sm">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={notifTaskCompletion}
|
||||||
|
disabled={savingNotification}
|
||||||
|
onChange={(event) => {
|
||||||
|
const next = event.target.checked
|
||||||
|
setNotifTaskCompletion(next)
|
||||||
|
saveNotificationSettings(next, notifSoundEnabled)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{t("notificationTaskCompletion")}
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label className="inline-flex items-center gap-2 text-sm">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={notifSoundEnabled}
|
||||||
|
disabled={savingNotification || !notifTaskCompletion}
|
||||||
|
onChange={(event) => {
|
||||||
|
const next = event.target.checked
|
||||||
|
setNotifSoundEnabled(next)
|
||||||
|
saveNotificationSettings(notifTaskCompletion, next)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Volume2 className="h-3.5 w-3.5 text-muted-foreground" />
|
||||||
|
{t("notificationSoundEnabled")}
|
||||||
|
</label>
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import {
|
|||||||
acpCancel,
|
acpCancel,
|
||||||
acpRespondPermission,
|
acpRespondPermission,
|
||||||
acpDisconnect,
|
acpDisconnect,
|
||||||
|
getSystemNotificationSettings,
|
||||||
} from "@/lib/api"
|
} from "@/lib/api"
|
||||||
import type {
|
import type {
|
||||||
AgentType,
|
AgentType,
|
||||||
@@ -1364,6 +1365,19 @@ export function AcpConnectionsProvider({ children }: { children: ReactNode }) {
|
|||||||
pushAlertRef.current = pushAlert
|
pushAlertRef.current = pushAlert
|
||||||
}, [pushAlert])
|
}, [pushAlert])
|
||||||
|
|
||||||
|
// Notification settings (loaded once, refreshed lazily)
|
||||||
|
const notificationSettingsRef = useRef({
|
||||||
|
task_completion: true,
|
||||||
|
sound_enabled: true,
|
||||||
|
})
|
||||||
|
useEffect(() => {
|
||||||
|
getSystemNotificationSettings()
|
||||||
|
.then((s) => {
|
||||||
|
notificationSettingsRef.current = s
|
||||||
|
})
|
||||||
|
.catch(() => {})
|
||||||
|
}, [])
|
||||||
|
|
||||||
// Ref-based store — mutations don't trigger React state updates
|
// Ref-based store — mutations don't trigger React state updates
|
||||||
const storeRef = useRef<InternalStore>({
|
const storeRef = useRef<InternalStore>({
|
||||||
connections: new Map(),
|
connections: new Map(),
|
||||||
@@ -1948,14 +1962,16 @@ export function AcpConnectionsProvider({ children }: { children: ReactNode }) {
|
|||||||
}
|
}
|
||||||
// Send OS notification when window is not focused
|
// Send OS notification when window is not focused
|
||||||
{
|
{
|
||||||
|
const ns = notificationSettingsRef.current
|
||||||
const nc = storeRef.current.connections.get(contextKey)
|
const nc = storeRef.current.connections.get(contextKey)
|
||||||
if (nc) {
|
if (nc && ns.task_completion) {
|
||||||
const agentLabel = AGENT_LABELS[nc.agentType]
|
const agentLabel = AGENT_LABELS[nc.agentType]
|
||||||
const fn = folderNameRef.current
|
const fn = folderNameRef.current
|
||||||
const title = fn ? `${fn} - Codeg` : "Codeg"
|
const title = fn ? `${fn} - Codeg` : "Codeg"
|
||||||
sendSystemNotification(
|
sendSystemNotification(
|
||||||
title,
|
title,
|
||||||
t("notificationTurnComplete", { agent: agentLabel })
|
t("notificationTurnComplete", { agent: agentLabel }),
|
||||||
|
{ sound: ns.sound_enabled }
|
||||||
).catch(() => {})
|
).catch(() => {})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -145,7 +145,12 @@
|
|||||||
"downloadFailed": "فشل تنزيل حزمة التحديث. يرجى المحاولة مرة أخرى لاحقًا.",
|
"downloadFailed": "فشل تنزيل حزمة التحديث. يرجى المحاولة مرة أخرى لاحقًا.",
|
||||||
"installFailed": "فشل تثبيت التحديث. يرجى إغلاق التطبيق ثم إعادة المحاولة.",
|
"installFailed": "فشل تثبيت التحديث. يرجى إغلاق التطبيق ثم إعادة المحاولة.",
|
||||||
"unknown": "فشل التحديث. يرجى المحاولة مرة أخرى لاحقًا."
|
"unknown": "فشل التحديث. يرجى المحاولة مرة أخرى لاحقًا."
|
||||||
}
|
},
|
||||||
|
"notificationTitle": "الإشعارات",
|
||||||
|
"notificationDescription": "تكوين إشعارات إتمام المهام وتنبيهات الصوت.",
|
||||||
|
"notificationTaskCompletion": "إشعار عند إتمام المهمة",
|
||||||
|
"notificationSoundEnabled": "تشغيل صوت الإشعار",
|
||||||
|
"notificationSaveFailed": "فشل حفظ إعدادات الإشعارات: {message}"
|
||||||
},
|
},
|
||||||
"VersionControlSettings": {
|
"VersionControlSettings": {
|
||||||
"loading": "جارٍ التحميل...",
|
"loading": "جارٍ التحميل...",
|
||||||
|
|||||||
@@ -145,7 +145,12 @@
|
|||||||
"downloadFailed": "Das Update-Paket konnte nicht heruntergeladen werden. Bitte später erneut versuchen.",
|
"downloadFailed": "Das Update-Paket konnte nicht heruntergeladen werden. Bitte später erneut versuchen.",
|
||||||
"installFailed": "Das Update konnte nicht installiert werden. Bitte App schließen und erneut versuchen.",
|
"installFailed": "Das Update konnte nicht installiert werden. Bitte App schließen und erneut versuchen.",
|
||||||
"unknown": "Update fehlgeschlagen. Bitte später erneut versuchen."
|
"unknown": "Update fehlgeschlagen. Bitte später erneut versuchen."
|
||||||
}
|
},
|
||||||
|
"notificationTitle": "Benachrichtigungen",
|
||||||
|
"notificationDescription": "Benachrichtigungen bei Aufgabenerledigung und Soundalarme konfigurieren.",
|
||||||
|
"notificationTaskCompletion": "Bei Aufgabenerledigung benachrichtigen",
|
||||||
|
"notificationSoundEnabled": "Benachrichtigungston abspielen",
|
||||||
|
"notificationSaveFailed": "Fehler beim Speichern der Benachrichtigungseinstellungen: {message}"
|
||||||
},
|
},
|
||||||
"VersionControlSettings": {
|
"VersionControlSettings": {
|
||||||
"loading": "Laden...",
|
"loading": "Laden...",
|
||||||
|
|||||||
@@ -145,7 +145,12 @@
|
|||||||
"downloadFailed": "Failed to download update package. Please try again later.",
|
"downloadFailed": "Failed to download update package. Please try again later.",
|
||||||
"installFailed": "Failed to install update. Please close the app and try again.",
|
"installFailed": "Failed to install update. Please close the app and try again.",
|
||||||
"unknown": "Update failed. Please try again later."
|
"unknown": "Update failed. Please try again later."
|
||||||
}
|
},
|
||||||
|
"notificationTitle": "Notifications",
|
||||||
|
"notificationDescription": "Configure task completion notifications and sound alerts.",
|
||||||
|
"notificationTaskCompletion": "Notify on task completion",
|
||||||
|
"notificationSoundEnabled": "Play notification sound",
|
||||||
|
"notificationSaveFailed": "Failed to save notification settings: {message}"
|
||||||
},
|
},
|
||||||
"VersionControlSettings": {
|
"VersionControlSettings": {
|
||||||
"loading": "Loading...",
|
"loading": "Loading...",
|
||||||
|
|||||||
@@ -145,7 +145,12 @@
|
|||||||
"downloadFailed": "No se pudo descargar el paquete de actualización. Inténtalo más tarde.",
|
"downloadFailed": "No se pudo descargar el paquete de actualización. Inténtalo más tarde.",
|
||||||
"installFailed": "No se pudo instalar la actualización. Cierra la app e inténtalo de nuevo.",
|
"installFailed": "No se pudo instalar la actualización. Cierra la app e inténtalo de nuevo.",
|
||||||
"unknown": "La actualización falló. Inténtalo más tarde."
|
"unknown": "La actualización falló. Inténtalo más tarde."
|
||||||
}
|
},
|
||||||
|
"notificationTitle": "Notificaciones",
|
||||||
|
"notificationDescription": "Configurar notificaciones de finalización de tareas y alertas de sonido.",
|
||||||
|
"notificationTaskCompletion": "Notificar al completar tarea",
|
||||||
|
"notificationSoundEnabled": "Reproducir sonido de notificación",
|
||||||
|
"notificationSaveFailed": "Error al guardar configuración de notificaciones: {message}"
|
||||||
},
|
},
|
||||||
"VersionControlSettings": {
|
"VersionControlSettings": {
|
||||||
"loading": "Cargando...",
|
"loading": "Cargando...",
|
||||||
|
|||||||
@@ -145,7 +145,12 @@
|
|||||||
"downloadFailed": "Impossible de télécharger le paquet de mise à jour. Veuillez réessayer plus tard.",
|
"downloadFailed": "Impossible de télécharger le paquet de mise à jour. Veuillez réessayer plus tard.",
|
||||||
"installFailed": "Impossible d’installer la mise à jour. Fermez l’app puis réessayez.",
|
"installFailed": "Impossible d’installer la mise à jour. Fermez l’app puis réessayez.",
|
||||||
"unknown": "La mise à jour a échoué. Veuillez réessayer plus tard."
|
"unknown": "La mise à jour a échoué. Veuillez réessayer plus tard."
|
||||||
}
|
},
|
||||||
|
"notificationTitle": "Notifications",
|
||||||
|
"notificationDescription": "Configurer les notifications de fin de tâche et les alertes sonores.",
|
||||||
|
"notificationTaskCompletion": "Notifier à la fin de la tâche",
|
||||||
|
"notificationSoundEnabled": "Jouer un son de notification",
|
||||||
|
"notificationSaveFailed": "Échec de l'enregistrement des paramètres de notification : {message}"
|
||||||
},
|
},
|
||||||
"VersionControlSettings": {
|
"VersionControlSettings": {
|
||||||
"loading": "Chargement...",
|
"loading": "Chargement...",
|
||||||
|
|||||||
@@ -145,7 +145,12 @@
|
|||||||
"downloadFailed": "更新パッケージのダウンロードに失敗しました。しばらくしてから再試行してください。",
|
"downloadFailed": "更新パッケージのダウンロードに失敗しました。しばらくしてから再試行してください。",
|
||||||
"installFailed": "更新のインストールに失敗しました。アプリを閉じて再試行してください。",
|
"installFailed": "更新のインストールに失敗しました。アプリを閉じて再試行してください。",
|
||||||
"unknown": "更新に失敗しました。しばらくしてから再試行してください。"
|
"unknown": "更新に失敗しました。しばらくしてから再試行してください。"
|
||||||
}
|
},
|
||||||
|
"notificationTitle": "通知",
|
||||||
|
"notificationDescription": "タスク完了通知とサウンドアラートを設定します。",
|
||||||
|
"notificationTaskCompletion": "タスク完了時に通知する",
|
||||||
|
"notificationSoundEnabled": "通知音を再生する",
|
||||||
|
"notificationSaveFailed": "通知設定の保存に失敗しました:{message}"
|
||||||
},
|
},
|
||||||
"VersionControlSettings": {
|
"VersionControlSettings": {
|
||||||
"loading": "読み込み中...",
|
"loading": "読み込み中...",
|
||||||
|
|||||||
@@ -145,7 +145,12 @@
|
|||||||
"downloadFailed": "업데이트 패키지 다운로드에 실패했습니다. 잠시 후 다시 시도하세요.",
|
"downloadFailed": "업데이트 패키지 다운로드에 실패했습니다. 잠시 후 다시 시도하세요.",
|
||||||
"installFailed": "업데이트 설치에 실패했습니다. 앱을 종료한 뒤 다시 시도하세요.",
|
"installFailed": "업데이트 설치에 실패했습니다. 앱을 종료한 뒤 다시 시도하세요.",
|
||||||
"unknown": "업데이트에 실패했습니다. 잠시 후 다시 시도하세요."
|
"unknown": "업데이트에 실패했습니다. 잠시 후 다시 시도하세요."
|
||||||
}
|
},
|
||||||
|
"notificationTitle": "알림",
|
||||||
|
"notificationDescription": "작업 완료 알림 및 소리 알림을 설정합니다.",
|
||||||
|
"notificationTaskCompletion": "작업 완료 시 알림",
|
||||||
|
"notificationSoundEnabled": "알림 소리 재생",
|
||||||
|
"notificationSaveFailed": "알림 설정 저장 실패: {message}"
|
||||||
},
|
},
|
||||||
"VersionControlSettings": {
|
"VersionControlSettings": {
|
||||||
"loading": "로딩 중...",
|
"loading": "로딩 중...",
|
||||||
|
|||||||
@@ -145,7 +145,12 @@
|
|||||||
"downloadFailed": "Falha ao baixar o pacote de atualização. Tente novamente mais tarde.",
|
"downloadFailed": "Falha ao baixar o pacote de atualização. Tente novamente mais tarde.",
|
||||||
"installFailed": "Falha ao instalar a atualização. Feche o app e tente novamente.",
|
"installFailed": "Falha ao instalar a atualização. Feche o app e tente novamente.",
|
||||||
"unknown": "Falha na atualização. Tente novamente mais tarde."
|
"unknown": "Falha na atualização. Tente novamente mais tarde."
|
||||||
}
|
},
|
||||||
|
"notificationTitle": "Notificações",
|
||||||
|
"notificationDescription": "Configurar notificações de conclusão de tarefas e alertas sonoros.",
|
||||||
|
"notificationTaskCompletion": "Notificar ao concluir tarefa",
|
||||||
|
"notificationSoundEnabled": "Reproduzir som de notificação",
|
||||||
|
"notificationSaveFailed": "Falha ao salvar configurações de notificação: {message}"
|
||||||
},
|
},
|
||||||
"VersionControlSettings": {
|
"VersionControlSettings": {
|
||||||
"loading": "Carregando...",
|
"loading": "Carregando...",
|
||||||
|
|||||||
@@ -145,7 +145,12 @@
|
|||||||
"downloadFailed": "下载更新包失败,请稍后重试。",
|
"downloadFailed": "下载更新包失败,请稍后重试。",
|
||||||
"installFailed": "安装更新失败,请关闭应用后重试。",
|
"installFailed": "安装更新失败,请关闭应用后重试。",
|
||||||
"unknown": "更新失败,请稍后重试。"
|
"unknown": "更新失败,请稍后重试。"
|
||||||
}
|
},
|
||||||
|
"notificationTitle": "通知",
|
||||||
|
"notificationDescription": "配置任务完成通知和声音提醒。",
|
||||||
|
"notificationTaskCompletion": "任务完成时通知",
|
||||||
|
"notificationSoundEnabled": "播放通知声音",
|
||||||
|
"notificationSaveFailed": "保存通知设置失败:{message}"
|
||||||
},
|
},
|
||||||
"VersionControlSettings": {
|
"VersionControlSettings": {
|
||||||
"loading": "加载中...",
|
"loading": "加载中...",
|
||||||
|
|||||||
@@ -145,7 +145,12 @@
|
|||||||
"downloadFailed": "下載更新包失敗,請稍後再試。",
|
"downloadFailed": "下載更新包失敗,請稍後再試。",
|
||||||
"installFailed": "安裝更新失敗,請關閉應用後重試。",
|
"installFailed": "安裝更新失敗,請關閉應用後重試。",
|
||||||
"unknown": "更新失敗,請稍後再試。"
|
"unknown": "更新失敗,請稍後再試。"
|
||||||
}
|
},
|
||||||
|
"notificationTitle": "通知",
|
||||||
|
"notificationDescription": "設定任務完成通知和聲音提醒。",
|
||||||
|
"notificationTaskCompletion": "任務完成時通知",
|
||||||
|
"notificationSoundEnabled": "播放通知聲音",
|
||||||
|
"notificationSaveFailed": "儲存通知設定失敗:{message}"
|
||||||
},
|
},
|
||||||
"VersionControlSettings": {
|
"VersionControlSettings": {
|
||||||
"loading": "載入中...",
|
"loading": "載入中...",
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ import type {
|
|||||||
SystemLanguageSettings,
|
SystemLanguageSettings,
|
||||||
SystemProxySettings,
|
SystemProxySettings,
|
||||||
SystemRenderingSettings,
|
SystemRenderingSettings,
|
||||||
|
SystemNotificationSettings,
|
||||||
GitCredentials,
|
GitCredentials,
|
||||||
GitDetectResult,
|
GitDetectResult,
|
||||||
PackageManagerInfo,
|
PackageManagerInfo,
|
||||||
@@ -469,6 +470,16 @@ export async function updateSystemRenderingSettings(
|
|||||||
return getTransport().call("update_system_rendering_settings", { settings })
|
return getTransport().call("update_system_rendering_settings", { settings })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getSystemNotificationSettings(): Promise<SystemNotificationSettings> {
|
||||||
|
return getTransport().call("get_system_notification_settings")
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateSystemNotificationSettings(
|
||||||
|
settings: SystemNotificationSettings
|
||||||
|
): Promise<SystemNotificationSettings> {
|
||||||
|
return getTransport().call("update_system_notification_settings", { settings })
|
||||||
|
}
|
||||||
|
|
||||||
// --- Version Control ---
|
// --- Version Control ---
|
||||||
|
|
||||||
export async function detectGit(): Promise<GitDetectResult> {
|
export async function detectGit(): Promise<GitDetectResult> {
|
||||||
|
|||||||
@@ -1,9 +1,31 @@
|
|||||||
import { getTransport } from "./transport"
|
import { getTransport } from "./transport"
|
||||||
import { isDesktop } from "./transport"
|
import { isDesktop } from "./transport"
|
||||||
|
|
||||||
|
let cachedAudio: HTMLAudioElement | null = null
|
||||||
|
|
||||||
|
function getNotificationAudio(): HTMLAudioElement {
|
||||||
|
if (!cachedAudio) {
|
||||||
|
cachedAudio = new Audio("/sounds/notification.wav")
|
||||||
|
}
|
||||||
|
return cachedAudio
|
||||||
|
}
|
||||||
|
|
||||||
|
export function playNotificationSound(): void {
|
||||||
|
try {
|
||||||
|
const audio = getNotificationAudio()
|
||||||
|
audio.currentTime = 0
|
||||||
|
audio.play().catch(() => {
|
||||||
|
// Autoplay may be blocked; silently ignore
|
||||||
|
})
|
||||||
|
} catch {
|
||||||
|
// Ignore audio playback errors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function sendSystemNotification(
|
export async function sendSystemNotification(
|
||||||
title: string,
|
title: string,
|
||||||
body: string
|
body: string,
|
||||||
|
options?: { sound?: boolean }
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (!document.hidden) return
|
if (!document.hidden) return
|
||||||
if (isDesktop()) {
|
if (isDesktop()) {
|
||||||
@@ -19,4 +41,8 @@ export async function sendSystemNotification(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options?.sound !== false) {
|
||||||
|
playNotificationSound()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -638,6 +638,11 @@ export interface SystemRenderingSettings {
|
|||||||
disable_hardware_acceleration: boolean
|
disable_hardware_acceleration: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SystemNotificationSettings {
|
||||||
|
task_completion: boolean
|
||||||
|
sound_enabled: boolean
|
||||||
|
}
|
||||||
|
|
||||||
// --- Version Control ---
|
// --- Version Control ---
|
||||||
|
|
||||||
export interface GitCredentials {
|
export interface GitCredentials {
|
||||||
|
|||||||
Reference in New Issue
Block a user