重要前提
安装AI Skills的关键前提是:必须科学上网,且开启TUN模式,这一点至关重要,直接决定安装能否顺利完成,在此郑重提醒三遍:科学上网,科学上网,科学上网。查看完整安装教程 →
customizing-tauri-windows by dchuk/claude-code-tauri-skills
npx skills add https://github.com/dchuk/claude-code-tauri-skills --skill customizing-tauri-windows涵盖 Tauri v2 中的窗口自定义功能:自定义标题栏、透明窗口和窗口菜单。
{
"app": {
"windows": [{
"title": "My App",
"width": 800,
"height": 600,
"decorations": true,
"transparent": false,
"alwaysOnTop": false,
"center": true
}]
}
}
{ "app": { "windows": [{ "decorations": false }] } }
{
"identifier": "main-capability",
"windows": ["main"],
"permissions": [
"core:window:default",
"core:window:allow-start-dragging",
"core:window:allow-close",
"core:window:allow-minimize",
"core:window:allow-toggle-maximize"
]
}
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
<div class="titlebar">
<div class="titlebar-drag" data-tauri-drag-region>
<span class="title">My Application</span>
</div>
<div class="titlebar-controls">
<button id="titlebar-minimize">-</button>
<button id="titlebar-maximize">[]</button>
<button id="titlebar-close">x</button>
</div>
</div>
<main class="content"><!-- App content --></main>
.titlebar {
height: 30px;
background: #329ea3;
position: fixed;
top: 0;
left: 0;
right: 0;
display: grid;
grid-template-columns: 1fr auto;
user-select: none;
}
.titlebar-drag {
display: flex;
align-items: center;
padding-left: 12px;
}
.titlebar-controls { display: flex; }
.titlebar-controls button {
width: 46px;
height: 30px;
border: none;
background: transparent;
color: white;
cursor: pointer;
}
.titlebar-controls button:hover { background: rgba(255,255,255,0.1); }
.titlebar-controls button#titlebar-close:hover { background: #e81123; }
.content { margin-top: 30px; padding: 16px; }
import { getCurrentWindow } from '@tauri-apps/api/window';
const appWindow = getCurrentWindow();
document.getElementById('titlebar-minimize')
?.addEventListener('click', () => appWindow.minimize());
document.getElementById('titlebar-maximize')
?.addEventListener('click', () => appWindow.toggleMaximize());
document.getElementById('titlebar-close')
?.addEventListener('click', () => appWindow.close());
data-tauri-drag-region 属性仅应用于其所在元素,不应用于子元素。这保留了按钮的交互性。如果需要,请为每个可拖拽的子元素添加该属性。
document.getElementById('titlebar')?.addEventListener('mousedown', (e) => {
if (e.buttons === 1 && e.target === e.currentTarget) {
e.detail === 2 ? appWindow.toggleMaximize() : appWindow.startDragging();
}
});
[target."cfg(target_os = \"macos\")".dependencies]
cocoa = "0.26"
use tauri::{TitleBarStyle, WebviewUrl, WebviewWindowBuilder};
pub fn run() {
tauri::Builder::default()
.setup(|app| {
let win_builder = WebviewWindowBuilder::new(app, "main", WebviewUrl::default())
.title("Transparent Titlebar Window")
.inner_size(800.0, 600.0);
#[cfg(target_os = "macos")]
let win_builder = win_builder.title_bar_style(TitleBarStyle::Transparent);
let window = win_builder.build().unwrap();
#[cfg(target_os = "macos")]
{
use cocoa::appkit::{NSColor, NSWindow};
use cocoa::base::{id, nil};
let ns_window = window.ns_window().unwrap() as id;
unsafe {
let bg_color = NSColor::colorWithRed_green_blue_alpha_(
nil, 50.0/255.0, 158.0/255.0, 163.5/255.0, 1.0
);
ns_window.setBackgroundColor_(bg_color);
}
}
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application")
}
注意:macOS 上的自定义标题栏会失去窗口贴靠等原生功能。透明标题栏则保留了这些功能。
| 类型 | 描述 |
|---|---|
| Text | 带有标签的基本菜单选项 |
| Check | 带有选中状态的可切换条目 |
| Separator | 各部分之间的视觉分隔符 |
| Icon | 带有自定义图标的条目 (Tauri 2.8.0+) |
import { Menu, MenuItem, Submenu, PredefinedMenuItem, CheckMenuItem } from '@tauri-apps/api/menu';
const fileSubmenu = await Submenu.new({
text: 'File',
items: [
await MenuItem.new({
id: 'new', text: 'New', accelerator: 'CmdOrCtrl+N',
action: () => console.log('New')
}),
await MenuItem.new({
id: 'open', text: 'Open', accelerator: 'CmdOrCtrl+O',
action: () => console.log('Open')
}),
await MenuItem.new({
id: 'save', text: 'Save', accelerator: 'CmdOrCtrl+S',
action: () => console.log('Save')
}),
{ type: 'Separator' },
await MenuItem.new({
id: 'quit', text: 'Quit', accelerator: 'CmdOrCtrl+Q',
action: () => console.log('Quit')
})
]
});
const editSubmenu = await Submenu.new({
text: 'Edit',
items: [
await PredefinedMenuItem.new({ item: 'Undo' }),
await PredefinedMenuItem.new({ item: 'Redo' }),
await PredefinedMenuItem.new({ item: 'Separator' }),
await PredefinedMenuItem.new({ item: 'Cut' }),
await PredefinedMenuItem.new({ item: 'Copy' }),
await PredefinedMenuItem.new({ item: 'Paste' })
]
});
const viewSubmenu = await Submenu.new({
text: 'View',
items: [
await CheckMenuItem.new({
id: 'sidebar', text: 'Show Sidebar', checked: true,
action: async (item) => console.log('Sidebar:', await item.isChecked())
})
]
});
const menu = await Menu.new({ items: [fileSubmenu, editSubmenu, viewSubmenu] });
await menu.setAsAppMenu();
use tauri::menu::{MenuBuilder, SubmenuBuilder};
let file_menu = SubmenuBuilder::new(app, "File")
.text("new", "New")
.text("open", "Open")
.text("save", "Save")
.separator()
.text("quit", "Quit")
.build()?;
let edit_menu = SubmenuBuilder::new(app, "Edit")
.undo()
.redo()
.separator()
.cut()
.copy()
.paste()
.build()?;
let menu = MenuBuilder::new(app)
.items(&[&file_menu, &edit_menu])
.build()?;
app.set_menu(menu)?;
macOS 注意:所有菜单项必须分组在子菜单下。顶级菜单项将被忽略。
app.on_menu_event(|_app_handle, event| {
match event.id().0.as_str() {
"new" => println!("New file"),
"open" => println!("Open file"),
"save" => println!("Save file"),
"quit" => std::process::exit(0),
_ => {}
}
});
JavaScript:
const statusItem = await menu.get('status');
if (statusItem) await statusItem.setText('Status: Ready');
Rust:
menu.get("status").unwrap().as_menuitem_unchecked().set_text("Status: Ready")?;
| 快捷键 | 加速键字符串 |
|---|---|
| Ctrl+S / Cmd+S | CmdOrCtrl+S |
| Ctrl+Shift+S | CmdOrCtrl+Shift+S |
| Alt+F4 | Alt+F4 |
| F11 | F11 |
use tauri::menu::{MenuBuilder, SubmenuBuilder};
pub fn run() {
tauri::Builder::default()
.setup(|app| {
let file_menu = SubmenuBuilder::new(app, "File")
.text("new", "New")
.text("open", "Open")
.separator()
.text("quit", "Quit")
.build()?;
let edit_menu = SubmenuBuilder::new(app, "Edit")
.undo().redo().separator().cut().copy().paste()
.build()?;
let menu = MenuBuilder::new(app)
.items(&[&file_menu, &edit_menu])
.build()?;
app.set_menu(menu)?;
Ok(())
})
.on_menu_event(|_app, event| {
match event.id().0.as_str() {
"quit" => std::process::exit(0),
id => println!("Menu event: {}", id),
}
})
.run(tauri::generate_context!())
.expect("error while running tauri application")
}
import { useEffect } from 'react';
import { getCurrentWindow } from '@tauri-apps/api/window';
function App() {
useEffect(() => {
const appWindow = getCurrentWindow();
const minimize = () => appWindow.minimize();
const maximize = () => appWindow.toggleMaximize();
const close = () => appWindow.close();
document.getElementById('titlebar-minimize')?.addEventListener('click', minimize);
document.getElementById('titlebar-maximize')?.addEventListener('click', maximize);
document.getElementById('titlebar-close')?.addEventListener('click', close);
return () => {
document.getElementById('titlebar-minimize')?.removeEventListener('click', minimize);
document.getElementById('titlebar-maximize')?.removeEventListener('click', maximize);
document.getElementById('titlebar-close')?.removeEventListener('click', close);
};
}, []);
return (
<>
<div className="titlebar">
<div className="titlebar-drag" data-tauri-drag-region>
<span>My Tauri App</span>
</div>
<div className="titlebar-controls">
<button id="titlebar-minimize">-</button>
<button id="titlebar-maximize">[]</button>
<button id="titlebar-close">x</button>
</div>
</div>
<main className="content">
<h1>Welcome to Tauri</h1>
</main>
</>
);
}
export default App;
每周安装量
44
代码仓库
GitHub 星标数
10
首次出现
2026年1月24日
安全审计
安装于
gemini-cli37
opencode36
codex36
cursor31
github-copilot31
claude-code30
Covers window customization in Tauri v2: custom titlebars, transparent windows, and window menus.
{
"app": {
"windows": [{
"title": "My App",
"width": 800,
"height": 600,
"decorations": true,
"transparent": false,
"alwaysOnTop": false,
"center": true
}]
}
}
{ "app": { "windows": [{ "decorations": false }] } }
{
"identifier": "main-capability",
"windows": ["main"],
"permissions": [
"core:window:default",
"core:window:allow-start-dragging",
"core:window:allow-close",
"core:window:allow-minimize",
"core:window:allow-toggle-maximize"
]
}
<div class="titlebar">
<div class="titlebar-drag" data-tauri-drag-region>
<span class="title">My Application</span>
</div>
<div class="titlebar-controls">
<button id="titlebar-minimize">-</button>
<button id="titlebar-maximize">[]</button>
<button id="titlebar-close">x</button>
</div>
</div>
<main class="content"><!-- App content --></main>
.titlebar {
height: 30px;
background: #329ea3;
position: fixed;
top: 0;
left: 0;
right: 0;
display: grid;
grid-template-columns: 1fr auto;
user-select: none;
}
.titlebar-drag {
display: flex;
align-items: center;
padding-left: 12px;
}
.titlebar-controls { display: flex; }
.titlebar-controls button {
width: 46px;
height: 30px;
border: none;
background: transparent;
color: white;
cursor: pointer;
}
.titlebar-controls button:hover { background: rgba(255,255,255,0.1); }
.titlebar-controls button#titlebar-close:hover { background: #e81123; }
.content { margin-top: 30px; padding: 16px; }
import { getCurrentWindow } from '@tauri-apps/api/window';
const appWindow = getCurrentWindow();
document.getElementById('titlebar-minimize')
?.addEventListener('click', () => appWindow.minimize());
document.getElementById('titlebar-maximize')
?.addEventListener('click', () => appWindow.toggleMaximize());
document.getElementById('titlebar-close')
?.addEventListener('click', () => appWindow.close());
The data-tauri-drag-region attribute applies only to its element, not children. This preserves button interactivity. Add the attribute to each draggable child if needed.
document.getElementById('titlebar')?.addEventListener('mousedown', (e) => {
if (e.buttons === 1 && e.target === e.currentTarget) {
e.detail === 2 ? appWindow.toggleMaximize() : appWindow.startDragging();
}
});
[target."cfg(target_os = \"macos\")".dependencies]
cocoa = "0.26"
use tauri::{TitleBarStyle, WebviewUrl, WebviewWindowBuilder};
pub fn run() {
tauri::Builder::default()
.setup(|app| {
let win_builder = WebviewWindowBuilder::new(app, "main", WebviewUrl::default())
.title("Transparent Titlebar Window")
.inner_size(800.0, 600.0);
#[cfg(target_os = "macos")]
let win_builder = win_builder.title_bar_style(TitleBarStyle::Transparent);
let window = win_builder.build().unwrap();
#[cfg(target_os = "macos")]
{
use cocoa::appkit::{NSColor, NSWindow};
use cocoa::base::{id, nil};
let ns_window = window.ns_window().unwrap() as id;
unsafe {
let bg_color = NSColor::colorWithRed_green_blue_alpha_(
nil, 50.0/255.0, 158.0/255.0, 163.5/255.0, 1.0
);
ns_window.setBackgroundColor_(bg_color);
}
}
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application")
}
Note : Custom titlebars on macOS lose native features like window snapping. Transparent titlebar preserves these.
| Type | Description |
|---|---|
| Text | Basic labeled menu option |
| Check | Toggleable entry with checked state |
| Separator | Visual divider between sections |
| Icon | Entry with custom icon (Tauri 2.8.0+) |
import { Menu, MenuItem, Submenu, PredefinedMenuItem, CheckMenuItem } from '@tauri-apps/api/menu';
const fileSubmenu = await Submenu.new({
text: 'File',
items: [
await MenuItem.new({
id: 'new', text: 'New', accelerator: 'CmdOrCtrl+N',
action: () => console.log('New')
}),
await MenuItem.new({
id: 'open', text: 'Open', accelerator: 'CmdOrCtrl+O',
action: () => console.log('Open')
}),
await MenuItem.new({
id: 'save', text: 'Save', accelerator: 'CmdOrCtrl+S',
action: () => console.log('Save')
}),
{ type: 'Separator' },
await MenuItem.new({
id: 'quit', text: 'Quit', accelerator: 'CmdOrCtrl+Q',
action: () => console.log('Quit')
})
]
});
const editSubmenu = await Submenu.new({
text: 'Edit',
items: [
await PredefinedMenuItem.new({ item: 'Undo' }),
await PredefinedMenuItem.new({ item: 'Redo' }),
await PredefinedMenuItem.new({ item: 'Separator' }),
await PredefinedMenuItem.new({ item: 'Cut' }),
await PredefinedMenuItem.new({ item: 'Copy' }),
await PredefinedMenuItem.new({ item: 'Paste' })
]
});
const viewSubmenu = await Submenu.new({
text: 'View',
items: [
await CheckMenuItem.new({
id: 'sidebar', text: 'Show Sidebar', checked: true,
action: async (item) => console.log('Sidebar:', await item.isChecked())
})
]
});
const menu = await Menu.new({ items: [fileSubmenu, editSubmenu, viewSubmenu] });
await menu.setAsAppMenu();
use tauri::menu::{MenuBuilder, SubmenuBuilder};
let file_menu = SubmenuBuilder::new(app, "File")
.text("new", "New")
.text("open", "Open")
.text("save", "Save")
.separator()
.text("quit", "Quit")
.build()?;
let edit_menu = SubmenuBuilder::new(app, "Edit")
.undo()
.redo()
.separator()
.cut()
.copy()
.paste()
.build()?;
let menu = MenuBuilder::new(app)
.items(&[&file_menu, &edit_menu])
.build()?;
app.set_menu(menu)?;
macOS Note : All menu items must be grouped under submenus. Top-level items are ignored.
app.on_menu_event(|_app_handle, event| {
match event.id().0.as_str() {
"new" => println!("New file"),
"open" => println!("Open file"),
"save" => println!("Save file"),
"quit" => std::process::exit(0),
_ => {}
}
});
JavaScript:
const statusItem = await menu.get('status');
if (statusItem) await statusItem.setText('Status: Ready');
Rust:
menu.get("status").unwrap().as_menuitem_unchecked().set_text("Status: Ready")?;
| Shortcut | Accelerator String |
|---|---|
| Ctrl+S / Cmd+S | CmdOrCtrl+S |
| Ctrl+Shift+S | CmdOrCtrl+Shift+S |
| Alt+F4 | Alt+F4 |
| F11 | F11 |
use tauri::menu::{MenuBuilder, SubmenuBuilder};
pub fn run() {
tauri::Builder::default()
.setup(|app| {
let file_menu = SubmenuBuilder::new(app, "File")
.text("new", "New")
.text("open", "Open")
.separator()
.text("quit", "Quit")
.build()?;
let edit_menu = SubmenuBuilder::new(app, "Edit")
.undo().redo().separator().cut().copy().paste()
.build()?;
let menu = MenuBuilder::new(app)
.items(&[&file_menu, &edit_menu])
.build()?;
app.set_menu(menu)?;
Ok(())
})
.on_menu_event(|_app, event| {
match event.id().0.as_str() {
"quit" => std::process::exit(0),
id => println!("Menu event: {}", id),
}
})
.run(tauri::generate_context!())
.expect("error while running tauri application")
}
import { useEffect } from 'react';
import { getCurrentWindow } from '@tauri-apps/api/window';
function App() {
useEffect(() => {
const appWindow = getCurrentWindow();
const minimize = () => appWindow.minimize();
const maximize = () => appWindow.toggleMaximize();
const close = () => appWindow.close();
document.getElementById('titlebar-minimize')?.addEventListener('click', minimize);
document.getElementById('titlebar-maximize')?.addEventListener('click', maximize);
document.getElementById('titlebar-close')?.addEventListener('click', close);
return () => {
document.getElementById('titlebar-minimize')?.removeEventListener('click', minimize);
document.getElementById('titlebar-maximize')?.removeEventListener('click', maximize);
document.getElementById('titlebar-close')?.removeEventListener('click', close);
};
}, []);
return (
<>
<div className="titlebar">
<div className="titlebar-drag" data-tauri-drag-region>
<span>My Tauri App</span>
</div>
<div className="titlebar-controls">
<button id="titlebar-minimize">-</button>
<button id="titlebar-maximize">[]</button>
<button id="titlebar-close">x</button>
</div>
</div>
<main className="content">
<h1>Welcome to Tauri</h1>
</main>
</>
);
}
export default App;
Weekly Installs
44
Repository
GitHub Stars
10
First Seen
Jan 24, 2026
Security Audits
Gen Agent Trust HubFailSocketPassSnykPass
Installed on
gemini-cli37
opencode36
codex36
cursor31
github-copilot31
claude-code30
Rust 开发模式与最佳实践:构建安全高性能应用程序的惯用模式指南
1,300 周安装
Sanity内容建模最佳实践:结构化内容设计原则与无头CMS指南
1,400 周安装
Chrome CDP 命令行工具:轻量级浏览器自动化,支持截图、执行JS、无障碍快照
1,300 周安装
template-skill 技能模板:快速构建AI技能,含触发词、输入输出与工作流
1,300 周安装
TanStack Start 全栈开发:基于 Cloudflare 的 React 19 + D1 数据库应用构建指南
1,300 周安装
Doublecheck AI 内容验证工具 - GitHub Copilot 三层事实核查流程,自动识别幻觉风险
1,400 周安装
Sentry问题修复指南:利用AI分析生产环境错误并自动修复
1,300 周安装