-
Notifications
You must be signed in to change notification settings - Fork 614
Refactor/app lifecycle management #803
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- Added preload script to expose Electron APIs to the renderer. - Created a new HTML structure for the splash screen. - Developed a Vue component for loading animation and progress display. - Integrated progress updates from the main process to the splash screen. - Implemented database initialization and error handling in lifecycle hooks. - Added unit tests for lifecycle management, error handling, and splash window management.
- 将所有 provider 及 presenter 中 ConfigPresenter 类型替换为接口 IConfigPresenter - 去除多余的 ConfigPresenter 导入,统一使用共享接口定义 - 保持构造函数参数及成员变量类型一致,增强依赖注入灵活性 - 更新相关模块导入声明,确保类型引用正确 - 改进代码解耦,提高模块间接口兼容性及维护性
- 移除 eventBus 的数据库就绪监听,改为直接在生命周期启动后初始化 Presenter - Presenter 构造函数移除数据库参数,改用生命周期管理器提供上下文获取相关实例 - Presenter 内部成员均替换为对应接口类型,提升抽象层次和可维护性 - 修改FloatingButtonPresenter、ShortcutPresenter构造函数,依赖改为接口类型的 configPresenter - presenter.getInstance 方法及调用调整为只接收生命周期管理器参数 - presenter.d.ts 中丰富接口定义,新增方法和属性,完善类型声明 - lifecycleManager 生命周期钩子注册接口简化,去除阶段参数,统一管理钩子标识 - 移除部分过时的数据库初始化和依赖管理代码,简化启动流程
- 删除 DatabaseInitializer 中不必要的事件触发,简化数据库初始化过程 - LifecycleManager 中移除生命周期钩子注册的 phase 参数,强化钩子管理一致性 - 修改 LifecycleManager 的 unregisterHook 方法,提高钩子注销的灵活性和健壮性 - 在生命周期 READY 阶段之后初始化 presenter,并保证事件发送到渲染进程的时机正确 - 将核心钩子统一通过遍历方式注册,简化 registerCoreHooks 函数实现 - 各初始化钩子添加 LifecyclePhase 阶段和优先级标识,实现更明确的生命周期管理 - configInitHook 中改用 ConfigPresenter 实例代替全局 presenter,确保配置初始化正确独立 - 新增 splashHook 实现启动界面等待时间,可通过环境变量配置等待时长 - 各钩子统一引入并声明所属阶段,增强代码规范和阶段控制能力 - 在 .env.example 中新增 VITE_APP_SPLASH_TIMEOUT 配置项,支持启动屏幕自定义等待时间
- 引入 is 工具判断开发环境 - 在非开发环境中将加载页延时设为 0 - 避免生产环境被无意中阻塞启动流程 - 保持开发环境配置的启动页延时生效
- 新增HOOK_COMPUTED生命周期事件以区分钩子执行完成与计算完成 - 统一生命周期事件发送方法为notifyMessage,简化事件广播逻辑 - 删除冗余的eventBus.sendToMain和sendToRenderer调用,改用notifyMessage - 移除splashHook生命周期钩子及相关测试,简化启动流程 - SplashWindowManager中progress更新事件改用统一事件数据结构和发送方式 - 钩子执行时增加延迟配置支持,优化开发环境下日志输出顺序 - 增强生命周期事件监听器,对各事件类型分别输出详细日志信息 - 删除无用的updateMessage方法及其调用,清理相关类型定义 - 移除主线程中冗余的import和测试文件,降低依赖复杂度
WalkthroughIntroduces a lifecycle system (LifecycleManager, phases, hooks), splash-screen manager and renderer, lifecycle-driven Presenter and database initialization, many new lifecycle hooks (init→shutdown), widespread interface/type refactors (IConfigPresenter, lifecycle types), and build/tooling updates to include splash entries. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant Electron as Electron App
participant LM as LifecycleManager
participant Splash as SplashWindow
participant Hooks as Registered Hooks
participant Presenter as Presenter
participant MainWin as MainWindow
Electron->>Electron: whenReady()
Electron->>LM: instantiate & registerCoreHooks()
Electron->>LM: start()
LM->>Splash: create()
rect rgba(200,220,255,0.10)
note over LM,Splash: Sequential startup phases
LM->>Hooks: execute INIT hooks
Splash-->>Splash: progress 0–25
LM->>Hooks: execute BEFORE_START hooks
Splash-->>Splash: progress 25–50
LM->>Hooks: execute READY hooks
Hooks-->>Presenter: presenterInitHook → create Presenter
Splash-->>Splash: progress 50–75
LM->>Hooks: execute AFTER_START hooks
Hooks->>MainWin: create initial window / tray / shortcuts
Splash-->>Splash: progress 75–100
end
LM->>Splash: close()
User->>Electron: Request Quit
Electron->>LM: requestShutdown()
LM->>Hooks: execute BEFORE_QUIT hooks (ordered)
alt All hooks allow quit
LM-->>Electron: permit quit
Electron->>Electron: quit()
else veto or critical failure
LM-->>Electron: cancel quit
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60–90 minutes Possibly related PRs
Poem
✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Optional tweak: feel free to browse the loaders on this page and swap in a cooler-looking one: https://uiverse.io/loaders?page=1
- 删除冗余的 LifecycleErrorHandler,实现错误处理内嵌在 LifecycleManager 中 - 新增生命周期钩子失败事件 HOOK_FAILED,丰富事件类型定义 - 优化生命周期阶段事件通知,简化事件数据结构 - 改进钩子执行逻辑,区分关键钩子和非关键钩子错误处理 - 启用钩子执行开始和完成的事件通知,增加失败事件通知和日志打印 - 移除 SplashWindowManager 中的无效阶段消息,调整启动和关闭流程事件 - 修改启动失败时通过对话框提示错误并允许退出应用 - 简化应用关闭流程,合并钩子失败日志和错误事件处理 - 移除旧有未使用的接口和方法,提升代码整洁性和可维护性 - 将 presenterInitHook 和 windowCreationHook 标记为关键钩子保证执行稳定
…nAIXYZ/deepchat into refactor/app-lifecycle-management
… hooks - 修改主进程入口,替换 lifecycle 导入路径至 presenter 目录 - 调整hooks目录结构,分类存放 - 简化 window-all-closed 事件处理,保留应用关闭逻辑以应对意外情况 - 将多个关闭前事件迁移到hook
- 为 app.quit() 添加注释,明确退出触发原因 - 在生命周期管理器中新增强制退出事件监听,以支持更新时强制退出 - WindowPresenter 中移除重复退出事件监听,改为管理退出状态标志 - 增加 WindowPresenter 的退出状态设置方法和悬浮聊天窗相关接口声明
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
♻️ Duplicate comments (5)
src/main/presenter/lifecyclePresenter/hooks/ready/presenterInitHook.ts (1)
14-21: Add error handling and await async init calls in critical READY phase hook.The READY phase is critical (
critical: true), but the hook doesn't await potentially async init methods or handle errors. This could lead to race conditions or silent failures.Apply this diff to ensure deterministic initialization:
async execute(context: LifecycleContext): Promise<void> { // init presenter console.log('presenterInitHook: Create Presenter Instance') - const presenter = getInstance(context.manager) - presenter.deeplinkPresenter.init() - presenter.init() - context.presenter = presenter + const presenter = getInstance(context.manager) + try { + await Promise.resolve(presenter.deeplinkPresenter.init()) + await Promise.resolve(presenter.init()) + context.presenter = presenter + } catch (err) { + console.error('presenterInitHook: Presenter initialization failed', err) + throw err + } }src/main/presenter/lifecyclePresenter/SplashWindowManager.ts (2)
11-12: Use English for all comments per coding guidelines.Replace Chinese comments with English to comply with the project's language standards.
Apply this diff:
-import icon from '../../../../resources/icon.png?asset' // 应用图标 (macOS/Linux) -import iconWin from '../../../../resources/icon.ico?asset' // 应用图标 (Windows) +import icon from '../../../../resources/icon.png?asset' // App icon (macOS/Linux) +import iconWin from '../../../../resources/icon.ico?asset' // App icon (Windows) - show: false, // 先隐藏窗口,等待 ready-to-show 以避免白屏 + show: false, // Hide window initially, wait for ready-to-show to avoid white flash - // Handle window closed event6 + // Handle window closed eventAlso applies to: 41-41, 62-62
43-43: Use.jsextension for splash preload path.The preload script will be emitted as
splash.jsby the bundler, notsplash.mjs. This will cause an ENOENT error at runtime.Apply this diff:
- preload: path.join(__dirname, '../preload/splash.mjs'), + preload: path.join(__dirname, '../preload/splash.js'),src/shared/presenter.d.ts (1)
236-236: Update aggregator's destroy method to handle Promise-returning TabPresenter.destroy().The
ITabPresenter.destroy()now returnsPromise<void>, but the aggregator'sdestroy()method insrc/main/presenter/index.tsdoesn't await it.In
src/main/presenter/index.ts, update the aggregator implementation:- destroy() { + async destroy(): Promise<void> { ... - this.tabPresenter.destroy() + await this.tabPresenter.destroy() ... }src/main/presenter/lifecyclePresenter/DatabaseInitializer.ts (1)
90-90: Add an idempotent close() for lifecycle shutdownExpose a resilient close() and clear state in finally. Mirrors an earlier review suggestion.
} + + /** + * Close and dispose database handle; idempotent and resilient + */ + close(): void { + if (!this.database) return + try { + this.database.close() + } catch (err) { + console.error('[DatabaseInitializer][WARN] Failed to close database', { + error: err instanceof Error ? { message: err.message, stack: err.stack } : String(err) + }) + } finally { + this.database = undefined + } + } }
🧹 Nitpick comments (3)
src/main/presenter/lifecyclePresenter/DatabaseInitializer.ts (3)
57-71: Migration method is effectively a no-op; tighten logging and typingKeep it, but make logs consistent and catch typed unknown.
- console.log('DatabaseInitializer: Starting database migration') + console.info('[DatabaseInitializer][INFO] Starting database migration') // Migration logic is already handled in SQLitePresenter constructor // This method is here for future migration needs that might be separate - console.log('DatabaseInitializer: Database migration completed') - } catch (error) { - console.error('DatabaseInitializer: Database migration failed:', error) + console.info('[DatabaseInitializer][INFO] Database migration completed') + } catch (error: unknown) { + console.error('[DatabaseInitializer][ERROR] Database migration failed', { + error: error instanceof Error ? { message: error.message, stack: error.stack } : String(error) + }) throw error }
76-89: Don’t silently swallow details in validation; keep typed catch and structured contextReturning boolean is fine, but keep rich error context.
- } catch (error) { - console.error('DatabaseInitializer: Connection validation failed:', error) + } catch (error: unknown) { + console.error('[DatabaseInitializer][ERROR] Connection validation failed', { + error: error instanceof Error ? { message: error.message, stack: error.stack } : String(error) + }) return false }
1-3: Use namespace import for ‘path’ and import ISQLitePresenter from the shared moduleFile: src/main/presenter/lifecyclePresenter/DatabaseInitializer.ts (lines 1–3) -import path from 'path' +import * as path from 'path' -import { SQLitePresenter } from '@/presenter/sqlitePresenter' +import { ISQLitePresenter } from '@shared/presenter' +import { SQLitePresenter } from '@/presenter/sqlitePresenter'
- Avoid default imports for Node built-ins (esModuleInterop is unset).
- Import ISQLitePresenter from '@shared/presenter' to depend on the interface rather than the concrete class.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (14)
src/main/presenter/lifecyclePresenter/DatabaseInitializer.ts(1 hunks)src/main/presenter/lifecyclePresenter/SplashWindowManager.ts(1 hunks)src/main/presenter/lifecyclePresenter/hooks/after-start/windowCreationHook.ts(1 hunks)src/main/presenter/lifecyclePresenter/hooks/beforeQuit/builtinKnowledgeDestroyHook.ts(1 hunks)src/main/presenter/lifecyclePresenter/hooks/beforeQuit/floatingDestroyHook.ts(1 hunks)src/main/presenter/lifecyclePresenter/hooks/beforeQuit/presenterDestroyHook.ts(1 hunks)src/main/presenter/lifecyclePresenter/hooks/beforeQuit/trayDestroyHook.ts(1 hunks)src/main/presenter/lifecyclePresenter/hooks/beforeQuit/windowQuittingHook.ts(1 hunks)src/main/presenter/lifecyclePresenter/hooks/init/databaseInitHook.ts(1 hunks)src/main/presenter/lifecyclePresenter/hooks/ready/presenterInitHook.ts(1 hunks)src/main/presenter/llmProviderPresenter/index.ts(3 hunks)src/main/presenter/llmProviderPresenter/providers/tokenfluxProvider.ts(2 hunks)src/main/presenter/windowPresenter/index.ts(4 hunks)src/shared/presenter.d.ts(10 hunks)
🚧 Files skipped from review as they are similar to previous changes (8)
- src/main/presenter/lifecyclePresenter/hooks/beforeQuit/builtinKnowledgeDestroyHook.ts
- src/main/presenter/lifecyclePresenter/hooks/beforeQuit/presenterDestroyHook.ts
- src/main/presenter/lifecyclePresenter/hooks/after-start/windowCreationHook.ts
- src/main/presenter/lifecyclePresenter/hooks/init/databaseInitHook.ts
- src/main/presenter/lifecyclePresenter/hooks/beforeQuit/trayDestroyHook.ts
- src/main/presenter/lifecyclePresenter/hooks/beforeQuit/floatingDestroyHook.ts
- src/main/presenter/llmProviderPresenter/index.ts
- src/main/presenter/lifecyclePresenter/hooks/beforeQuit/windowQuittingHook.ts
🧰 Additional context used
📓 Path-based instructions (11)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development-setup.mdc)
**/*.{js,jsx,ts,tsx}: 使用 OxLint 进行代码检查
Log和注释使用英文书写
Files:
src/main/presenter/llmProviderPresenter/providers/tokenfluxProvider.tssrc/main/presenter/lifecyclePresenter/hooks/ready/presenterInitHook.tssrc/main/presenter/lifecyclePresenter/DatabaseInitializer.tssrc/main/presenter/lifecyclePresenter/SplashWindowManager.tssrc/main/presenter/windowPresenter/index.tssrc/shared/presenter.d.ts
src/{main,renderer}/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/electron-best-practices.mdc)
src/{main,renderer}/**/*.ts: Use context isolation for improved security
Implement proper inter-process communication (IPC) patterns
Optimize application startup time with lazy loading
Implement proper error handling and logging for debugging
Files:
src/main/presenter/llmProviderPresenter/providers/tokenfluxProvider.tssrc/main/presenter/lifecyclePresenter/hooks/ready/presenterInitHook.tssrc/main/presenter/lifecyclePresenter/DatabaseInitializer.tssrc/main/presenter/lifecyclePresenter/SplashWindowManager.tssrc/main/presenter/windowPresenter/index.ts
src/main/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/electron-best-practices.mdc)
Use Electron's built-in APIs for file system and native dialogs
From main to renderer, broadcast events via EventBus using mainWindow.webContents.send()
Files:
src/main/presenter/llmProviderPresenter/providers/tokenfluxProvider.tssrc/main/presenter/lifecyclePresenter/hooks/ready/presenterInitHook.tssrc/main/presenter/lifecyclePresenter/DatabaseInitializer.tssrc/main/presenter/lifecyclePresenter/SplashWindowManager.tssrc/main/presenter/windowPresenter/index.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/error-logging.mdc)
**/*.{ts,tsx}: 始终使用 try-catch 处理可能的错误
提供有意义的错误信息
记录详细的错误日志
优雅降级处理
日志应包含时间戳、日志级别、错误代码、错误描述、堆栈跟踪(如适用)、相关上下文信息
日志级别应包括 ERROR、WARN、INFO、DEBUG
不要吞掉错误
提供用户友好的错误信息
实现错误重试机制
避免记录敏感信息
使用结构化日志
设置适当的日志级别Enable and adhere to strict TypeScript type checking
Files:
src/main/presenter/llmProviderPresenter/providers/tokenfluxProvider.tssrc/main/presenter/lifecyclePresenter/hooks/ready/presenterInitHook.tssrc/main/presenter/lifecyclePresenter/DatabaseInitializer.tssrc/main/presenter/lifecyclePresenter/SplashWindowManager.tssrc/main/presenter/windowPresenter/index.tssrc/shared/presenter.d.ts
src/main/presenter/llmProviderPresenter/providers/*.ts
📄 CodeRabbit inference engine (.cursor/rules/llm-agent-loop.mdc)
src/main/presenter/llmProviderPresenter/providers/*.ts: Each file insrc/main/presenter/llmProviderPresenter/providers/*.tsshould handle interaction with a specific LLM API, including request/response formatting, tool definition conversion, native/non-native tool call management, and standardizing output streams to a common event format.
Provider implementations must use acoreStreammethod that yields standardized stream events to decouple the main loop from provider-specific details.
ThecoreStreammethod in each Provider must perform a single streaming API request per conversation round and must not contain multi-round tool call loop logic.
Provider files should implement helper methods such asformatMessages,convertToProviderTools,parseFunctionCalls, andprepareFunctionCallPromptas needed for provider-specific logic.
All provider implementations must parse provider-specific data chunks and yield standardized events for text, reasoning, tool calls, usage, errors, stop reasons, and image data.
When a provider does not support native function calling, it must prepare messages using prompt wrapping (e.g.,prepareFunctionCallPrompt) before making the API call.
When a provider supports native function calling, MCP tools must be converted to the provider's format (e.g., usingconvertToProviderTools) and included in the API request.
Provider implementations should aggregate and yield usage events as part of the standardized stream.
Provider implementations should yield image data events in the standardized format when applicable.
Provider implementations should yield reasoning events in the standardized format when applicable.
Provider implementations should yield tool call events (tool_call_start,tool_call_chunk,tool_call_end) in the standardized format.
Provider implementations should yield stop events with appropriatestop_reasonin the standardized format.
Provider implementations should yield error events in the standardized format...
Files:
src/main/presenter/llmProviderPresenter/providers/tokenfluxProvider.ts
src/main/**/*.{ts,js,tsx,jsx}
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
主进程代码放在
src/main
Files:
src/main/presenter/llmProviderPresenter/providers/tokenfluxProvider.tssrc/main/presenter/lifecyclePresenter/hooks/ready/presenterInitHook.tssrc/main/presenter/lifecyclePresenter/DatabaseInitializer.tssrc/main/presenter/lifecyclePresenter/SplashWindowManager.tssrc/main/presenter/windowPresenter/index.ts
src/**/*.{ts,tsx,vue}
📄 CodeRabbit inference engine (CLAUDE.md)
Use English for all logs and comments
Files:
src/main/presenter/llmProviderPresenter/providers/tokenfluxProvider.tssrc/main/presenter/lifecyclePresenter/hooks/ready/presenterInitHook.tssrc/main/presenter/lifecyclePresenter/DatabaseInitializer.tssrc/main/presenter/lifecyclePresenter/SplashWindowManager.tssrc/main/presenter/windowPresenter/index.tssrc/shared/presenter.d.ts
src/main/presenter/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Maintain one presenter per functional domain in src/main/presenter/
Files:
src/main/presenter/llmProviderPresenter/providers/tokenfluxProvider.tssrc/main/presenter/lifecyclePresenter/hooks/ready/presenterInitHook.tssrc/main/presenter/lifecyclePresenter/DatabaseInitializer.tssrc/main/presenter/lifecyclePresenter/SplashWindowManager.tssrc/main/presenter/windowPresenter/index.ts
src/shared/*.d.ts
📄 CodeRabbit inference engine (.cursor/rules/electron-best-practices.mdc)
The shared/*.d.ts files are used to define the types of objects exposed by the main process to the renderer process
Files:
src/shared/presenter.d.ts
src/shared/**/*.{ts,tsx,d.ts}
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
共享类型定义放在
shared目录
Files:
src/shared/presenter.d.ts
src/shared/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Put shared types and IPC contracts in src/shared/
Files:
src/shared/presenter.d.ts
🧠 Learnings (21)
📚 Learning: 2025-08-26T14:13:46.601Z
Learnt from: CR
PR: ThinkInAIXYZ/deepchat#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-26T14:13:46.601Z
Learning: Applies to src/main/presenter/configPresenter/providers.ts : Add new provider configuration entries in configPresenter/providers.ts
Applied to files:
src/main/presenter/llmProviderPresenter/providers/tokenfluxProvider.ts
📚 Learning: 2025-08-28T05:55:31.458Z
Learnt from: zerob13
PR: ThinkInAIXYZ/deepchat#804
File: src/main/presenter/llmProviderPresenter/providers/tokenfluxProvider.ts:153-156
Timestamp: 2025-08-28T05:55:31.458Z
Learning: TokenFlux models generally support function calling by default, so it's reasonable to assume hasFunctionCalling = true for TokenFlux provider implementations in src/main/presenter/llmProviderPresenter/providers/tokenfluxProvider.ts
Applied to files:
src/main/presenter/llmProviderPresenter/providers/tokenfluxProvider.ts
📚 Learning: 2025-08-26T14:13:46.601Z
Learnt from: CR
PR: ThinkInAIXYZ/deepchat#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-26T14:13:46.601Z
Learning: Applies to src/main/presenter/llmProviderPresenter/providers/*.ts : Each LLM provider must implement provider-specific API interactions, convert MCP tools, and normalize streaming responses
Applied to files:
src/main/presenter/llmProviderPresenter/providers/tokenfluxProvider.ts
📚 Learning: 2025-07-21T01:46:52.880Z
Learnt from: CR
PR: ThinkInAIXYZ/deepchat#0
File: .cursor/rules/llm-agent-loop.mdc:0-0
Timestamp: 2025-07-21T01:46:52.880Z
Learning: Applies to src/main/presenter/llmProviderPresenter/providers/*.ts : Each file in `src/main/presenter/llmProviderPresenter/providers/*.ts` should handle interaction with a specific LLM API, including request/response formatting, tool definition conversion, native/non-native tool call management, and standardizing output streams to a common event format.
Applied to files:
src/main/presenter/llmProviderPresenter/providers/tokenfluxProvider.ts
📚 Learning: 2025-08-26T14:13:46.601Z
Learnt from: CR
PR: ThinkInAIXYZ/deepchat#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-26T14:13:46.601Z
Learning: Applies to src/main/presenter/configPresenter/**/*.ts : Centralize configuration logic under configPresenter/
Applied to files:
src/main/presenter/llmProviderPresenter/providers/tokenfluxProvider.tssrc/main/presenter/windowPresenter/index.tssrc/shared/presenter.d.ts
📚 Learning: 2025-07-21T01:46:52.880Z
Learnt from: CR
PR: ThinkInAIXYZ/deepchat#0
File: .cursor/rules/llm-agent-loop.mdc:0-0
Timestamp: 2025-07-21T01:46:52.880Z
Learning: Applies to src/main/presenter/llmProviderPresenter/providers/*.ts : Provider implementations must use a `coreStream` method that yields standardized stream events to decouple the main loop from provider-specific details.
Applied to files:
src/main/presenter/llmProviderPresenter/providers/tokenfluxProvider.ts
📚 Learning: 2025-07-21T01:46:52.880Z
Learnt from: CR
PR: ThinkInAIXYZ/deepchat#0
File: .cursor/rules/llm-agent-loop.mdc:0-0
Timestamp: 2025-07-21T01:46:52.880Z
Learning: Applies to src/main/presenter/llmProviderPresenter/providers/*.ts : Provider files should implement helper methods such as `formatMessages`, `convertToProviderTools`, `parseFunctionCalls`, and `prepareFunctionCallPrompt` as needed for provider-specific logic.
Applied to files:
src/main/presenter/llmProviderPresenter/providers/tokenfluxProvider.ts
📚 Learning: 2025-07-21T01:46:52.880Z
Learnt from: CR
PR: ThinkInAIXYZ/deepchat#0
File: .cursor/rules/llm-agent-loop.mdc:0-0
Timestamp: 2025-07-21T01:46:52.880Z
Learning: Applies to src/main/presenter/llmProviderPresenter/providers/*.ts : Provider implementations should yield events asynchronously using the async generator pattern.
Applied to files:
src/main/presenter/llmProviderPresenter/providers/tokenfluxProvider.ts
📚 Learning: 2025-08-26T14:13:46.601Z
Learnt from: CR
PR: ThinkInAIXYZ/deepchat#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-26T14:13:46.601Z
Learning: Applies to src/main/presenter/llmProviderPresenter/providers/*.ts : Implement a coreStream method for new providers following the standardized event interface
Applied to files:
src/main/presenter/llmProviderPresenter/providers/tokenfluxProvider.ts
📚 Learning: 2025-07-21T01:46:52.880Z
Learnt from: CR
PR: ThinkInAIXYZ/deepchat#0
File: .cursor/rules/llm-agent-loop.mdc:0-0
Timestamp: 2025-07-21T01:46:52.880Z
Learning: Applies to src/main/presenter/llmProviderPresenter/index.ts : `src/main/presenter/llmProviderPresenter/index.ts` should manage the overall Agent loop, conversation history, tool execution via `McpPresenter`, and frontend communication via `eventBus`.
Applied to files:
src/main/presenter/llmProviderPresenter/providers/tokenfluxProvider.tssrc/main/presenter/lifecyclePresenter/hooks/ready/presenterInitHook.tssrc/shared/presenter.d.ts
📚 Learning: 2025-08-26T14:13:46.601Z
Learnt from: CR
PR: ThinkInAIXYZ/deepchat#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-26T14:13:46.601Z
Learning: Applies to src/main/presenter/llmProviderPresenter/index.ts : Agent Loop layer must manage conversation flow, execute tools via McpPresenter, and standardize events to the frontend
Applied to files:
src/main/presenter/llmProviderPresenter/providers/tokenfluxProvider.tssrc/shared/presenter.d.ts
📚 Learning: 2025-07-21T01:46:52.880Z
Learnt from: CR
PR: ThinkInAIXYZ/deepchat#0
File: .cursor/rules/llm-agent-loop.mdc:0-0
Timestamp: 2025-07-21T01:46:52.880Z
Learning: Applies to src/main/presenter/llmProviderPresenter/providers/*.ts : All provider implementations must parse provider-specific data chunks and yield standardized events for text, reasoning, tool calls, usage, errors, stop reasons, and image data.
Applied to files:
src/main/presenter/llmProviderPresenter/providers/tokenfluxProvider.ts
📚 Learning: 2025-07-21T01:46:52.880Z
Learnt from: CR
PR: ThinkInAIXYZ/deepchat#0
File: .cursor/rules/llm-agent-loop.mdc:0-0
Timestamp: 2025-07-21T01:46:52.880Z
Learning: Applies to src/main/presenter/llmProviderPresenter/providers/*.ts : When a provider does not support native function calling, it must prepare messages using prompt wrapping (e.g., `prepareFunctionCallPrompt`) before making the API call.
Applied to files:
src/main/presenter/llmProviderPresenter/providers/tokenfluxProvider.ts
📚 Learning: 2025-07-21T01:46:52.880Z
Learnt from: CR
PR: ThinkInAIXYZ/deepchat#0
File: .cursor/rules/llm-agent-loop.mdc:0-0
Timestamp: 2025-07-21T01:46:52.880Z
Learning: Applies to src/main/presenter/llmProviderPresenter/index.ts : The main Agent loop should buffer text content, handle tool call events, format tool results for the next LLM call, and manage conversation continuation logic.
Applied to files:
src/main/presenter/llmProviderPresenter/providers/tokenfluxProvider.tssrc/shared/presenter.d.ts
📚 Learning: 2025-07-21T01:45:54.229Z
Learnt from: CR
PR: ThinkInAIXYZ/deepchat#0
File: .cursor/rules/electron-best-practices.mdc:0-0
Timestamp: 2025-07-21T01:45:54.229Z
Learning: Applies to src/renderer/src/composables/usePresenter.ts : The IPC in the renderer process is implemented in usePresenter.ts, allowing direct calls to the presenter-related interfaces exposed by the main process
Applied to files:
src/main/presenter/windowPresenter/index.tssrc/shared/presenter.d.ts
📚 Learning: 2025-07-21T01:45:54.229Z
Learnt from: CR
PR: ThinkInAIXYZ/deepchat#0
File: .cursor/rules/electron-best-practices.mdc:0-0
Timestamp: 2025-07-21T01:45:54.229Z
Learning: Applies to src/main/presenter/index.ts : The IPC messages from the main process to notify the view mainly rely on the EventBus index.ts to listen for events that need to be notified and then send them to the renderer through the mainWindow
Applied to files:
src/main/presenter/windowPresenter/index.ts
📚 Learning: 2025-08-26T14:13:46.601Z
Learnt from: CR
PR: ThinkInAIXYZ/deepchat#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-26T14:13:46.601Z
Learning: Applies to src/renderer/**/*.{ts,vue} : From renderer to main, call presenters via the usePresenter.ts composable
Applied to files:
src/main/presenter/windowPresenter/index.ts
📚 Learning: 2025-07-21T01:45:54.229Z
Learnt from: CR
PR: ThinkInAIXYZ/deepchat#0
File: .cursor/rules/electron-best-practices.mdc:0-0
Timestamp: 2025-07-21T01:45:54.229Z
Learning: Applies to src/main/**/*.ts : Use Electron's built-in APIs for file system and native dialogs
Applied to files:
src/main/presenter/windowPresenter/index.ts
📚 Learning: 2025-08-26T14:13:46.601Z
Learnt from: CR
PR: ThinkInAIXYZ/deepchat#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-26T14:13:46.601Z
Learning: Applies to src/main/presenter/mcpPresenter/inMemoryServers/**/*.ts : Implement new MCP tools under mcpPresenter/inMemoryServers/
Applied to files:
src/shared/presenter.d.ts
📚 Learning: 2025-08-26T14:13:46.601Z
Learnt from: CR
PR: ThinkInAIXYZ/deepchat#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-26T14:13:46.601Z
Learning: Applies to src/main/presenter/mcpPresenter/index.ts : Register new MCP tools in mcpPresenter/index.ts
Applied to files:
src/shared/presenter.d.ts
📚 Learning: 2025-07-21T01:46:52.880Z
Learnt from: CR
PR: ThinkInAIXYZ/deepchat#0
File: .cursor/rules/llm-agent-loop.mdc:0-0
Timestamp: 2025-07-21T01:46:52.880Z
Learning: Applies to src/main/presenter/llmProviderPresenter/providers/*.ts : When a provider supports native function calling, MCP tools must be converted to the provider's format (e.g., using `convertToProviderTools`) and included in the API request.
Applied to files:
src/shared/presenter.d.ts
🧬 Code graph analysis (6)
src/main/presenter/llmProviderPresenter/providers/tokenfluxProvider.ts (1)
src/shared/presenter.d.ts (2)
LLM_PROVIDER(512-532)IConfigPresenter(360-483)
src/main/presenter/lifecyclePresenter/hooks/ready/presenterInitHook.ts (3)
src/main/presenter/lifecyclePresenter/hooks/index.ts (1)
presenterInitHook(9-9)src/shared/presenter.d.ts (2)
LifecycleHook(1866-1872)LifecycleContext(1857-1861)src/main/presenter/index.ts (1)
presenter(223-223)
src/main/presenter/lifecyclePresenter/DatabaseInitializer.ts (1)
src/main/presenter/sqlitePresenter/index.ts (1)
SQLitePresenter(23-356)
src/main/presenter/lifecyclePresenter/SplashWindowManager.ts (4)
src/shared/presenter.d.ts (1)
ISplashWindowManager(1903-1908)src/main/eventbus.ts (1)
eventBus(151-151)src/main/events.ts (1)
LIFECYCLE_EVENTS(201-210)src/main/presenter/lifecyclePresenter/types.ts (1)
ProgressUpdatedEventData(54-57)
src/main/presenter/windowPresenter/index.ts (1)
src/shared/presenter.d.ts (1)
IConfigPresenter(360-483)
src/shared/presenter.d.ts (1)
src/main/presenter/windowPresenter/FloatingChatWindow.ts (1)
FloatingChatWindow(44-398)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build-check (x64)
🔇 Additional comments (3)
src/main/presenter/llmProviderPresenter/providers/tokenfluxProvider.ts (1)
1-8: LGTM! Constructor signature properly migrated to use IConfigPresenter.The import and constructor changes correctly follow the established pattern of using the public
IConfigPresenterinterface instead of the concreteConfigPresenterimplementation. This improves decoupling and aligns with the lifecycle refactor's dependency injection approach.Also applies to: 33-33
src/main/presenter/windowPresenter/index.ts (1)
1179-1181: LGTM! Clean lifecycle control interface.The new
setApplicationQuittingmethod provides a clean way for the lifecycle manager to control the window quitting state, properly decoupling window management from internal Electron app events.src/shared/presenter.d.ts (1)
1856-1928: LGTM! Well-designed lifecycle management interfaces.The new lifecycle interfaces (
ILifecycleManager,ISplashWindowManager,LifecycleContext,LifecycleHook) provide a clean, hook-based architecture for managing application startup and shutdown. The phase-based approach with priorities and critical flags offers good control over the startup sequence.
| export interface IDatabaseInitializer { | ||
| initialize(): Promise<SQLitePresenter> | ||
| migrate(): Promise<void> | ||
| validateConnection(): Promise<boolean> | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Extend the contract: return the interface and expose close()
Callers should not depend on the concrete class; also expose a close() for lifecycle teardown.
export interface IDatabaseInitializer {
- initialize(): Promise<SQLitePresenter>
+ initialize(): Promise<ISQLitePresenter>
migrate(): Promise<void>
validateConnection(): Promise<boolean>
+ close(): void
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export interface IDatabaseInitializer { | |
| initialize(): Promise<SQLitePresenter> | |
| migrate(): Promise<void> | |
| validateConnection(): Promise<boolean> | |
| } | |
| export interface IDatabaseInitializer { | |
| initialize(): Promise<ISQLitePresenter> | |
| migrate(): Promise<void> | |
| validateConnection(): Promise<boolean> | |
| close(): void | |
| } |
🤖 Prompt for AI Agents
In src/main/presenter/lifecyclePresenter/DatabaseInitializer.ts around lines 8
to 12, change the contract so callers depend on an interface and can tear down
the resource: replace the concrete return type Promise<SQLitePresenter> with
Promise<IDatabasePresenter> (or the actual presenter interface name used in the
codebase) and add a close(): Promise<void> method to the IDatabaseInitializer
interface; update imports to reference the presenter interface, and then update
the concrete implementation(s) and all callers to implement/consume the new
close() method and to return the presenter via the interface type.
| export class DatabaseInitializer implements IDatabaseInitializer { | ||
| private dbPath: string | ||
| private password?: string | ||
| private database?: SQLitePresenter | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don’t call app.getPath() in the constructor; compute path lazily after app is ready
Constructors may run before Electron is ready. Defer path resolution to initialize() and mark dbPath as definite assignment.
export class DatabaseInitializer implements IDatabaseInitializer {
- private dbPath: string
+ private dbPath!: string
private password?: string
- private database?: SQLitePresenter
+ private database?: ISQLitePresenter
constructor(password?: string) {
- // Initialize database path
- const dbDir = path.join(app.getPath('userData'), 'app_db')
- this.dbPath = path.join(dbDir, 'chat.db')
this.password = password
}Also applies to: 23-28
🤖 Prompt for AI Agents
In src/main/presenter/lifecyclePresenter/DatabaseInitializer.ts around lines
18-22 (and similarly 23-28), remove any call to app.getPath() from the
constructor and make dbPath a definitely assigned string (remove the optional
?); instead, compute and assign dbPath lazily inside initialize() after Electron
is ready (use app.isReady() or await app.whenReady()), ensure password and
database remain optional until initialization completes, and set up the
SQLitePresenter instance after dbPath is resolved; update types/signature to
reflect dbPath will be assigned during initialize().
| async initialize(): Promise<SQLitePresenter> { | ||
| try { | ||
| console.log('DatabaseInitializer: Starting database initialization') | ||
|
|
||
| // Create SQLitePresenter instance | ||
| this.database = new SQLitePresenter(this.dbPath, this.password) | ||
|
|
||
| // Validate the connection | ||
| const isValid = await this.validateConnection() | ||
| if (!isValid) { | ||
| throw new Error('Database connection validation failed') | ||
| } | ||
|
|
||
| console.log('DatabaseInitializer: Database initialization completed successfully') | ||
| return this.database | ||
| } catch (error) { | ||
| console.error('DatabaseInitializer: Database initialization failed:', error) | ||
| throw error | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Make initialize() idempotent, await app.whenReady(), and clean up on validation failure
This prevents early access to Electron paths, repeated initializations, and handle leaks on partial failures. Also use typed catches.
- async initialize(): Promise<SQLitePresenter> {
+ async initialize(): Promise<ISQLitePresenter> {
try {
- console.log('DatabaseInitializer: Starting database initialization')
+ console.info('[DatabaseInitializer][INFO] Starting database initialization')
+ if (!app.isReady()) {
+ await app.whenReady()
+ }
+ const dbDir = path.join(app.getPath('userData'), 'app_db')
+ this.dbPath = path.join(dbDir, 'chat.db')
+
+ // Idempotent: return existing instance
+ if (this.database) {
+ return this.database
+ }
// Create SQLitePresenter instance
this.database = new SQLitePresenter(this.dbPath, this.password)
// Validate the connection
const isValid = await this.validateConnection()
if (!isValid) {
- throw new Error('Database connection validation failed')
+ try { this.database?.close() } catch {}
+ this.database = undefined
+ throw new Error('Database connection validation failed')
}
- console.log('DatabaseInitializer: Database initialization completed successfully')
+ console.info('[DatabaseInitializer][INFO] Database initialization completed successfully')
return this.database
- } catch (error) {
- console.error('DatabaseInitializer: Database initialization failed:', error)
+ } catch (error: unknown) {
+ console.error('[DatabaseInitializer][ERROR] Database initialization failed', {
+ error: error instanceof Error ? { message: error.message, stack: error.stack } : String(error)
+ })
throw error
}
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| async initialize(): Promise<SQLitePresenter> { | |
| try { | |
| console.log('DatabaseInitializer: Starting database initialization') | |
| // Create SQLitePresenter instance | |
| this.database = new SQLitePresenter(this.dbPath, this.password) | |
| // Validate the connection | |
| const isValid = await this.validateConnection() | |
| if (!isValid) { | |
| throw new Error('Database connection validation failed') | |
| } | |
| console.log('DatabaseInitializer: Database initialization completed successfully') | |
| return this.database | |
| } catch (error) { | |
| console.error('DatabaseInitializer: Database initialization failed:', error) | |
| throw error | |
| } | |
| } | |
| async initialize(): Promise<ISQLitePresenter> { | |
| try { | |
| console.info('[DatabaseInitializer][INFO] Starting database initialization') | |
| if (!app.isReady()) { | |
| await app.whenReady() | |
| } | |
| const dbDir = path.join(app.getPath('userData'), 'app_db') | |
| this.dbPath = path.join(dbDir, 'chat.db') | |
| // Idempotent: return existing instance | |
| if (this.database) { | |
| return this.database | |
| } | |
| // Create SQLitePresenter instance | |
| this.database = new SQLitePresenter(this.dbPath, this.password) | |
| // Validate the connection | |
| const isValid = await this.validateConnection() | |
| if (!isValid) { | |
| try { this.database?.close() } catch {} | |
| this.database = undefined | |
| throw new Error('Database connection validation failed') | |
| } | |
| console.info('[DatabaseInitializer][INFO] Database initialization completed successfully') | |
| return this.database | |
| } catch (error: unknown) { | |
| console.error('[DatabaseInitializer][ERROR] Database initialization failed', { | |
| error: error instanceof Error ? { message: error.message, stack: error.stack } : String(error) | |
| }) | |
| throw error | |
| } | |
| } |
🤖 Prompt for AI Agents
In src/main/presenter/lifecyclePresenter/DatabaseInitializer.ts around lines 33
to 52, make initialize() idempotent and safe: first return existing
this.database if already initialized; await app.whenReady() before constructing
the SQLitePresenter to avoid accessing Electron paths too early; create the
presenter and call validateConnection(), and if validation fails close/dispose
the partially created presenter, set this.database to undefined/null to avoid
leaks, then rethrow the error; use a typed catch (catch (err: unknown)) and
normalize to an Error for logging before rethrowing. Ensure resources are
cleaned up on failure and subsequent calls get the same initialized instance.
| console.log('DatabaseInitializer: Starting database initialization') | ||
|
|
||
| // Create SQLitePresenter instance | ||
| this.database = new SQLitePresenter(this.dbPath, this.password) | ||
|
|
||
| // Validate the connection | ||
| const isValid = await this.validateConnection() | ||
| if (!isValid) { | ||
| throw new Error('Database connection validation failed') | ||
| } | ||
|
|
||
| console.log('DatabaseInitializer: Database initialization completed successfully') | ||
| return this.database | ||
| } catch (error) { | ||
| console.error('DatabaseInitializer: Database initialization failed:', error) | ||
| throw error | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Perform database migrations | ||
| */ | ||
| async migrate(): Promise<void> { | ||
| if (!this.database) { | ||
| throw new Error('Database must be initialized before migration') | ||
| } | ||
|
|
||
| try { | ||
| console.log('DatabaseInitializer: Starting database migration') | ||
| // Migration logic is already handled in SQLitePresenter constructor | ||
| // This method is here for future migration needs that might be separate | ||
| console.log('DatabaseInitializer: Database migration completed') | ||
| } catch (error) { | ||
| console.error('DatabaseInitializer: Database migration failed:', error) | ||
| throw error | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Validate database connection | ||
| */ | ||
| async validateConnection(): Promise<boolean> { | ||
| if (!this.database) { | ||
| return false | ||
| } | ||
|
|
||
| try { | ||
| // Test basic database functionality | ||
| await this.database.getConversationCount() | ||
| return true | ||
| } catch (error) { | ||
| console.error('DatabaseInitializer: Connection validation failed:', error) | ||
| return false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Adopt a centralized structured logger per guidelines
Replace console.* with a structured logger (levels, timestamp, error codes, context). This aligns with “Use English for logs,” structured logging, and proper levels (ERROR/WARN/INFO/DEBUG).
If a Logger already exists in the lifecycle framework, inject it via the lifecycle context and use logger.info/error(...) here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (1)
src/main/presenter/lifecyclePresenter/hooks/beforeQuit/windowQuittingHook.ts (1)
16-18: If keeping the throw, fix messageIf you intentionally throw here, prefer a neutral message.
- throw new Error('windowQuittingHook: Presenter has been destroyed') + throw new Error('windowQuittingHook: Presenter is not available')
🧹 Nitpick comments (2)
src/main/presenter/lifecyclePresenter/hooks/beforeQuit/windowQuittingHook.ts (2)
1-3: Clarify hook docstring to reflect phase and actionsBe explicit about BEFORE_QUIT and what this hook does.
/** - * window quitting flag setup hook + * Window quitting hook for BEFORE_QUIT phase. + * Marks the app as quitting and tears down the floating chat window. */
12-12: Clarify comment on windowQuittingHook priorityFile: src/main/presenter/lifecyclePresenter/hooks/beforeQuit/windowQuittingHook.ts line 12
- priority: 10, // make sure presenter be destroyed lastest + priority: 10, // Run before presenter teardown (presenterDestroyHook uses MAX_VALUE)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
docs/app-lifecycle.md(1 hunks)src/main/presenter/lifecyclePresenter/hooks/beforeQuit/windowQuittingHook.ts(1 hunks)src/main/presenter/lifecyclePresenter/hooks/index.ts(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- docs/app-lifecycle.md
🚧 Files skipped from review as they are similar to previous changes (1)
- src/main/presenter/lifecyclePresenter/hooks/index.ts
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development-setup.mdc)
**/*.{js,jsx,ts,tsx}: 使用 OxLint 进行代码检查
Log和注释使用英文书写
Files:
src/main/presenter/lifecyclePresenter/hooks/beforeQuit/windowQuittingHook.ts
src/{main,renderer}/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/electron-best-practices.mdc)
src/{main,renderer}/**/*.ts: Use context isolation for improved security
Implement proper inter-process communication (IPC) patterns
Optimize application startup time with lazy loading
Implement proper error handling and logging for debugging
Files:
src/main/presenter/lifecyclePresenter/hooks/beforeQuit/windowQuittingHook.ts
src/main/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/electron-best-practices.mdc)
Use Electron's built-in APIs for file system and native dialogs
From main to renderer, broadcast events via EventBus using mainWindow.webContents.send()
Files:
src/main/presenter/lifecyclePresenter/hooks/beforeQuit/windowQuittingHook.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/error-logging.mdc)
**/*.{ts,tsx}: 始终使用 try-catch 处理可能的错误
提供有意义的错误信息
记录详细的错误日志
优雅降级处理
日志应包含时间戳、日志级别、错误代码、错误描述、堆栈跟踪(如适用)、相关上下文信息
日志级别应包括 ERROR、WARN、INFO、DEBUG
不要吞掉错误
提供用户友好的错误信息
实现错误重试机制
避免记录敏感信息
使用结构化日志
设置适当的日志级别Enable and adhere to strict TypeScript type checking
Files:
src/main/presenter/lifecyclePresenter/hooks/beforeQuit/windowQuittingHook.ts
src/main/**/*.{ts,js,tsx,jsx}
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
主进程代码放在
src/main
Files:
src/main/presenter/lifecyclePresenter/hooks/beforeQuit/windowQuittingHook.ts
src/**/*.{ts,tsx,vue}
📄 CodeRabbit inference engine (CLAUDE.md)
Use English for all logs and comments
Files:
src/main/presenter/lifecyclePresenter/hooks/beforeQuit/windowQuittingHook.ts
src/main/presenter/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Maintain one presenter per functional domain in src/main/presenter/
Files:
src/main/presenter/lifecyclePresenter/hooks/beforeQuit/windowQuittingHook.ts
🧬 Code graph analysis (1)
src/main/presenter/lifecyclePresenter/hooks/beforeQuit/windowQuittingHook.ts (3)
src/main/presenter/lifecyclePresenter/hooks/index.ts (1)
windowQuittingHook(17-17)src/shared/presenter.d.ts (2)
LifecycleHook(1866-1872)LifecycleContext(1857-1861)src/main/presenter/index.ts (1)
presenter(223-223)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build-check (x64)
🔇 Additional comments (1)
src/main/presenter/lifecyclePresenter/hooks/beforeQuit/windowQuittingHook.ts (1)
9-11: Registration looks correct (name + phase).
| import { LifecycleHook, LifecycleContext } from '@shared/presenter' | ||
| import { presenter } from '@/presenter' | ||
| import { LifecyclePhase } from '@shared/lifecycle' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Prefer DI via LifecycleContext over global singleton import
This file imports the global presenter, which goes against the PR’s DI goal. Fetch presenter from context, with a safe fallback only if necessary.
-import { presenter } from '@/presenter'
+import { presenter as globalPresenter } from '@/presenter'
+import type { Presenter } from '@/presenter'Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/main/presenter/lifecyclePresenter/hooks/beforeQuit/windowQuittingHook.ts
around lines 5 to 7, the module currently imports the global presenter singleton
which breaks the PR's DI goal; instead remove the global import and retrieve the
presenter from the provided LifecycleContext (e.g., use ctx.presenter or
ctx.get('presenter') inside the hook), update the hook to accept/use the context
value, and only use the global presenter as a guarded fallback if the context
value is undefined.
| execute: async (_context: LifecycleContext) => { | ||
| // Ensure presenter is available | ||
| if (!presenter) { | ||
| throw new Error('windowQuittingHook: Presenter has been destroyed') | ||
| } | ||
| presenter.windowPresenter.setApplicationQuitting(true) | ||
| presenter.windowPresenter.destroyFloatingChatWindow() | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add defensive checks and structured logging; don’t throw in non-critical hook
Handle absent presenter/windowPresenter and wrap calls; log with context; keep non-critical behavior.
- execute: async (_context: LifecycleContext) => {
- // Ensure presenter is available
- if (!presenter) {
- throw new Error('windowQuittingHook: Presenter has been destroyed')
- }
- presenter.windowPresenter.setApplicationQuitting(true)
- presenter.windowPresenter.destroyFloatingChatWindow()
- }
+ execute: async (context: LifecycleContext) => {
+ const p: Presenter | undefined = (context as any)?.presenter ?? globalPresenter
+ if (!p || !p.windowPresenter) {
+ const payload = {
+ ts: new Date().toISOString(),
+ level: 'WARN',
+ code: 'LIFECYCLE.BEFORE_QUIT.WINDOW_QUITTING.NO_PRESENTER',
+ message: 'Presenter or windowPresenter not available during BEFORE_QUIT.',
+ phase: context?.phase
+ }
+ ;(p?.logger?.warn?.bind(p.logger) ?? console.warn)(payload)
+ return
+ }
+ try {
+ p.windowPresenter.setApplicationQuitting(true)
+ } catch (err) {
+ const e = err as Error
+ const payload = {
+ ts: new Date().toISOString(),
+ level: 'ERROR',
+ code: 'LIFECYCLE.BEFORE_QUIT.WINDOW_QUITTING.SET_FLAG_FAILED',
+ message: 'Failed to set application quitting flag.',
+ phase: context?.phase,
+ stack: e?.stack
+ }
+ ;(p.logger?.error?.bind(p.logger) ?? console.error)(payload)
+ }
+ try {
+ p.windowPresenter.destroyFloatingChatWindow()
+ } catch (err) {
+ const e = err as Error
+ const payload = {
+ ts: new Date().toISOString(),
+ level: 'WARN',
+ code: 'LIFECYCLE.BEFORE_QUIT.WINDOW_QUITTING.DESTROY_FLOATING_FAILED',
+ message: 'Failed to destroy floating chat window.',
+ phase: context?.phase,
+ stack: e?.stack
+ }
+ ;(p.logger?.warn?.bind(p.logger) ?? console.warn)(payload)
+ }
+ }Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/main/presenter/lifecyclePresenter/hooks/beforeQuit/windowQuittingHook.ts
around lines 14 to 21, the hook currently throws if presenter is missing and
calls presenter.windowPresenter methods without checks; change this to be
defensive and non-throwing: check for presenter and presenter.windowPresenter,
guard each call (no throws), wrap the operations in a try/catch so any
unexpected error is caught, and emit structured log entries with context (e.g.,
module/hook name and whether presenter or windowPresenter was missing) instead
of throwing so the hook remains non-critical; ensure setApplicationQuitting and
destroyFloatingChatWindow are only invoked when the respective objects exist.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (7)
src/main/presenter/llmProviderPresenter/providers/geminiProvider.ts (7)
366-385: Incorrect string vs enum comparisons and redundant await in safety settingsComparing HarmBlockThreshold enum values to string literals always succeeds; also “await” on sync getSetting is unnecessary. This causes BLOCK_NONE/UNSPECIFIED thresholds to be mistakenly applied.
Apply:
- const settingValue = - (await this.configPresenter.getSetting<string>( - `geminiSafety_${key}` // Match the key used in settings store - )) || 'HARM_BLOCK_THRESHOLD_UNSPECIFIED' // Default if not set + const settingValue = + this.configPresenter.getSetting<string>(`geminiSafety_${key}`) ?? + 'HARM_BLOCK_THRESHOLD_UNSPECIFIED' - // Only add if threshold is defined, category is defined, and threshold is not BLOCK_NONE - if ( - threshold && - category && - threshold !== 'BLOCK_NONE' && - threshold !== 'HARM_BLOCK_THRESHOLD_UNSPECIFIED' - ) { + // Only add if both are defined and threshold is not NONE/UNSPECIFIED + if ( + category !== undefined && + threshold !== undefined && + threshold !== HarmBlockThreshold.BLOCK_NONE && + threshold !== HarmBlockThreshold.HARM_BLOCK_THRESHOLD_UNSPECIFIED + ) { safetySettings.push({ category, threshold }) }
307-323: Set isInitialized only after successful model loadCurrently set to true before fetch; on failure, provider remains “initialized” incorrectly.
- this.isInitialized = true - // 使用API获取模型列表,如果失败则回退到静态列表 - this.models = await this.fetchProviderModels() + // Fetch models first; mark initialized only on success + this.models = await this.fetchProviderModels() await this.autoEnableModelsIfNeeded() // gemini 比较慢,特殊补偿一下 eventBus.sendToRenderer( CONFIG_EVENTS.MODEL_LIST_CHANGED, SendTarget.ALL_WINDOWS, this.provider.id ) - console.info('Provider initialized successfully:', this.provider.name) + this.isInitialized = true + console.info('[Gemini] Provider initialized successfully:', this.provider.name) } catch (error) { - console.warn('Provider initialization failed:', this.provider.name, error) + this.isInitialized = false + console.warn('[Gemini] Provider initialization failed:', this.provider.name, error) }
891-891: Avoid logging potentially sensitive safety settingsGate behind user logging flag and avoid dumping full objects.
-console.log('safetySettings', safetySettings) +if (this.configPresenter.getLoggingEnabled()) { + console.debug('[Gemini] safety settings applied', { + categories: safetySettings?.map(s => s.category)?.length ?? 0 + }) +}
913-919: Do not log full request payload (may include prompts/images/tools)Log sanitized metadata only, behind a flag.
- console.log('requestParams', requestParams) + if (this.configPresenter.getLoggingEnabled()) { + const { model, config } = requestParams + console.debug('[Gemini] request', { + model, + hasTools: Boolean(config?.tools?.length), + hasSafety: Boolean(config?.safetySettings?.length), + reasoning: Boolean(config?.thinkingConfig) + }) + }
959-963: Remove verbose candidate dumps from stream logsThis prints model output and may contain PII; use a compact, gated log.
- console.log('chunk.candidates', JSON.stringify(chunk.candidates, null, 2)) + if (this.configPresenter.getLoggingEnabled()) { + const partsCount = chunk.candidates?.[0]?.content?.parts?.length ?? 0 + console.debug('[Gemini] stream chunk', { partsCount, hasUsage: !!chunk.usageMetadata }) + }
262-279: Translate logs/messages to English and remove misleading logKeep logs/comments in English per guidelines; also the log says “ignore modelId” but the code uses it.
- console.log('gemini ignore modelId', modelId) + if (this.configPresenter.getLoggingEnabled()) { + console.debug('[Gemini] summaryTitles request', { modelId }) + } @@ - return result.text?.trim() || '新对话' + return result.text?.trim() || 'New chat' } catch (error) { - console.error('生成对话标题失败:', error) - return '新对话' + console.error('[Gemini] Failed to generate conversation title:', error) + return 'New chat' }
1115-1129: Avoid Array.prototype.findLast for wider runtime supportfindLast may not be available in all Electron/Node targets; use a backward-compatible loop.
- const userMessage = messages.findLast((msg) => msg.role === 'user') + let userMessage: ChatMessage | undefined + for (let i = messages.length - 1; i >= 0; i--) { + if (messages[i].role === 'user') { + userMessage = messages[i] + break + } + }
🧹 Nitpick comments (4)
src/main/presenter/llmProviderPresenter/providers/geminiProvider.ts (4)
767-775: English prompt for summariesAligns with “Use English for all logs and comments”.
- const prompt = `请为以下内容生成一个简洁的摘要:\n\n${text}` + const prompt = `Please generate a concise summary for the following content:\n\n${text}`
825-863: English fallbacks and resilient JSON parsingTranslate user-facing strings; retain fallback behavior.
- return ['无法生成建议'] + return ['Unable to generate suggestions'] } catch (error) { - console.error('Gemini suggestions error:', error) - return ['发生错误,无法获取建议'] + console.error('[Gemini] suggestions error:', error) + return ['An error occurred. Unable to fetch suggestions'] }
952-1003: Drain stream after tool_call to capture usage and stop cleanlyBreaking immediately after emitting tool_call_* may skip usageMetadata and final stop of the upstream stream. Consider draining remaining chunks without emitting text to capture usage.
Implementation sketch:
- Introduce a flag (e.g., drainingForUsage = false).
- On first functionCall, emit tool_call_* events, set drainingForUsage = true, and continue the loop (do not break).
- When drainingForUsage is true, ignore content parts, but keep harvesting usageMetadata.
- Yield stop with stop_reason: 'tool_use' after the loop.
This keeps a single API call per round and matches provider guidelines. I can provide a concrete patch if desired.
512-529: Replace ts-ignore blocks with typed helpersMultiple @ts-ignore usages around function call/response parts bypass type safety. Define narrow interfaces for tool message parts and map them explicitly to Gemini’s Part type.
I can add minimal types (e.g., ToolCallPart, ToolResponsePart) and refactor these branches to eliminate ts-ignore while preserving behavior.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
src/main/events.ts(1 hunks)src/main/presenter/floatingButtonPresenter/index.ts(2 hunks)src/main/presenter/llmProviderPresenter/providers/geminiProvider.ts(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- src/main/events.ts
- src/main/presenter/floatingButtonPresenter/index.ts
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development-setup.mdc)
**/*.{js,jsx,ts,tsx}: 使用 OxLint 进行代码检查
Log和注释使用英文书写
Files:
src/main/presenter/llmProviderPresenter/providers/geminiProvider.ts
src/{main,renderer}/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/electron-best-practices.mdc)
src/{main,renderer}/**/*.ts: Use context isolation for improved security
Implement proper inter-process communication (IPC) patterns
Optimize application startup time with lazy loading
Implement proper error handling and logging for debugging
Files:
src/main/presenter/llmProviderPresenter/providers/geminiProvider.ts
src/main/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/electron-best-practices.mdc)
Use Electron's built-in APIs for file system and native dialogs
From main to renderer, broadcast events via EventBus using mainWindow.webContents.send()
Files:
src/main/presenter/llmProviderPresenter/providers/geminiProvider.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/error-logging.mdc)
**/*.{ts,tsx}: 始终使用 try-catch 处理可能的错误
提供有意义的错误信息
记录详细的错误日志
优雅降级处理
日志应包含时间戳、日志级别、错误代码、错误描述、堆栈跟踪(如适用)、相关上下文信息
日志级别应包括 ERROR、WARN、INFO、DEBUG
不要吞掉错误
提供用户友好的错误信息
实现错误重试机制
避免记录敏感信息
使用结构化日志
设置适当的日志级别Enable and adhere to strict TypeScript type checking
Files:
src/main/presenter/llmProviderPresenter/providers/geminiProvider.ts
src/main/presenter/llmProviderPresenter/providers/*.ts
📄 CodeRabbit inference engine (.cursor/rules/llm-agent-loop.mdc)
src/main/presenter/llmProviderPresenter/providers/*.ts: Each file insrc/main/presenter/llmProviderPresenter/providers/*.tsshould handle interaction with a specific LLM API, including request/response formatting, tool definition conversion, native/non-native tool call management, and standardizing output streams to a common event format.
Provider implementations must use acoreStreammethod that yields standardized stream events to decouple the main loop from provider-specific details.
ThecoreStreammethod in each Provider must perform a single streaming API request per conversation round and must not contain multi-round tool call loop logic.
Provider files should implement helper methods such asformatMessages,convertToProviderTools,parseFunctionCalls, andprepareFunctionCallPromptas needed for provider-specific logic.
All provider implementations must parse provider-specific data chunks and yield standardized events for text, reasoning, tool calls, usage, errors, stop reasons, and image data.
When a provider does not support native function calling, it must prepare messages using prompt wrapping (e.g.,prepareFunctionCallPrompt) before making the API call.
When a provider supports native function calling, MCP tools must be converted to the provider's format (e.g., usingconvertToProviderTools) and included in the API request.
Provider implementations should aggregate and yield usage events as part of the standardized stream.
Provider implementations should yield image data events in the standardized format when applicable.
Provider implementations should yield reasoning events in the standardized format when applicable.
Provider implementations should yield tool call events (tool_call_start,tool_call_chunk,tool_call_end) in the standardized format.
Provider implementations should yield stop events with appropriatestop_reasonin the standardized format.
Provider implementations should yield error events in the standardized format...
Files:
src/main/presenter/llmProviderPresenter/providers/geminiProvider.ts
src/main/**/*.{ts,js,tsx,jsx}
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
主进程代码放在
src/main
Files:
src/main/presenter/llmProviderPresenter/providers/geminiProvider.ts
src/**/*.{ts,tsx,vue}
📄 CodeRabbit inference engine (CLAUDE.md)
Use English for all logs and comments
Files:
src/main/presenter/llmProviderPresenter/providers/geminiProvider.ts
src/main/presenter/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Maintain one presenter per functional domain in src/main/presenter/
Files:
src/main/presenter/llmProviderPresenter/providers/geminiProvider.ts
🧠 Learnings (11)
📚 Learning: 2025-08-26T14:13:46.601Z
Learnt from: CR
PR: ThinkInAIXYZ/deepchat#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-26T14:13:46.601Z
Learning: Applies to src/main/presenter/configPresenter/providers.ts : Add new provider configuration entries in configPresenter/providers.ts
Applied to files:
src/main/presenter/llmProviderPresenter/providers/geminiProvider.ts
📚 Learning: 2025-08-26T14:13:46.601Z
Learnt from: CR
PR: ThinkInAIXYZ/deepchat#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-26T14:13:46.601Z
Learning: Applies to src/main/presenter/configPresenter/**/*.ts : Centralize configuration logic under configPresenter/
Applied to files:
src/main/presenter/llmProviderPresenter/providers/geminiProvider.ts
📚 Learning: 2025-08-26T14:13:46.601Z
Learnt from: CR
PR: ThinkInAIXYZ/deepchat#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-26T14:13:46.601Z
Learning: Applies to src/main/presenter/llmProviderPresenter/providers/*.ts : Each LLM provider must implement provider-specific API interactions, convert MCP tools, and normalize streaming responses
Applied to files:
src/main/presenter/llmProviderPresenter/providers/geminiProvider.ts
📚 Learning: 2025-07-21T01:46:52.880Z
Learnt from: CR
PR: ThinkInAIXYZ/deepchat#0
File: .cursor/rules/llm-agent-loop.mdc:0-0
Timestamp: 2025-07-21T01:46:52.880Z
Learning: Applies to src/main/presenter/llmProviderPresenter/providers/*.ts : Each file in `src/main/presenter/llmProviderPresenter/providers/*.ts` should handle interaction with a specific LLM API, including request/response formatting, tool definition conversion, native/non-native tool call management, and standardizing output streams to a common event format.
Applied to files:
src/main/presenter/llmProviderPresenter/providers/geminiProvider.ts
📚 Learning: 2025-07-21T01:46:52.880Z
Learnt from: CR
PR: ThinkInAIXYZ/deepchat#0
File: .cursor/rules/llm-agent-loop.mdc:0-0
Timestamp: 2025-07-21T01:46:52.880Z
Learning: Applies to src/main/presenter/llmProviderPresenter/providers/*.ts : Provider files should implement helper methods such as `formatMessages`, `convertToProviderTools`, `parseFunctionCalls`, and `prepareFunctionCallPrompt` as needed for provider-specific logic.
Applied to files:
src/main/presenter/llmProviderPresenter/providers/geminiProvider.ts
📚 Learning: 2025-08-26T14:13:46.601Z
Learnt from: CR
PR: ThinkInAIXYZ/deepchat#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-26T14:13:46.601Z
Learning: Applies to src/main/presenter/llmProviderPresenter/index.ts : Agent Loop layer must manage conversation flow, execute tools via McpPresenter, and standardize events to the frontend
Applied to files:
src/main/presenter/llmProviderPresenter/providers/geminiProvider.ts
📚 Learning: 2025-07-21T01:46:52.880Z
Learnt from: CR
PR: ThinkInAIXYZ/deepchat#0
File: .cursor/rules/llm-agent-loop.mdc:0-0
Timestamp: 2025-07-21T01:46:52.880Z
Learning: Applies to src/main/presenter/llmProviderPresenter/index.ts : `src/main/presenter/llmProviderPresenter/index.ts` should manage the overall Agent loop, conversation history, tool execution via `McpPresenter`, and frontend communication via `eventBus`.
Applied to files:
src/main/presenter/llmProviderPresenter/providers/geminiProvider.ts
📚 Learning: 2025-07-21T01:46:52.880Z
Learnt from: CR
PR: ThinkInAIXYZ/deepchat#0
File: .cursor/rules/llm-agent-loop.mdc:0-0
Timestamp: 2025-07-21T01:46:52.880Z
Learning: Applies to src/main/presenter/llmProviderPresenter/streamEvents.ts : Standardized stream events should conform to the `LLMCoreStreamEvent` interface, ideally defined in a shared file such as `src/main/presenter/llmProviderPresenter/streamEvents.ts`.
Applied to files:
src/main/presenter/llmProviderPresenter/providers/geminiProvider.ts
📚 Learning: 2025-08-26T14:13:46.601Z
Learnt from: CR
PR: ThinkInAIXYZ/deepchat#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-26T14:13:46.601Z
Learning: Applies to src/main/presenter/llmProviderPresenter/providers/*.ts : When adding a new LLM provider, create a provider file under providers/
Applied to files:
src/main/presenter/llmProviderPresenter/providers/geminiProvider.ts
📚 Learning: 2025-07-21T01:46:52.880Z
Learnt from: CR
PR: ThinkInAIXYZ/deepchat#0
File: .cursor/rules/llm-agent-loop.mdc:0-0
Timestamp: 2025-07-21T01:46:52.880Z
Learning: Applies to src/main/presenter/llmProviderPresenter/providers/*.ts : Provider implementations should yield events asynchronously using the async generator pattern.
Applied to files:
src/main/presenter/llmProviderPresenter/providers/geminiProvider.ts
📚 Learning: 2025-07-21T01:46:52.880Z
Learnt from: CR
PR: ThinkInAIXYZ/deepchat#0
File: .cursor/rules/llm-agent-loop.mdc:0-0
Timestamp: 2025-07-21T01:46:52.880Z
Learning: Applies to src/main/presenter/llmProviderPresenter/index.ts : The main Agent loop should buffer text content, handle tool call events, format tool results for the next LLM call, and manage conversation continuation logic.
Applied to files:
src/main/presenter/llmProviderPresenter/providers/geminiProvider.ts
🧬 Code graph analysis (1)
src/main/presenter/llmProviderPresenter/providers/geminiProvider.ts (1)
src/shared/presenter.d.ts (2)
LLM_PROVIDER(512-532)IConfigPresenter(360-483)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build-check (x64)
🔇 Additional comments (3)
src/main/presenter/llmProviderPresenter/providers/geminiProvider.ts (3)
18-25: Switch to IConfigPresenter import — LGTMType-level import change aligns providers with shared presenter interface and reduces coupling.
646-665: Parity: consider applying safetySettings in non-streaming completionsStreaming path applies safety settings; non-streaming completions currently do not. For consistency (and policy compliance), optionally include getFormattedSafetySettings() here as well.
Would you like me to patch completions/summaries/generateText to include safetySettings when available?
153-160: The script has been executed; please share its output so we can verify the BaseLLMProvider constructor signature.
|
This is an excellent refactor. I've done an initial round of testing on the main features, and so far everything looks solid—no major issues spotted. The splash screen styling could use a bit more polish, but that can definitely be improved in a follow-up. Overall, this PR lays a great foundation for the next phase of our project. Awesome work! |
Pull Request Description
Is your feature request related to a problem? Please describe.
The previous application startup process was monolithic, with most of the logic concentrated in
src/main/index.ts. This made the startup sequence rigid, difficult to extend, and hard to debug. Furthermore, it lacked user feedback during initialization, which could lead to a poor user experience if the application was slow to start, making it seem unresponsive.Describe the solution you'd like
This PR introduces a comprehensive architectural refactoring of the application's startup and shutdown process by implementing a modern, hook-based Lifecycle Management system.
Key features of this solution include:
INIT,BEFORE_START,READY,AFTER_START) and modular hooks. Each hook handles a specific task (e.g., database initialization, configuration setup, window creation), making the process more organized and extensible.LifecycleErrorHandleris introduced, providing robust error handling with features like automatic retries, timeouts, and graceful degradation for non-critical tasks.Presenteris refactored into a singleton, and its dependencies are injected via the lifecycle context. This reduces coupling and improves the overall architecture.UI/UX changes for Desktop Application
This PR introduces a new splash screen to improve the application's startup experience.
Platform Compatibility Notes
Additional context
This is a foundational architectural change that significantly improves the structure and robustness of the main process. It establishes a clean and scalable pattern for adding future functionalities to the application's startup and shutdown sequences.
Pull Request Description (中文)
你的功能请求是否与某个问题有关?请描述一下。
先前的应用启动过程是“单体式”的,大部分逻辑都集中在
src/main/index.ts中。这使得启动序列僵化、难以扩展和调试。此外,它在初始化过程中缺乏用户反馈,如果应用启动缓慢,可能会让用户感觉程序无响应,导致糟糕的用户体验。请描述你希望的解决方案
此 PR 通过实现一个现代化的、基于钩子(Hook)的生命周期管理系统,对应用的启动和关闭流程进行了全面的架构重构。
该解决方案的主要特点包括:
INIT,BEFORE_START,READY,AFTER_START)和模块化的钩子。每个钩子处理一个特定的任务(例如,数据库初始化、配置设置、窗口创建),使整个过程更有条理、更易于扩展。LifecycleErrorHandler,它提供了强大的错误处理功能,如自动重试、超时和对非关键任务的优雅降级。Presenter被重构为单例模式,其依赖项通过生命周期上下文注入。这降低了耦合度,并改善了整体架构。桌面应用程序的 UI/UX 更改
此 PR 引入了一个新的启动画面,以改善应用的启动体验。
下面是一个每个步骤增加1s延迟的演示。
splash.mp4
平台兼容性注意事项
附加背景
这是一个基础性的架构变更,显著改善了主进程的结构和健壮性。它为将来在应用的启动和关闭序列中添加新功能建立了一个清晰且可扩展的模式。
Summary by CodeRabbit
New Features
Improvements
Behavior Changes
Documentation
Chores