Merge pull request #217 from appergb/feat/autostart-194

feat(autostart): macOS / Linux / Windows 三平台开机自启
This commit is contained in:
Chris233 authored and GitHub committed 2026-05-04 08:09:13 +08:00
commit bdd4cb1c7d
9 files changed
+163 -10

No files matched your search

+12 -2
View File
@@ -1,14 +1,15 @@
{
"name": "openless-app",
"version": "1.2.8",
"version": "1.2.13",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "openless-app",
"version": "1.2.8",
"version": "1.2.13",
"dependencies": {
"@tauri-apps/api": "^2.1.1",
"@tauri-apps/plugin-autostart": "^2.5.1",
"@tauri-apps/plugin-shell": "^2.0.1",
"@tauri-apps/plugin-updater": "^2.10.1",
"i18next": "^26.0.8",
@@ -1342,6 +1343,15 @@
"node": ">= 10"
}
},
"node_modules/@tauri-apps/plugin-autostart": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-autostart/-/plugin-autostart-2.5.1.tgz",
"integrity": "sha512-zS/xx7yzveCcotkA+8TqkI2lysmG2wvQXv2HGAVExITmnFfHAdj1arGsbbfs3o6EktRHf6l34pJxc3YGG2mg7w==",
"license": "MIT OR Apache-2.0",
"dependencies": {
"@tauri-apps/api": "^2.8.0"
}
},
"node_modules/@tauri-apps/plugin-shell": {
"version": "2.3.5",
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-shell/-/plugin-shell-2.3.5.tgz",
+1
View File
@@ -12,6 +12,7 @@
},
"dependencies": {
"@tauri-apps/api": "^2.1.1",
"@tauri-apps/plugin-autostart": "^2.5.1",
"@tauri-apps/plugin-shell": "^2.0.1",
"@tauri-apps/plugin-updater": "^2.10.1",
"i18next": "^26.0.8",
+73 -7
View File
@@ -308,6 +308,17 @@ version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]]
name = "auto-launch"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f012b8cc0c850f34117ec8252a44418f2e34a2cf501de89e29b241ae5f79471"
dependencies = [
"dirs 4.0.0",
"thiserror 1.0.69",
"winreg 0.10.1",
]
[[package]]
name = "autocfg"
version = "1.5.0"
@@ -1090,13 +1101,33 @@ dependencies = [
"crypto-common",
]
[[package]]
name = "dirs"
version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059"
dependencies = [
"dirs-sys 0.3.7",
]
[[package]]
name = "dirs"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
dependencies = [
"dirs-sys",
"dirs-sys 0.5.0",
]
[[package]]
name = "dirs-sys"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
dependencies = [
"libc",
"redox_users 0.4.6",
"winapi",
]
[[package]]
@@ -1107,7 +1138,7 @@ checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
dependencies = [
"libc",
"option-ext",
"redox_users",
"redox_users 0.5.2",
"windows-sys 0.61.2",
]
@@ -3278,6 +3309,7 @@ dependencies = [
"simplelog",
"tauri",
"tauri-build",
"tauri-plugin-autostart",
"tauri-plugin-shell",
"tauri-plugin-single-instance",
"tauri-plugin-updater",
@@ -4042,6 +4074,17 @@ dependencies = [
"bitflags 2.11.1",
]
[[package]]
name = "redox_users"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
dependencies = [
"getrandom 0.2.17",
"libredox",
"thiserror 1.0.69",
]
[[package]]
name = "redox_users"
version = "0.5.2"
@@ -5026,7 +5069,7 @@ dependencies = [
"anyhow",
"bytes",
"cookie",
"dirs",
"dirs 6.0.0",
"dunce",
"embed_plist",
"getrandom 0.3.4",
@@ -5076,7 +5119,7 @@ checksum = "4bbc990d1dbf57a8e1c7fa2327f2a614d8b757805603c1b9ba5c81bade09fd4d"
dependencies = [
"anyhow",
"cargo_toml",
"dirs",
"dirs 6.0.0",
"glob",
"heck 0.5.0",
"json-patch",
@@ -5148,6 +5191,20 @@ dependencies = [
"walkdir",
]
[[package]]
name = "tauri-plugin-autostart"
version = "2.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "459383cebc193cdd03d1ba4acc40f2c408a7abce419d64bdcd2d745bc2886f70"
dependencies = [
"auto-launch",
"serde",
"serde_json",
"tauri",
"tauri-plugin",
"thiserror 2.0.18",
]
[[package]]
name = "tauri-plugin-shell"
version = "2.3.5"
@@ -5191,7 +5248,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "806d9dac662c2e4594ff03c647a552f2c9bd544e7d0f683ec58f872f952ce4af"
dependencies = [
"base64 0.22.1",
"dirs",
"dirs 6.0.0",
"flate2",
"futures-util",
"http",
@@ -5742,7 +5799,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5e85aa143ceb072062fc4d6356c1b520a51d636e7bc8e77ec94be3608e5e80c"
dependencies = [
"crossbeam-channel",
"dirs",
"dirs 6.0.0",
"libappindicator",
"muda",
"objc2 0.6.4",
@@ -6927,6 +6984,15 @@ dependencies = [
"memchr",
]
[[package]]
name = "winreg"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
dependencies = [
"winapi",
]
[[package]]
name = "winreg"
version = "0.52.0"
@@ -7057,7 +7123,7 @@ dependencies = [
"block2 0.6.2",
"cookie",
"crossbeam-channel",
"dirs",
"dirs 6.0.0",
"dom_query",
"dpi",
"dunce",
+1
View File
@@ -18,6 +18,7 @@ tauri = { version = "2", features = ["macos-private-api", "tray-icon"] }
tauri-plugin-shell = "2"
tauri-plugin-updater = "2"
tauri-plugin-single-instance = "2"
tauri-plugin-autostart = "2"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tokio = { version = "1", features = ["full"] }
@@ -16,6 +16,7 @@
"core:webview:default",
"core:event:default",
"shell:allow-open",
"updater:default"
"updater:default",
"autostart:default"
]
}
+7
View File
@@ -56,6 +56,13 @@ pub fn run() {
}))
.plugin(tauri_plugin_shell::init())
.plugin(tauri_plugin_updater::Builder::new().build())
// 跨平台开机自启:mac 写 LaunchAgent plist,linux 写 ~/.config/autostart/*.desktop,
// windows 写 HKCU\Software\Microsoft\Windows\CurrentVersion\Run。前端 toggle 直接
// 调插件 isEnabled / enable / disable,不维持本地 prefs,让 OS 当唯一真相。issue #194。
.plugin(tauri_plugin_autostart::init(
tauri_plugin_autostart::MacosLauncher::LaunchAgent,
None,
))
.manage(coordinator.clone())
.setup(move |app| {
// Capsule 启动时定位到屏幕底部居中并隐藏;coordinator 按需显示。
+3
View File
@@ -266,6 +266,9 @@ export const en: typeof zhCN = {
capsuleDesc: 'Show a translucent capsule at the bottom of the screen while recording / transcribing.',
restoreClipboardLabel: 'Restore clipboard after insert',
restoreClipboardDesc: 'Windows / Linux only: restore your original clipboard after a successful paste (default on). Turn off to keep the dictation text in the clipboard so you can manually Ctrl+V if the simulated paste did not actually land. See issue #111.',
startupAtBoot: 'Launch at login',
startupAtBootDesc: 'Start OpenLess automatically when you sign in. macOS uses a LaunchAgent, Linux writes ~/.config/autostart, Windows writes HKCU\\Run (no admin required). See issue #194.',
startupAtBootError: 'Failed to toggle launch at login: {{message}}',
},
providers: {
llmTitle: 'LLM (polishing)',
+3
View File
@@ -264,6 +264,9 @@ export const zhCN = {
capsuleDesc: '录音 / 转写时在屏幕底部显示半透明胶囊。',
restoreClipboardLabel: '插入后恢复剪贴板',
restoreClipboardDesc: '仅 Windows / Linux:粘贴成功后恢复你原来的剪贴板内容(默认开)。关掉就把听写文本留在剪贴板,模拟粘贴没真正落地时可以手动 Ctrl+V 找回。详见 issue #111。',
startupAtBoot: '开机自启',
startupAtBootDesc: '登录后自动启动 OpenLess。macOS 写 LaunchAgent,Linux 写 ~/.config/autostart,Windows 写 HKCU\\Run(不需要管理员)。详见 issue #194。',
startupAtBootError: '开机自启切换失败:{{message}}',
},
providers: {
llmTitle: 'LLM 模型(润色)',
+61
View File
@@ -4,6 +4,7 @@
import { useEffect, useRef, useState, type CSSProperties, type ReactNode } from 'react';
import { useTranslation } from 'react-i18next';
import { disable as disableAutostart, enable as enableAutostart, isEnabled as isAutostartEnabled } from '@tauri-apps/plugin-autostart';
import { Icon } from '../components/Icon';
import { isDialogStatus, UpdateDialog, useAutoUpdate } from '../components/AutoUpdate';
import { APP_VERSION_LABEL } from '../lib/appVersion';
@@ -231,6 +232,7 @@ function RecordingSection() {
>
<Toggle on={prefs.restoreClipboardAfterPaste} onToggle={onRestoreClipboardChange} />
</SettingRow>
<AutostartRow />
{capability.statusHint && (
<div style={{ marginTop: 6, fontSize: 11.5, color: 'var(--ol-ink-4)', lineHeight: 1.5 }}>
{capability.statusHint}
@@ -240,6 +242,65 @@ function RecordingSection() {
);
}
// 不存进 prefs:autostart 状态由 OS 持有(mac LaunchAgent plist / linux .desktop /
// windows HKCU\Run),prefs 缓存反而会与 OS 真相不一致。issue #194。
function AutostartRow() {
const { t } = useTranslation();
const [enabled, setEnabled] = useState(false);
const [loaded, setLoaded] = useState(false);
// 切 plist / 注册表失败时给用户看的错误。null = 没有失败/上次操作已成功。
// 不渲染等于把失败吞掉 —— Windows 写 HKCU\Run 被组策略拦、macOS 写
// LaunchAgent plist 权限不够 都是真实可能。issue #194。
const [error, setError] = useState<string | null>(null);
useEffect(() => {
let cancelled = false;
isAutostartEnabled()
.then(v => {
if (!cancelled) {
setEnabled(v);
setLoaded(true);
}
})
.catch(err => {
console.error('[autostart] isEnabled failed', err);
if (!cancelled) setLoaded(true);
});
return () => {
cancelled = true;
};
}, []);
const onToggle = async (next: boolean) => {
setEnabled(next);
setError(null);
try {
if (next) await enableAutostart();
else await disableAutostart();
} catch (err) {
console.error('[autostart] toggle failed', err);
setEnabled(!next);
setError(err instanceof Error ? err.message : String(err));
}
};
return (
<SettingRow
label={t('settings.recording.startupAtBoot')}
desc={t('settings.recording.startupAtBootDesc')}
>
<div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
{loaded ? <Toggle on={enabled} onToggle={onToggle} /> : null}
{error && (
<div style={{ fontSize: 11, color: 'var(--ol-err)', marginTop: 4, lineHeight: 1.5 }}>
{t('settings.recording.startupAtBootError', { message: error })}
</div>
)}
</div>
</SettingRow>
);
}
function Toggle({ on, onToggle }: { on: boolean; onToggle?: (next: boolean) => void }) {
return (
<button