Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
8d04f68
feat: Implement splash screen with loading state and progress updates
hllshiro Aug 26, 2025
6367f1d
perf: optimize Presenter initialization logic
hllshiro Aug 27, 2025
a15f5b8
perf: streamlined design
hllshiro Aug 27, 2025
ef71a83
refactor(llmProvider): 替换 ConfigPresenter 类型为接口 IConfigPresenter
hllshiro Aug 27, 2025
5a51bb8
refactor(presenter): 使用接口替代具体实现简化初始化逻辑
hllshiro Aug 27, 2025
0b54032
refactor(lifecycle): 优化生命周期管理和核心钩子注册逻辑
hllshiro Aug 27, 2025
6e2a6b9
fix(lifecycle): 修复启动页加载时延时判断逻辑
hllshiro Aug 27, 2025
e55ad12
refactor(test): adjust import paths for lifecycle tests
hllshiro Aug 27, 2025
0e44ce9
refactor(lifecycle): 优化生命周期管理模块的事件通知和日志输出
hllshiro Aug 27, 2025
b10f28b
feat(docs): add lifecycle documentation and update existing docs
hllshiro Aug 27, 2025
f134946
fix(splash): fix index.html resource loading
hllshiro Aug 27, 2025
bdf3858
Merge branch 'dev' into refactor/app-lifecycle-management
hllshiro Aug 28, 2025
d1d8175
fix: reverting accidental edits
hllshiro Aug 28, 2025
3c12bc4
fix: reverting accidental edits
hllshiro Aug 28, 2025
b47d90c
perf: Harden BrowserWindow security
hllshiro Aug 28, 2025
3b6b656
fix: Self-import causes redeclaration
hllshiro Aug 28, 2025
bc5a0d4
docs: Clarify the hook registration process
hllshiro Aug 28, 2025
e9fdd26
fix: Rename HOOK_COMPUTED to HOOK_COMPLETED across the codebase
hllshiro Aug 28, 2025
0dacbf6
fix: Fix switch fallthrough and satisfy linter
hllshiro Aug 28, 2025
60375b2
refactor(lifecycle): 重构生命周期管理逻辑,简化错误处理和事件通知
hllshiro Aug 28, 2025
1d977a0
Merge branch 'refactor/app-lifecycle-management' of github.com:ThinkI…
hllshiro Aug 28, 2025
a25103f
refactor(lifecycle): simplify app shutdown logic and remove lifecycle…
hllshiro Aug 28, 2025
01a3707
refactor(main): 优化应用退出逻辑及相关状态管理
hllshiro Aug 28, 2025
e394116
Merge remote-tracking branch 'upstream/dev' into refactor/app-lifecyc…
hllshiro Aug 28, 2025
94a7834
fix: Replace ConfigPresenter Instance with IConfigPresenter type
hllshiro Aug 28, 2025
5d92205
perf: propagate critical hook error
hllshiro Aug 28, 2025
124ad86
fix: wrong comments
hllshiro Aug 28, 2025
0c735e1
perf: remove unused event in splash progress update
hllshiro Aug 28, 2025
cff6e79
perf: remove unused fn
hllshiro Aug 28, 2025
ff21e7c
docs: update doc
hllshiro Aug 28, 2025
983be45
fix: add ignored windowQuittingHook
hllshiro Aug 28, 2025
dbc38a0
chore: Merge branch 'dev' into refactor/app-lifecycle-management
zerob13 Aug 28, 2025
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
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ VITE_GITHUB_CLIENT_ID=Iv1.b507a08c87ecfe98
VITE_GITHUB_CLIENT_SECRET=your_github_client_secret_here
VITE_GITHUB_REDIRECT_URI=https://deepchatai.cn/auth/github/callback
VITE_LOG_IPC_CALL=0

VITE_APP_LIFECYCLE_HOOK_DELAY=0
96 changes: 96 additions & 0 deletions docs/app-lifecycle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@

# App Lifecycle Management

The application's lifecycle is managed by the `LifecycleManager` class, which implements the `ILifecycleManager` interface. It provides a structured and extensible way to control the application's startup, shutdown, and other lifecycle events.

## Overview

The `LifecycleManager` orchestrates the application's lifecycle through a series of phases. For each phase, it executes a set of registered hooks. This allows for a modular and decoupled architecture, where different parts of the application can hook into the lifecycle to perform their own initialization and cleanup tasks.

## Lifecycle Phases

The application lifecycle is divided into the following phases:

- **`INIT`**: This is the first phase of the application lifecycle. It is used for initializing essential services and configurations that are required before the application starts.
- **`BEFORE_START`**: This phase is executed before the main application window is created. It is used for tasks that need to be performed before the UI is displayed.
- **`READY`**: This phase is executed after the main application window is created and the application is ready to receive user input. It is used for tasks that require the UI to be present. **Presenter will be init first in this phase.**
- **`AFTER_START`**: This phase is executed after the application has fully started and is visible to the user. It is used for tasks that can be performed in the background, such as checking for new messages or syncing data.
- **`BEFORE_QUIT`**: This phase is executed when the application is about to quit. It can be used to perform cleanup tasks or to prevent the application from quitting (e.g., by showing a confirmation dialog).

