Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions openless-all/app/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions openless-all/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
80 changes: 73 additions & 7 deletions openless-all/app/src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions openless-all/app/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"] }
Expand Down
3 changes: 2 additions & 1 deletion openless-all/app/src-tauri/capabilities/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"core:webview:default",
"core:event:default",
"shell:allow-open",
"updater:default"
"updater:default",
"autostart:default"
]
}
7 changes: 7 additions & 0 deletions openless-all/app/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 按需显示。
Expand Down
3 changes: 3 additions & 0 deletions openless-all/app/src/i18n/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)',
Expand Down
3 changes: 3 additions & 0 deletions openless-all/app/src/i18n/zh-CN.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 模型(润色)',
Expand Down
61 changes: 61 additions & 0 deletions openless-all/app/src/pages/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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}
Expand All @@ -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
Expand Down
Loading