Skip to content

Commit 3388857

Browse files
committed
feat(desktop): 系统托盘常驻 + Windows 自绘红绿灯窗口
- 系统托盘:图标常驻通知区(tooltip「ScriptDeck 脚本中台」),左键恢复窗口, 右键菜单「显示主界面 / 退出」;点关闭按钮隐藏到托盘而非退出(CloseRequested → hide + prevent_close),只能经托盘菜单真正退出 - Cargo.toml 启用 tauri tray-icon feature - Windows/Linux 去原生标题栏(set_decorations(false)),前端自绘 mac 红绿灯 (close/minimize/toggleMaximize),capabilities 补窗口权限 - 前端徽章/标题兼容 .sh,同步 sync-ui 校验规则 - VERSION 1.1.0 → 1.2.0(触发 CI 打包发布 Release)
1 parent f30eeef commit 3388857

14 files changed

Lines changed: 2040 additions & 1700 deletions

File tree

‎VERSION‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.1.0
1+
1.2.0

‎desktop/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "scriptdeck-desktop",
33
"private": true,
4-
"version": "1.1.0",
4+
"version": "1.2.0",
55
"description": "ScriptDeck 桌面版(Tauri 2 + Rust)",
66
"scripts": {
77
"sync-ui": "node scripts/sync-ui.mjs",

‎desktop/scripts/sync-ui.mjs‎

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ const htmlReplacements = [
3030
[`{{ url_for('static', filename='js/vendor/marked.min.js') }}`, "static/js/vendor/marked.min.js"],
3131
[`{{ url_for('static', filename='js/app.js') }}`, "static/js/app.js"],
3232
["../static/logo.png", "static/logo.png"],
33-
["自动扫描 bat / vbs 和 README", "自动扫描 bat / vbs / sh 和 README"],
3433
];
3534
for (const [from, to] of htmlReplacements) {
3635
if (!html.includes(from)) {
@@ -115,15 +114,15 @@ const jsReplacements = [
115114
});`,
116115
` await invoke("open_folder", { path: folderPath });`,
117116
],
118-
// .sh 标题后缀剥离
117+
// .sh 标题后缀剥离(根目录源已支持 .sh,此条仅作存在性校验)
119118
[
120-
`scriptFile.name.replace(/\\.(bat|vbs)$/i, "")`,
119+
`scriptFile.name.replace(/\\.(bat|vbs|sh)$/i, "")`,
121120
`scriptFile.name.replace(/\\.(bat|vbs|sh)$/i, "")`,
122121
],
123-
// 徽章:BAT/VBS/SH 通用化
122+
// 徽章:类型大写直出(根目录源已通用化,此条仅作存在性校验)
124123
[
125-
`<span class="badge \${script.type === "vbs" ? "vbs" : ""}">\${script.type === "vbs" ? "VBS" : "BAT"}</span>`,
126-
`<span class="badge \${script.type === "bat" ? "" : script.type}">\${script.type.toUpperCase()}</span>`,
124+
`<span class="badge \${escapeAttr(script.type)}">\${escapeHtml(script.type.toUpperCase())}</span>`,
125+
`<span class="badge \${escapeAttr(script.type)}">\${escapeHtml(script.type.toUpperCase())}</span>`,
127126
],
128127
];
129128

‎desktop/src-tauri/Cargo.lock‎

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎desktop/src-tauri/Cargo.toml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
[package]
22
name = "scriptdeck"
3-
version = "1.1.0"
3+
version = "1.2.0"
44
description = "ScriptDeck 脚本中台桌面版"
55
edition = "2021"
66

77
[build-dependencies]
88
tauri-build = { version = "2", features = [] }
99

1010
[dependencies]
11-
tauri = { version = "2", features = [] }
11+
tauri = { version = "2", features = ["tray-icon"] }
1212
serde = { version = "1", features = ["derive"] }
1313
serde_json = "1"
1414
walkdir = "2"

‎desktop/src-tauri/capabilities/default.json‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,12 @@
33
"identifier": "default",
44
"description": "ScriptDeck 主窗口默认能力(所有业务逻辑走自定义 Rust 命令)",
55
"windows": ["main"],
6-
"permissions": ["core:default"]
6+
"permissions": [
7+
"core:default",
8+
"core:window:allow-minimize",
9+
"core:window:allow-toggle-maximize",
10+
"core:window:allow-close",
11+
"core:window:allow-start-dragging",
12+
"core:window:allow-internal-toggle-maximize"
13+
]
714
}

‎desktop/src-tauri/src/main.rs‎

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,65 @@ fn open_folder(path: String) -> Value {
126126

127127
fn main() {
128128
tauri::Builder::default()
129+
.setup(|app| {
130+
use tauri::Manager;
131+
use tauri::menu::{Menu, MenuItem, PredefinedMenuItem};
132+
use tauri::tray::TrayIconBuilder;
133+
// Windows/Linux 去掉原生标题栏,由前端自绘 mac 红绿灯接管窗口控制;
134+
// macOS 保留原生窗口(titleBarStyle Overlay,红绿灯融入顶栏)
135+
#[cfg(any(windows, target_os = "linux"))]
136+
{
137+
if let Some(win) = app.get_webview_window("main") {
138+
let _ = win.set_decorations(false);
139+
}
140+
}
141+
if let Some(win) = app.get_webview_window("main") {
142+
let _ = win.show();
143+
}
144+
145+
// 系统托盘:常驻通知区,关窗不退出,只能从托盘菜单真正退出
146+
let show_item = MenuItem::with_id(app, "show", "显示主界面", true, None::<&str>)?;
147+
let quit_item = MenuItem::with_id(app, "quit", "退出", true, None::<&str>)?;
148+
let sep = PredefinedMenuItem::separator(app)?;
149+
let tray_menu = Menu::with_items(app, &[&show_item, &sep, &quit_item])?;
150+
let _tray = TrayIconBuilder::with_id("main-tray")
151+
.icon(app.default_window_icon().expect("app icon").clone())
152+
.menu(&tray_menu)
153+
.tooltip("ScriptDeck 脚本中台")
154+
.show_menu_on_left_click(false)
155+
.on_menu_event(|app, event| match event.id().as_ref() {
156+
"show" => {
157+
if let Some(win) = app.get_webview_window("main") {
158+
let _ = win.show();
159+
let _ = win.unminimize();
160+
let _ = win.set_focus();
161+
}
162+
}
163+
"quit" => app.exit(0),
164+
_ => {}
165+
})
166+
.on_tray_icon_event(|tray, event| {
167+
if let tauri::tray::TrayIconEvent::Click { button, .. } = event {
168+
if button == tauri::tray::MouseButton::Left {
169+
let app = tray.app_handle();
170+
if let Some(win) = app.get_webview_window("main") {
171+
let _ = win.show();
172+
let _ = win.unminimize();
173+
let _ = win.set_focus();
174+
}
175+
}
176+
}
177+
})
178+
.build(app)?;
179+
Ok(())
180+
})
181+
.on_window_event(|window, event| {
182+
// 点红键 / 关闭按钮 → 隐藏到托盘而非退出,保证常驻
183+
if let tauri::WindowEvent::CloseRequested { api, .. } = event {
184+
let _ = window.hide();
185+
api.prevent_close();
186+
}
187+
})
129188
.invoke_handler(tauri::generate_handler![
130189
scan,
131190
set_root,

‎desktop/src-tauri/tauri.conf.json‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"$schema": "https://schema.tauri.app/config/2",
33
"productName": "ScriptDeck",
4-
"version": "1.1.0",
4+
"version": "1.2.0",
55
"identifier": "com.scriptdeck.app",
66
"build": {
77
"frontendDist": "../ui"
@@ -15,7 +15,10 @@
1515
"height": 900,
1616
"minWidth": 980,
1717
"minHeight": 640,
18-
"center": true
18+
"center": true,
19+
"visible": false,
20+
"titleBarStyle": "Overlay",
21+
"hiddenTitle": true
1922
}
2023
],
2124
"security": {

‎desktop/ui/index.html‎

Lines changed: 45 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,38 @@
1313
<div class="ambient ambient-one"></div>
1414
<div class="ambient ambient-two"></div>
1515

16-
<div class="app-shell">
17-
<div class="workspace">
18-
<aside class="sidebar">
19-
<div class="sidebar-header">
20-
<div class="brand">
21-
<img src="static/logo.png" alt="ScriptDeck Logo" class="brand-logo">
22-
<div>
23-
<div class="brand-title">ScriptDeck</div>
24-
<div class="brand-subtitle">脚本中台</div>
25-
</div>
16+
<div class="app-shell" id="appShell">
17+
<header class="topbar" id="topbar">
18+
<div class="topbar-inner">
19+
<div class="traffic-lights" data-tauri-drag-region>
20+
<button class="tl tl-close" id="winCloseBtn" type="button" title="关闭窗口" aria-label="关闭窗口"></button>
21+
<button class="tl tl-min" id="winMinBtn" type="button" title="最小化" aria-label="最小化"></button>
22+
<button class="tl tl-max" id="winMaxBtn" type="button" title="最大化 / 还原" aria-label="最大化"></button>
23+
</div>
24+
<div class="brand" data-tauri-drag-region>
25+
<img src="static/logo.png" alt="ScriptDeck Logo" class="brand-logo">
26+
<div>
27+
<div class="brand-title">ScriptDeck</div>
28+
<div class="brand-subtitle">脚本中台</div>
2629
</div>
30+
</div>
2731

28-
<label class="command-search" for="searchInput">
29-
<span class="search-icon">⌘</span>
30-
<input id="searchInput" type="search" placeholder="搜索脚本…">
31-
</label>
32+
<label class="command-search" for="searchInput">
33+
<span class="search-icon">⌘</span>
34+
<input id="searchInput" type="search" placeholder="搜索脚本、目录或 README…">
35+
<kbd>Ctrl K</kbd>
36+
</label>
37+
38+
<div class="topbar-actions">
39+
<button class="ghost-button ghost-icon" id="themeBtn" type="button" title="切换主题">☾</button>
40+
<button class="ghost-button" id="refreshBtn" type="button" title="重新扫描">刷新</button>
41+
<button class="primary-button" id="settingsBtn" type="button">设置目录</button>
3242
</div>
43+
</div>
44+
</header>
3345

46+
<div class="workspace">
47+
<aside class="sidebar">
3448
<nav class="nav-tabs" aria-label="快捷筛选">
3549
<button class="nav-tab active" type="button" data-mode="all">
3650
全部<b id="countAll">0</b>
@@ -43,16 +57,26 @@
4357
</button>
4458
</nav>
4559

60+
<div class="metrics">
61+
<div class="metric-item">
62+
<strong id="metricDirs">0</strong>
63+
<span>目录</span>
64+
</div>
65+
<div class="metric-item">
66+
<strong id="metricScripts">0</strong>
67+
<span>脚本</span>
68+
</div>
69+
<div class="metric-item">
70+
<strong id="metricReadmes">0</strong>
71+
<span>文档</span>
72+
</div>
73+
</div>
74+
4675
<div class="sidebar-title">目录树</div>
4776
<div class="tree" id="sidebarTree"></div>
4877
</aside>
4978

5079
<main class="content">
51-
<section class="hero">
52-
<h1>更快找到脚本,更舒服地启动任务</h1>
53-
<p>自动扫描 bat / vbs / sh 和 README,支持收藏、最近运行、目录筛选与详情预览</p>
54-
</section>
55-
5680
<section class="content-toolbar">
5781
<div>
5882
<h2 id="viewTitle">全部脚本</h2>
@@ -74,31 +98,11 @@ <h2 id="viewTitle">全部脚本</h2>
7498
</main>
7599

76100
<aside class="detail-panel" id="detailPanel" aria-label="脚本详情">
77-
<div class="detail-actions-row">
78-
<button class="ghost-button" id="themeBtn" type="button" title="切换主题">☾</button>
79-
<button class="ghost-button" id="refreshBtn" type="button">刷新</button>
80-
<button class="primary-button" id="settingsBtn" type="button">设置目录</button>
81-
</div>
82-
<div class="detail-metrics">
83-
<div class="metric-item">
84-
<span>目录</span>
85-
<strong id="metricDirs">0</strong>
86-
</div>
87-
<div class="metric-item">
88-
<span>脚本</span>
89-
<strong id="metricScripts">0</strong>
90-
</div>
91-
<div class="metric-item">
92-
<span>文档</span>
93-
<strong id="metricReadmes">0</strong>
94-
</div>
95-
</div>
96-
97101
<div class="detail-content">
98102
<div class="detail-empty">
99103
<div class="empty-orb">✦</div>
100104
<h3>选择一个脚本</h3>
101-
<p>这里会展示路径、README 摘要和快捷操作。</p>
105+
<p>这里会展示路径、README 和快捷操作。</p>
102106
</div>
103107
</div>
104108
</aside>
@@ -114,7 +118,7 @@ <h3 id="settingsTitle">设置脚本根目录</h3>
114118
</div>
115119
<button class="icon-button" id="closeSettingsBtn" type="button" aria-label="关闭">×</button>
116120
</div>
117-
<p class="modal-copy">程序会扫描该目录下的子文件夹,收集 <b>.bat</b> / <b>.vbs</b> 与 README 文件。</p>
121+
<p class="modal-copy">程序会扫描该目录下的子文件夹,收集 <b>.bat</b> / <b>.vbs</b> / <b>.sh</b> 与 README 文件。</p>
118122
<div class="path-input-row">
119123
<input id="rootInput" type="text" placeholder="例如:C:\project">
120124
<button class="primary-button" id="setRootBtn" type="button">应用</button>

0 commit comments

Comments
 (0)