Merge pull request #217 from appergb/feat/autostart-194
feat(autostart): macOS / Linux / Windows 三平台开机自启
This commit is contained in:
9 files changed
+163
-10
No files matched your search
Generated
+12
-2
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
Generated
+73
-7
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
@@ -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 按需显示。
|
||||
|
||||
@@ -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)',
|
||||
|
||||
@@ -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 模型(润色)',
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user