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
79 changes: 65 additions & 14 deletions .github/inno-script/Boss-Key.iss
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@
; 依赖 package.ps1 先组装好便携文件夹 dist\Boss-Key,安装包的文件与许可协议都取自那里。
; 需要 Inno Setup 7+:简繁中文语言包自 7.0 起才随官方安装包分发(见 scripts/install-inno.ps1)。

; 不设默认值:写死的版本号迟早会过期,装出一个版本号对不上的包比编译失败更难发现。
#ifndef MyAppVersion
#define MyAppVersion "3.0.0"
#error "缺少 MyAppVersion:请用 scripts/package.ps1 -Installer 编译,由它从 Cargo.toml 取版本号传入"
#endif
#ifndef MyAppVersion4
#define MyAppVersion4 "3.0.0.0"
#error "缺少 MyAppVersion4:请用 scripts/package.ps1 -Installer 编译"
#endif

#define MyAppName "Boss Key"
Expand All @@ -19,6 +20,8 @@
#define CoreExe "Boss Key.exe"
#define ConfigExe "config.exe"
#define SourceDir "..\..\dist\Boss-Key"
; 文件名须与 crates/common/src/paths.rs 的 INSTALLED_MARKER 一致
#define InstalledMarker "installed.marker"

[Setup]
AppId={{BA8E9784-B92D-48EE-B447-99709232260B}
Expand All @@ -34,6 +37,11 @@ DefaultGroupName={#MyAppName}
AllowNoIcons=yes
; 复用便携版里的那份,避免和仓库根 LICENSE 各自漂移
LicenseFile={#SourceDir}\LICENSE.txt
; 默认普通权限安装({autopf} 此时为 %LocalAppData%\Programs):不必为装个隐藏窗口的小工具
; 弹 UAC。仍允许在启动对话框改选「为所有用户安装」装进 Program Files。
; 两种模式下数据都在 %APPDATA%\BossKey,与安装目录无关,见 crates/common/src/paths.rs。
; 升级时 Inno 沿用上次的安装模式(UsePreviousPrivileges 默认开),旧的按机器安装原地升级。
PrivilegesRequired=lowest
PrivilegesRequiredOverridesAllowed=dialog
OutputDir=..\..\dist\installer
OutputBaseFilename=Boss-Key-{#MyAppVersion}-Setup
Expand All @@ -52,9 +60,9 @@ Name: "chinesetraditional"; MessagesFile: "compiler:Languages\ChineseTraditional
Name: "english"; MessagesFile: "compiler:Default.isl"

[CustomMessages]
chinesesimplified.KeepConfigPrompt=是否保留配置文件(config.json)?%n%n选择“是”将保留你的设置,重新安装后可继续使用;%n选择“否”将删除包括配置文件在内的整个安装目录
chinesetraditional.KeepConfigPrompt=是否保留設定檔(config.json)?%n%n選擇「是」將保留你的設定,重新安裝後可繼續使用;%n選擇「否」將刪除包括設定檔在內的整個安裝目錄
english.KeepConfigPrompt=Do you want to keep your settings file (config.json)?%n%nChoose "Yes" to keep your settings for a future reinstall;%nchoose "No" to delete the entire installation folder, including the settings file.
chinesesimplified.KeepConfigPrompt=是否保留配置文件(config.json)?%n%n选择“是”将保留你的设置,重新安装后可继续使用;%n选择“否”将删除包括配置文件在内的全部数据
chinesetraditional.KeepConfigPrompt=是否保留設定檔(config.json)?%n%n選擇「是」將保留你的設定,重新安裝後可繼續使用;%n選擇「否」將刪除包括設定檔在內的全部資料
english.KeepConfigPrompt=Do you want to keep your settings file (config.json)?%n%nChoose "Yes" to keep your settings for a future reinstall;%nchoose "No" to delete all data, including the settings file.

[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
Expand All @@ -63,6 +71,9 @@ Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{
Source: "{#SourceDir}\{#CoreExe}"; DestDir: "{app}"; Flags: ignoreversion
Source: "{#SourceDir}\{#ConfigExe}"; DestDir: "{app}"; Flags: ignoreversion
Source: "static\icon.ico"; DestDir: "{app}"; Flags: ignoreversion
; 安装版标记:程序据它把数据存到 %APPDATA%\BossKey 而非安装目录(见 crates/common/src/paths.rs)。
; 不放在便携文件夹 dist\Boss-Key 里,故直接从脚本目录取——便携版有了它就不便携了。
Source: "{#InstalledMarker}"; DestDir: "{app}"; Flags: ignoreversion

[Icons]
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#CoreExe}"
Expand All @@ -79,6 +90,8 @@ const
AutostartTaskName = 'BossKeyAutostart';
RunSubkey = 'Software\Microsoft\Windows\CurrentVersion\Run';
RunValueName = 'Boss Key Application';
// 配置界面(Tauri)的 WebView2 用户数据目录名,等于 tauri.conf.json 里的 identifier。
WebViewDataDirName = 'cn.hanloth.bosskey.config';

// 强制结束核心与配置进程。核心是无窗口常驻进程,CloseApplications 关不掉它;
// 且映像名 "Boss Key.exe" 含空格,taskkill 的 /IM 值必须加引号,否则参数被拆断而失败。
Expand Down Expand Up @@ -111,13 +124,42 @@ begin
Result := '';
end;

// 清理一个数据目录里的运行时产物(日志、恢复文件、缓存、写入残留),配置文件除外。
// config.json.tmp 是原子保存的中间文件,写到一半崩溃会留下;
// .BossKey-write-probe-* 是可写性探测的探针文件,进程被强杀时会留下。
// 二者都不该留在磁盘上,且留着会让空目录删不掉。
procedure RemoveRuntimeFiles(Dir: string);
begin
DelTree(Dir + '\logs', True, True, True);
DeleteFile(Dir + '\recovery.json');
DeleteFile(Dir + '\verhub_cache.json');
DeleteFile(Dir + '\config.json.tmp');
DelTree(Dir + '\.BossKey-write-probe-*', False, True, False);
end;

// 清理配置界面的 WebView2 用户数据目录(%LOCALAPPDATA%\<identifier>\EBWebView)。
// 它不在数据目录里,位置由 Tauri 按 identifier 决定,不删会残留几十 MB 的浏览器缓存。
// 里面只有缓存与本地存储,用户设置在 config.json 中,故不受「是否保留配置」影响。
procedure RemoveWebViewData;
begin
DelTree(ExpandConstant('{localappdata}\' + WebViewDataDirName), True, True, True);
end;

// 卸载时清理:
// - usUninstall(删文件前):摘掉自启看门狗并结束进程,确保核心不会被重新拉起、文件不被占用。
// - usPostUninstall(删文件后):清理运行时产物(日志、恢复文件),并询问是否保留配置文件;
// 保留则只留下 config.json,不保留则连同整个安装目录一起删除。静默卸载不弹窗,默认保留配置。
// - usPostUninstall(删文件后):清理运行时产物(日志、恢复文件、缓存、WebView2 用户数据),
// 并询问是否保留配置文件;保留则只留下 config.json,不保留则连同整个目录一起删除。
// 静默卸载不弹窗,默认保留配置。
//
// 安装版的数据在 %APPDATA%\BossKey(见 crates/common/src/paths.rs)。安装目录里也扫一遍:
// 早期版本把数据放在那里,迁移时删不掉的原文件会留下。
//
// 提权卸载(按机器安装)时 {userappdata} / {localappdata} 指向执行卸载的账户:与当初安装的是
// 同一账户(UAC 提权自己)时正确,由另一个管理员账户授权时则指向错误的用户目录,那里找不到
// 文件、什么也不会删。宁可留下也不去遍历所有用户目录误删别人的数据。
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
var
AppDir: string;
AppDir, UserDir: string;
KeepConfig: Boolean;
begin
if CurUninstallStep = usUninstall then
Expand All @@ -130,15 +172,24 @@ begin
if CurUninstallStep <> usPostUninstall then
Exit;
AppDir := ExpandConstant('{app}');
DelTree(AppDir + '\logs', True, True, True);
DeleteFile(AppDir + '\recovery.json');
if FileExists(AppDir + '\config.json') then
UserDir := ExpandConstant('{userappdata}\BossKey');
RemoveRuntimeFiles(AppDir);
RemoveRuntimeFiles(UserDir);
RemoveWebViewData;

if FileExists(AppDir + '\config.json') or FileExists(UserDir + '\config.json') then
begin
KeepConfig := UninstallSilent or
(MsgBox(CustomMessage('KeepConfigPrompt'), mbConfirmation, MB_YESNO) = IDYES);
if not KeepConfig then
begin
DelTree(AppDir, True, True, True);
end
else
RemoveDir(AppDir);
DelTree(UserDir, True, True, True);
Exit;
end;
end;

// 只在目录已空时收尾,留着配置的目录会被跳过。
RemoveDir(AppDir);
RemoveDir(UserDir);
end;
16 changes: 16 additions & 0 deletions .github/inno-script/installed.marker
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
This file marks an installed copy of Boss Key.

Because of it, the program stores its settings in %APPDATA%\BossKey instead of
this folder: an installation may live under C:\Program Files, which normal
privileges cannot write to.

Do not delete it. The uninstaller removes it for you.

---

此文件标记这是 Boss Key 的安装版。

程序据此把设置存到 %APPDATA%\BossKey,而不是本目录:安装目录可能位于
C:\Program Files,普通权限写不进去。

请勿删除,卸载时会自动移除。
5 changes: 2 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,10 @@ jobs:
run: |
$headers = @{ Authorization = "Bearer $env:STS_TOKEN"; 'Content-Type' = 'application/json' }

# 版本号只落在这两处,其余地方构建时取自 Cargo.toml(见 scripts/version.ps1)
$files = @(
'Cargo.toml',
'Cargo.lock',
'apps/config/src-tauri/tauri.conf.json',
'apps/config/ui/package.json'
'Cargo.lock'
)
$additions = foreach ($f in $files) {
@{ path = $f; contents = [Convert]::ToBase64String([IO.File]::ReadAllBytes($f)) }
Expand Down
16 changes: 16 additions & 0 deletions README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,22 @@ The settings window and the core's tray menu and notifications are available in

For the full feature list and usage guide, see the Boss-Key [guide](https://boss-key.ivan-hanloth.cn/en/guide/).

## Where the data lives, and how to remove it

The **portable edition** keeps its settings, logs, recovery file and cache **inside the program folder**, so copying the folder takes your whole setup with it. If that folder is not writable (it sits somewhere like `C:\Program Files`, or on read-only media), the program stores them in `%APPDATA%\BossKey` instead and says so in the settings window.

The **installer edition** always uses `%APPDATA%\BossKey`: the installation folder may be `C:\Program Files`, which normal privileges cannot write to. The program tells the two apart by the `installed.marker` file the installer drops.

Either way, the browser component used by the settings window keeps its own data in `%LOCALAPPDATA%\cn.hanloth.bosskey.config`, which deleting the program folder does not remove. The package ships a `cleanup.ps1`; open PowerShell in the program folder and run:

```powershell
powershell -ExecutionPolicy Bypass -File cleanup.ps1
```

It lists what it is about to delete and waits for your confirmation, then removes `%LOCALAPPDATA%\cn.hanloth.bosskey.config`, any `%APPDATA%\BossKey`, and what autostart leaves behind: the scheduled task `BossKeyAutostart` and the registry entry `HKCU\Software\Microsoft\Windows\CurrentVersion\Run\Boss Key Application`. The program folder itself is left alone — delete it yourself once the script is done.

The installer edition does not need this: the uninstaller already does the same, and asks whether to keep your settings file.

## Development and contributing

For details on development and contributing, see the Boss-Key [development docs](https://boss-key.ivan-hanloth.cn/en/dev/).
Expand Down
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,22 @@ v2.1.0版本加入了鼠标相关操作隐藏绑定,可以选择鼠标中键

完整功能介绍及使用指南,请参阅 Boss-Key [使用文档](https://boss-key.ivan-hanloth.cn/guide)

## 数据存放位置与清理

**便携版**把配置、日志、恢复文件与缓存放在**程序文件夹里**,拷走整个文件夹就带走了全部设置。若该文件夹不可写(放在了 `C:\Program Files` 之类的地方,或只读介质上),程序会改存到 `%APPDATA%\BossKey` 并在界面上说明原因。

**安装版**一律存到 `%APPDATA%\BossKey`:安装目录可能在 `C:\Program Files`,普通权限写不进去。程序凭安装包放的 `installed.marker` 分辨自己是哪一种。

无论哪种,配置界面用到的浏览器组件另有一份数据在 `%LOCALAPPDATA%\cn.hanloth.bosskey.config`,删程序文件夹清不掉它。随包附有 `cleanup.ps1`,在程序目录里打开 PowerShell 执行即可:

```powershell
powershell -ExecutionPolicy Bypass -File cleanup.ps1
```

它会列出将要删除的内容并等你确认,随后清理 `%LOCALAPPDATA%\cn.hanloth.bosskey.config`、可能存在的 `%APPDATA%\BossKey`,以及开机自启留下的计划任务 `BossKeyAutostart` 和注册表项 `HKCU\Software\Microsoft\Windows\CurrentVersion\Run\Boss Key Application`。程序文件夹本身不会被删,跑完后自行删除即可。

安装版无需这一步:卸载程序已经做了同样的事,并会询问是否保留配置文件。

## 开发及贡献指南

有关开发和贡献的详细信息,请参阅 Boss-Key [开发文档](https://boss-key.ivan-hanloth.cn/dev)
Expand Down
16 changes: 16 additions & 0 deletions README.zh-TW.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,22 @@ Boss-Key 支援以滑鼠中鍵、側鍵 1、側鍵 2 切換隱藏狀態,並可

完整功能介紹及使用指南,請參閱 Boss-Key [使用說明](https://boss-key.ivan-hanloth.cn/zh-tw/guide/)。

## 資料存放位置與清理

**可攜版**把設定、記錄檔、復原檔與快取放在**程式資料夾裡**,複製走整個資料夾就帶走了全部設定。若該資料夾不可寫入(放在了 `C:\Program Files` 之類的地方,或唯讀媒體上),程式會改存到 `%APPDATA%\BossKey` 並在介面上說明原因。

**安裝版**一律存到 `%APPDATA%\BossKey`:安裝資料夾可能在 `C:\Program Files`,一般權限寫不進去。程式憑安裝程式放的 `installed.marker` 分辨自己是哪一種。

無論哪種,設定介面用到的瀏覽器元件另有一份資料在 `%LOCALAPPDATA%\cn.hanloth.bosskey.config`,刪程式資料夾清不掉它。隨附有 `cleanup.ps1`,在程式資料夾裡開啟 PowerShell 執行即可:

```powershell
powershell -ExecutionPolicy Bypass -File cleanup.ps1
```

它會列出將要刪除的內容並等你確認,隨後清理 `%LOCALAPPDATA%\cn.hanloth.bosskey.config`、可能存在的 `%APPDATA%\BossKey`,以及開機自動啟動留下的排程工作 `BossKeyAutostart` 與登錄項目 `HKCU\Software\Microsoft\Windows\CurrentVersion\Run\Boss Key Application`。程式資料夾本身不會被刪,跑完後自行刪除即可。

安裝版不需要這一步:解除安裝程式已經做了同樣的事,並會詢問是否保留設定檔。

## 開發及貢獻指南

有關開發和貢獻的詳細資訊,請參閱 Boss-Key [開發文件](https://boss-key.ivan-hanloth.cn/zh-tw/dev/)。
Expand Down
54 changes: 44 additions & 10 deletions apps/config/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,23 @@ mod verhub;

const CORE_EXE: &str = "Boss Key.exe";

/// 程序自身所在目录:只用来找同目录下的可执行文件(核心、pssuspend)。
/// 数据文件一律走 [`bosskey_common::paths`]——安装版存到用户目录,便携版才在这里。
fn exe_dir() -> PathBuf {
std::env::current_exe()
.ok()
.and_then(|p| p.parent().map(|d| d.to_path_buf()))
.unwrap_or_else(|| PathBuf::from("."))
bosskey_common::paths::exe_dir()
}

/// 数据目录(配置、日志、恢复文件、缓存);与核心得出的结果一致。
fn data_dir() -> PathBuf {
bosskey_common::paths::data_dir()
}

fn config_path() -> PathBuf {
exe_dir().join("config.json")
bosskey_common::paths::config_path()
}

fn log_dir() -> PathBuf {
data_dir().join(bosskey_core::logging::LOG_DIR_NAME)
}

/// 定位同目录下的核心程序,不存在时报错。
Expand Down Expand Up @@ -61,6 +69,15 @@ async fn blocking<T: Send + 'static>(f: impl FnOnce() -> T + Send + 'static) ->
.expect("阻塞任务执行失败")
}

/// 数据目录的位置与由来,供界面在便携版回退时提示用户。
#[derive(Serialize)]
struct DataLocation {
dir: String,
program_dir: String,
/// `installed` / `portable` / `portable_fallback`。
kind: &'static str,
}

#[derive(Serialize)]
struct AppInfo {
name: &'static str,
Expand Down Expand Up @@ -389,17 +406,33 @@ fn startup_action() -> Option<String> {
.find(|a| a == bosskey_common::ARG_RESTORE || a == bosskey_common::ARG_ABOUT)
}

/// 打开日志目录(`<exe 同目录>/logs`);目录不存在时先创建,再用资源管理器打开。
/// 打开日志目录(`<数据目录>/logs`);目录不存在时先创建,再用资源管理器打开。
#[tauri::command]
async fn open_log_dir() -> Result<(), String> {
blocking(|| {
let dir = exe_dir().join(bosskey_core::logging::LOG_DIR_NAME);
let dir = log_dir();
std::fs::create_dir_all(&dir).map_err(|e| e.to_string())?;
bosskey_core::shell::open(&dir.to_string_lossy())
})
.await
}

/// 数据目录及其由来。界面据 `kind` 判断是否要提示便携版写不进程序目录。
#[tauri::command]
fn data_location() -> DataLocation {
use bosskey_common::paths::DataDirKind;
let located = bosskey_common::paths::locate();
DataLocation {
dir: located.dir.display().to_string(),
program_dir: located.program_dir.display().to_string(),
kind: match located.kind {
DataDirKind::Installed => "installed",
DataDirKind::Portable => "portable",
DataDirKind::PortableFallback => "portable_fallback",
},
}
}

#[tauri::command]
fn app_info() -> AppInfo {
AppInfo {
Expand All @@ -423,11 +456,11 @@ async fn open_external(url: String) -> Result<(), String> {
blocking(move || bosskey_core::shell::open(&url)).await
}

/// 项目公开链接(主页 / 仓库 / 文档等)。带缓存(内存 + exe 同目录磁盘文件
/// 项目公开链接(主页 / 仓库 / 文档等)。带缓存(内存 + 数据目录下的磁盘文件
/// 有效期一天),过期才请求 Verhub;请求失败退回过期缓存。
#[tauri::command]
async fn verhub_project_links() -> Result<verhub::ProjectLinks, String> {
verhub::project_links(&exe_dir().join("verhub_cache.json"))
verhub::project_links(&data_dir().join("verhub_cache.json"))
.await
.map_err(|e| e.to_string())
}
Expand Down Expand Up @@ -484,7 +517,7 @@ async fn verhub_upload_log(content: String) -> Result<(), String> {
#[tauri::command]
async fn recent_log_tail(lines: usize) -> String {
blocking(move || {
let dir = exe_dir().join(bosskey_core::logging::LOG_DIR_NAME);
let dir = log_dir();
let latest = std::fs::read_dir(&dir)
.ok()
.into_iter()
Expand Down Expand Up @@ -557,6 +590,7 @@ pub fn run() {
pssuspend_available,
startup_action,
app_info,
data_location,
open_external,
verhub_project_links,
verhub_check_update,
Expand Down
1 change: 0 additions & 1 deletion apps/config/src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "Boss Key",
"version": "3.1.0-rc.1",
"identifier": "cn.hanloth.bosskey.config",
"build": {
"frontendDist": "../dist",
Expand Down
2 changes: 0 additions & 2 deletions apps/config/ui/package-lock.json

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

1 change: 0 additions & 1 deletion apps/config/ui/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{
"name": "bosskey-config-ui",
"private": true,
"version": "3.1.0-rc.1",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down
Loading
Loading