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
15 changes: 15 additions & 0 deletions docs/issues/mac-app-name-identity/plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# macOS App Name Identity Plan

## Implementation

- Update the main-process startup path in `src/main/appMain.ts` to set the Electron application name to
`DeepChat` before the app creates windows or menus.
- Ensure the macOS process advertises itself as a regular foreground app and reveals its Dock identity
before startup windows attempt to take focus.
- Keep the change scoped to startup identity only, avoiding any unrelated menu, dock, or window policy
changes unless verification shows they are required.

## Validation

- Run a node-side typecheck or equivalent narrow validation for the touched startup file.
- Run repository-required format, i18n, and lint checks before handoff.
20 changes: 20 additions & 0 deletions docs/issues/mac-app-name-identity/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# macOS App Name Identity Spec

## Goal

On macOS, DeepChat should identify itself as the active application when its window is focused,
so the menu bar shows DeepChat instead of Finder or the generic Electron host identity.

## Acceptance Criteria

- DeepChat sets its user-visible application name during main-process startup before windows are created.
- DeepChat declares a regular foreground macOS activation policy before startup windows are shown.
- When a DeepChat window becomes the active foreground app on macOS, the menu bar app label resolves to
DeepChat rather than Finder.
- The change does not alter Windows or Linux startup behavior.

## Non-Goals

- No app icon, bundle identifier, or code-signing changes.
- No renderer UI changes.
- No shortcut or menu structure refactor.
6 changes: 6 additions & 0 deletions docs/issues/mac-app-name-identity/tasks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# macOS App Name Identity Tasks

- [x] Document the bug, implementation scope, and validation plan.
- [x] Set the macOS-visible app name during main-process startup.
- [x] Run focused validation for the startup change.
- [x] Run required format, i18n, and lint checks.
15 changes: 15 additions & 0 deletions src/main/appMain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,28 @@ import {
storeStartupDeepLink
} from './lib/startupDeepLink'
import { isInsecureTlsAllowed } from './lib/insecureTls'
import { activateAppOnMac, ensureRegularAppOnMac } from './lib/activateApp'

let appStarted = false
const APP_NAME = 'DeepChat'

export function startApp(): void {
if (appStarted) {
return
}
appStarted = true

app.setName(APP_NAME)
if (process.platform === 'darwin') {
if (app.isReady()) {
ensureRegularAppOnMac()
} else {
app.once('ready', () => {
ensureRegularAppOnMac()
})
}
}

registerWorkspacePreviewSchemes()

// Handle unhandled exceptions to prevent app crash or error dialogs
Expand Down Expand Up @@ -105,6 +118,7 @@ export function startApp(): void {
}
targetWindow.show()
targetWindow.focus()
activateAppOnMac()
}

const routeIncomingDeeplink = (url: string, source: string) => {
Expand Down Expand Up @@ -157,6 +171,7 @@ export function startApp(): void {

// Start the lifecycle management system instead of using app.whenReady()
app.whenReady().then(async () => {
ensureRegularAppOnMac()
// Set app user model id for windows
electronApp.setAppUserModelId('com.wefonk.deepchat')
try {
Expand Down
19 changes: 19 additions & 0 deletions src/main/lib/activateApp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { app } from 'electron'

export function ensureRegularAppOnMac(): void {
if (process.platform !== 'darwin') {
return
}

app.setActivationPolicy('regular')
app.dock?.show()
}

export function activateAppOnMac(): void {
if (process.platform !== 'darwin') {
return
}

ensureRegularAppOnMac()
app.focus({ steal: true })
}
2 changes: 2 additions & 0 deletions src/main/presenter/lifecyclePresenter/SplashWindowManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
type DatabaseUnlockRequestPayload,
type DatabaseUnlockReason
} from '@shared/contracts/databaseSecurity'
import { activateAppOnMac } from '@/lib/activateApp'

type SplashActivityStatus = 'running' | 'completed' | 'failed'

Expand Down Expand Up @@ -483,6 +484,7 @@ export class SplashWindowManager implements ISplashWindowManager {
}
this.splashWindow.show()
this.splashWindow.focus()
activateAppOnMac()
}

private markSplashLoaded(): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { WINDOW_EVENTS, TRAY_EVENTS, FLOATING_BUTTON_EVENTS, SETTINGS_EVENTS } f
import { handleShowHiddenWindow } from '@/utils'
import { presenter } from '@/presenter'
import { LifecyclePhase } from '@shared/lifecycle'
import { activateAppOnMac } from '@/lib/activateApp'

export const eventListenerSetupHook: LifecycleHook = {
name: 'event-listener-setup',
Expand Down Expand Up @@ -45,6 +46,7 @@ export const eventListenerSetupHook: LifecycleHook = {
if (!targetWindow.isDestroyed()) {
targetWindow.show()
targetWindow.focus() // Ensure window gets focus
activateAppOnMac()
} else {
console.warn(
'eventListenerSetupHook: App activated but target window is destroyed, creating new window.'
Expand Down
6 changes: 6 additions & 0 deletions src/main/presenter/windowPresenter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { FloatingChatWindow } from './FloatingChatWindow' // Floating chat windo
import type { ProviderInstallPreview } from '@shared/providerDeeplink'
import { StartupWorkloadCoordinator } from '../startupWorkloadCoordinator'
import { openExternalUrl } from '@/lib/externalUrl'
import { activateAppOnMac } from '@/lib/activateApp'

type PendingSettingsMessage = {
channel: string
Expand Down Expand Up @@ -357,6 +358,7 @@ export class WindowPresenter implements IWindowPresenter {
targetWindow.show()
if (shouldFocus) {
targetWindow.focus() // Bring to foreground
activateAppOnMac()
}
// 触发恢复逻辑以确保活动标签页可见且位置正确
this.handleWindowRestore(targetWindow.id).catch((error) => {
Expand Down Expand Up @@ -529,6 +531,7 @@ export class WindowPresenter implements IWindowPresenter {
if (switchToTarget) {
targetWindow.show()
targetWindow.focus()
activateAppOnMac()
}

return true
Expand Down Expand Up @@ -699,6 +702,7 @@ export class WindowPresenter implements IWindowPresenter {
if (!appWindow.isDestroyed()) {
appWindow.show()
appWindow.focus()
activateAppOnMac()
eventBus.sendToMain(WINDOW_EVENTS.WINDOW_CREATED, {
windowId,
isMainWindow: windowId === this.mainWindowId
Expand Down Expand Up @@ -1231,6 +1235,7 @@ export class WindowPresenter implements IWindowPresenter {
console.log('Settings window already exists, showing and focusing.')
this.settingsWindow.show()
this.settingsWindow.focus()
activateAppOnMac()
if (navigation) {
if (this.settingsWindowReady) {
this.sendToWindow(this.settingsWindow.id, SETTINGS_EVENTS.NAVIGATE, navigation)
Expand Down Expand Up @@ -1407,6 +1412,7 @@ export class WindowPresenter implements IWindowPresenter {

mainWindow.show()
mainWindow.focus()
activateAppOnMac()
return true
}

Expand Down