mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-16 13:03:31 +08:00
feat(desktop): route macOS native About menu to in-app Settings page
Replace the default macOS about dialog with a custom menu that emits a Tauri event, navigating to the Settings > About tab within the React app. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
bd604429ec
commit
301e0c3a33
@ -6,7 +6,10 @@ use std::{
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use tauri::{AppHandle, Manager, RunEvent, State};
|
||||
use tauri::{
|
||||
menu::{MenuBuilder, MenuItemBuilder, SubmenuBuilder},
|
||||
AppHandle, Emitter, Manager, RunEvent, State,
|
||||
};
|
||||
use tauri_plugin_shell::{
|
||||
process::{CommandChild, CommandEvent},
|
||||
ShellExt,
|
||||
@ -302,6 +305,63 @@ pub fn run() {
|
||||
get_server_url,
|
||||
restart_adapters_sidecar
|
||||
])
|
||||
.menu(|app| {
|
||||
let about_item = MenuItemBuilder::with_id("nav_about", "About Claude Code Haha")
|
||||
.build(app)?;
|
||||
let settings_item = MenuItemBuilder::with_id("nav_settings", "Settings...")
|
||||
.accelerator("CmdOrCtrl+,")
|
||||
.build(app)?;
|
||||
|
||||
let app_submenu = SubmenuBuilder::new(app, "Claude Code Haha")
|
||||
.item(&about_item)
|
||||
.separator()
|
||||
.item(&settings_item)
|
||||
.separator()
|
||||
.services()
|
||||
.separator()
|
||||
.hide()
|
||||
.hide_others()
|
||||
.show_all()
|
||||
.separator()
|
||||
.quit()
|
||||
.build()?;
|
||||
|
||||
let edit_submenu = SubmenuBuilder::new(app, "Edit")
|
||||
.undo()
|
||||
.redo()
|
||||
.separator()
|
||||
.cut()
|
||||
.copy()
|
||||
.paste()
|
||||
.select_all()
|
||||
.build()?;
|
||||
|
||||
let view_submenu = SubmenuBuilder::new(app, "View")
|
||||
.fullscreen()
|
||||
.build()?;
|
||||
|
||||
let window_submenu = SubmenuBuilder::new(app, "Window")
|
||||
.minimize()
|
||||
.maximize()
|
||||
.close_window()
|
||||
.build()?;
|
||||
|
||||
MenuBuilder::new(app)
|
||||
.item(&app_submenu)
|
||||
.item(&edit_submenu)
|
||||
.item(&view_submenu)
|
||||
.item(&window_submenu)
|
||||
.build()
|
||||
})
|
||||
.on_menu_event(|app, event| match event.id().as_ref() {
|
||||
"nav_about" => {
|
||||
let _ = app.emit("native-menu-navigate", "about");
|
||||
}
|
||||
"nav_settings" => {
|
||||
let _ = app.emit("native-menu-navigate", "settings");
|
||||
}
|
||||
_ => {}
|
||||
})
|
||||
.setup(|app| {
|
||||
let state = app.state::<ServerState>();
|
||||
let mut guard = state
|
||||
|
||||
@ -4,10 +4,11 @@ import { ContentRouter } from './ContentRouter'
|
||||
import { ToastContainer } from '../shared/Toast'
|
||||
import { UpdateChecker } from '../shared/UpdateChecker'
|
||||
import { useSettingsStore } from '../../stores/settingsStore'
|
||||
import { useUIStore, type SettingsTab } from '../../stores/uiStore'
|
||||
import { useKeyboardShortcuts } from '../../hooks/useKeyboardShortcuts'
|
||||
import { initializeDesktopServerUrl } from '../../lib/desktopRuntime'
|
||||
import { TabBar } from './TabBar'
|
||||
import { useTabStore } from '../../stores/tabStore'
|
||||
import { useTabStore, SETTINGS_TAB_ID } from '../../stores/tabStore'
|
||||
import { useChatStore } from '../../stores/chatStore'
|
||||
|
||||
export function AppShell() {
|
||||
@ -46,6 +47,24 @@ export function AppShell() {
|
||||
}
|
||||
}, [fetchSettings])
|
||||
|
||||
// Listen for macOS native menu navigation events (About / Settings)
|
||||
useEffect(() => {
|
||||
let unlisten: (() => void) | undefined
|
||||
import(/* @vite-ignore */ '@tauri-apps/api/event')
|
||||
.then(({ listen }) =>
|
||||
listen<string>('native-menu-navigate', (event) => {
|
||||
const target = event.payload as SettingsTab | 'settings'
|
||||
if (target === 'about') {
|
||||
useUIStore.getState().setPendingSettingsTab('about')
|
||||
}
|
||||
useTabStore.getState().openTab(SETTINGS_TAB_ID, 'Settings', 'settings')
|
||||
}),
|
||||
)
|
||||
.then((fn) => { unlisten = fn })
|
||||
.catch(() => {})
|
||||
return () => { unlisten?.() }
|
||||
}, [])
|
||||
|
||||
useKeyboardShortcuts()
|
||||
|
||||
if (startupError) {
|
||||
|
||||
@ -18,13 +18,19 @@ import { MarkdownRenderer } from '../components/markdown/MarkdownRenderer'
|
||||
import { useSkillStore } from '../stores/skillStore'
|
||||
import { SkillList } from '../components/skills/SkillList'
|
||||
import { SkillDetail } from '../components/skills/SkillDetail'
|
||||
|
||||
type SettingsTab = 'providers' | 'permissions' | 'general' | 'adapters' | 'agents' | 'skills' | 'about'
|
||||
import { useUIStore, type SettingsTab } from '../stores/uiStore'
|
||||
|
||||
export function Settings() {
|
||||
const [activeTab, setActiveTab] = useState<SettingsTab>('providers')
|
||||
const pendingSettingsTab = useUIStore((s) => s.pendingSettingsTab)
|
||||
const t = useTranslation()
|
||||
|
||||
useEffect(() => {
|
||||
if (!pendingSettingsTab) return
|
||||
setActiveTab(pendingSettingsTab)
|
||||
useUIStore.getState().setPendingSettingsTab(null)
|
||||
}, [pendingSettingsTab])
|
||||
|
||||
return (
|
||||
<div className="flex-1 flex flex-col overflow-hidden bg-[var(--color-surface)]">
|
||||
<div className="flex-1 flex overflow-hidden">
|
||||
|
||||
@ -7,12 +7,15 @@ export type Toast = {
|
||||
duration?: number
|
||||
}
|
||||
|
||||
export type SettingsTab = 'providers' | 'permissions' | 'general' | 'adapters' | 'agents' | 'skills' | 'about'
|
||||
|
||||
type ActiveView = 'code' | 'scheduled' | 'terminal' | 'history' | 'settings'
|
||||
|
||||
type UIStore = {
|
||||
theme: 'light' | 'dark'
|
||||
sidebarOpen: boolean
|
||||
activeView: ActiveView
|
||||
pendingSettingsTab: SettingsTab | null
|
||||
activeModal: string | null
|
||||
toasts: Toast[]
|
||||
|
||||
@ -21,6 +24,7 @@ type UIStore = {
|
||||
toggleSidebar: () => void
|
||||
setSidebarOpen: (open: boolean) => void
|
||||
setActiveView: (view: ActiveView) => void
|
||||
setPendingSettingsTab: (tab: SettingsTab | null) => void
|
||||
openModal: (id: string) => void
|
||||
closeModal: () => void
|
||||
addToast: (toast: Omit<Toast, 'id'>) => void
|
||||
@ -33,6 +37,7 @@ export const useUIStore = create<UIStore>((set) => ({
|
||||
theme: 'light',
|
||||
sidebarOpen: true,
|
||||
activeView: 'code',
|
||||
pendingSettingsTab: null,
|
||||
activeModal: null,
|
||||
toasts: [],
|
||||
|
||||
@ -52,6 +57,7 @@ export const useUIStore = create<UIStore>((set) => ({
|
||||
toggleSidebar: () => set((s) => ({ sidebarOpen: !s.sidebarOpen })),
|
||||
setSidebarOpen: (open) => set({ sidebarOpen: open }),
|
||||
setActiveView: (view) => set({ activeView: view }),
|
||||
setPendingSettingsTab: (tab) => set({ pendingSettingsTab: tab }),
|
||||
openModal: (id) => set({ activeModal: id }),
|
||||
closeModal: () => set({ activeModal: null }),
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user