## Lifecycle Hooks

A lifecycle hook is a function that is executed at a specific lifecycle phase. Hooks are registered with the `LifecycleManager` and are executed in order of their priority.

Each hook is defined as an object with the following properties:

- **`name`**: A unique name for the hook.
- **`phase`**: The lifecycle phase at which the hook should be executed.
- **`priority`**: A number that determines the order in which the hooks are executed. Lower numbers are executed first.
- **`critical`**: A boolean that indicates whether the hook is critical. If a critical hook fails during startup, the application will quit. During shutdown, a critical hook failure will be logged, but the shutdown process will continue.
- **`execute`**: A function that contains the logic of the hook. It receives a `LifecycleContext` object as its only argument.

### Creating a Hook

To create a new lifecycle hook, create a new file in the `src/main/presenter/lifecyclePresenter/hooks` directory and export a `LifecycleHook` object.

For example, the following hook initializes the configuration service in the `INIT` phase:

```typescript
// src/main/presenter/lifecyclePresenter/hooks/configInitHook.ts
import { LifecycleHook, LifecyclePhase } from '@shared/lifecycle';

export const configInitHook: LifecycleHook = {
name: 'config-init',
phase: LifecyclePhase.INIT,
priority: 10,
critical: true,
execute: async (context) => {
// Initialize the configuration service
// ...
},
};
```

### Registering a Hook

To register a hook, add it to the `src/main/presenter/lifecyclePresenter/hooks/index.ts` file. The hooks are then registered with the `LifecycleManager` via the `registerCoreHooks()` function during application startup.

## LifecycleManager

The `LifecycleManager` is the central class that manages the application's lifecycle. It is responsible for:

- Registering and unregistering lifecycle hooks.
- Executing the lifecycle phases in the correct order.
- Executing the registered hooks for each phase.
- Managing the splash screen.
- Handling application shutdown.

The `LifecycleManager` is initialized in the `src/main/index.ts` file.

## Splash Screen

The `LifecycleManager` displays a splash screen during the startup phases to provide feedback to the user. The splash screen shows the current lifecycle phase and the progress of the startup process.

The splash screen is implemented in the `src/renderer/splash` directory.

## Shutdown

The `LifecycleManager` intercepts the `before-quit` event from Electron to allow registered hooks to perform cleanup or even prevent the application from quitting.

To initiate a graceful shutdown, call `app.quit()`. The `LifecycleManager` will then execute all hooks registered for the `BEFORE_QUIT` phase. If any of these hooks return `false`, the shutdown process will be aborted.

If the shutdown is not aborted, the application terminates.

Forceful termination of the application (e.g., via task manager or system shutdown) is not controlled by the lifecycle manager and may prevent cleanup hooks from running.

## Lifecycle Events

The `LifecycleManager` emits a series of events to the `eventBus` that allow other parts of the application to monitor the lifecycle progress. These events are sent to both the main and renderer processes.

- `LIFECYCLE_EVENTS.PHASE_STARTED`: Dispatched when a new lifecycle phase begins.
- `LIFECYCLE_EVENTS.PHASE_COMPLETED`: Dispatched when a lifecycle phase completes.
- `LIFECYCLE_EVENTS.HOOK_EXECUTED`: Dispatched when a hook begins execution.
- `LIFECYCLE_EVENTS.HOOK_COMPLETED`: Dispatched when a hook successfully completes.
- `LIFECYCLE_EVENTS.HOOK_FAILED`: Dispatched when a hook fails.
- `LIFECYCLE_EVENTS.ERROR_OCCURRED`: Dispatched when a critical error occurs.
- `LIFECYCLE_EVENTS.PROGRESS_UPDATED`: Dispatched to update the progress on the splash screen.
31 changes: 19 additions & 12 deletions docs/deepchat-architecture-overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,20 +142,27 @@ graph TB
sequenceDiagram
participant App as Electron App
participant Main as 主进程
participant Presenters as Presenter层
participant Window as 窗口管理
participant Shell as 窗口外壳
participant Content as 内容标签页
participant LifecycleManager as 生命周期管理器
participant Hooks as 生命周期钩子
participant SplashWindow as 闪屏窗口
participant MainWindow as 主窗口

App->>Main: app.whenReady()
Main->>Presenters: 初始化所有Presenter
Presenters->>Presenters: 注册事件监听器
Main->>Window: createWindow()
Window->>Shell: 加载 shell/index.html
Shell->>Content: 创建首个标签页
Content->>Content: 加载主应用 src/index.html

Note over Main,Content: 应用就绪,开始处理用户交互
Main->>LifecycleManager: new LifecycleManager()
Main->>LifecycleManager: registerCoreHooks()
Main->>LifecycleManager: start()
LifecycleManager->>SplashWindow: create()
LifecycleManager->>Hooks: execute(INIT)
Hooks-->>LifecycleManager: done
LifecycleManager->>Hooks: execute(BEFORE_START)
Hooks-->>LifecycleManager: done
LifecycleManager->>Hooks: execute(READY)
Hooks->>MainWindow: createWindow()
Hooks-->>LifecycleManager: done
LifecycleManager->>Hooks: execute(AFTER_START)
Hooks-->>LifecycleManager: done
LifecycleManager->>SplashWindow: close()
Note over Main,MainWindow: 应用就绪,开始处理用户交互
```

### 2. 多窗口标签页管理
Expand Down
2 changes: 1 addition & 1 deletion docs/developer-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ For more details, refer to the "Project Structure" section in [CONTRIBUTING.md](
### Electron Architecture

DeepChat is an Electron application. This means it has:
- **Main Process**: A single process that is the entry point of the application. It runs Node.js and has access to system-level operations. It's responsible for creating and managing application windows (renderer processes) and handling application lifecycle events. The code for the main process is in `src/main/`.
- **Main Process**: A single process that is the entry point of the application. It runs Node.js and has access to system-level operations. It's responsible for creating and managing application windows (renderer processes) and handling application lifecycle events. The code for the main process is in `src/main/`. For a detailed explanation of the application lifecycle, see the [App Lifecycle Management](./app-lifecycle.md) document.
- **Renderer Processes**: Each window in DeepChat runs its own renderer process. This process is responsible for rendering web content (HTML, CSS, JavaScript). The UI is built using web technologies. The code for the renderer process is in `src/renderer/`.
- **Preload Scripts**: These scripts run in a privileged context in the renderer process and can expose specific Node.js APIs or main process functionalities to the renderer process via an IPC (Inter-Process Communication) bridge. See `src/preload/`.
- **Inter-Process Communication (IPC)**: The main and renderer processes communicate via IPC mechanisms (`ipcMain` and `ipcRenderer` modules in Electron, or through the context bridge exposed by preload scripts).
Expand Down
12 changes: 7 additions & 5 deletions docs/mcp-architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,16 +148,18 @@ classDiagram

```mermaid
sequenceDiagram
participant AppStartup
participant LifecycleManager as 生命周期管理器
participant PresenterInitHook as Presenter初始化钩子
participant McpPresenter
participant ServerManager
participant IConfigPresenter
participant McpClient

AppStartup->>McpPresenter: constructor(configPresenter)
McpPresenter->>ServerManager: constructor(configPresenter)
McpPresenter->>ToolManager: constructor(configPresenter, serverManager)
AppStartup->>McpPresenter: initialize()
LifecycleManager->>PresenterInitHook: execute()
PresenterInitHook->>McpPresenter: new McpPresenter(configPresenter)
McpPresenter->>ServerManager: new ServerManager(configPresenter)
McpPresenter->>ToolManager: new ToolManager(configPresenter, serverManager)
PresenterInitHook->>McpPresenter: initialize()
McpPresenter->>IConfigPresenter: getMcpServers()
McpPresenter->>IConfigPresenter: getMcpDefaultServers()
McpPresenter->>ServerManager: testNpmRegistrySpeed()
Expand Down
6 changes: 4 additions & 2 deletions electron.vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ export default defineConfig({
rollupOptions: {
input: {
index: resolve('src/preload/index.ts'),
floating: resolve('src/preload/floating-preload.ts')
floating: resolve('src/preload/floating-preload.ts'),
splash: resolve('src/preload/splash.ts')
}
}
}
Expand Down Expand Up @@ -91,7 +92,8 @@ export default defineConfig({
input: {
shell: resolve('src/renderer/shell/index.html'),
index: resolve('src/renderer/index.html'),
floating: resolve('src/renderer/floating/index.html')
floating: resolve('src/renderer/floating/index.html'),
splash: resolve('src/renderer/splash/index.html')
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion scripts/rebrand.js
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,8 @@ function updateHtmlTitles(config) {
const htmlFiles = [
'src/renderer/index.html',
'src/renderer/shell/index.html',
'src/renderer/floating/index.html'
'src/renderer/floating/index.html',
'src/renderer/splash/index.html'
]

let updatedCount = 0
Expand Down
12 changes: 12 additions & 0 deletions src/main/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,3 +199,15 @@ export const RAG_EVENTS = {
FILE_UPDATED: 'rag:file-updated', // File status update
FILE_PROGRESS: 'rag:file-progress' // File processing progress update
}

// Lifecycle management events
export const LIFECYCLE_EVENTS = {
PHASE_STARTED: 'lifecycle:phase-started', // Lifecycle phase started
PHASE_COMPLETED: 'lifecycle:phase-completed', // Lifecycle phase completed
HOOK_EXECUTED: 'lifecycle:hook-executed', // Lifecycle hook executed start
HOOK_COMPLETED: 'lifecycle:hook-completed', // Lifecycle hook executed completed
HOOK_FAILED: 'lifecycle:hook-failed', // Lifecycle hook executed failed
ERROR_OCCURRED: 'lifecycle:error-occurred', // Lifecycle error occurred
PROGRESS_UPDATED: 'lifecycle:progress-updated', // Lifecycle progress updated
SHUTDOWN_REQUESTED: 'lifecycle:shutdown-requested' // Application shutdown requested
}
Loading