Skip to content

Conversation

@hllshiro
Copy link
Collaborator

@hllshiro hllshiro commented Aug 28, 2025

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:

  • Modular Startup: The startup process is now divided into distinct phases (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.
  • Enhanced Resilience: A new LifecycleErrorHandler is introduced, providing robust error handling with features like automatic retries, timeouts, and graceful degradation for non-critical tasks.
  • Improved User Experience: A splash screen is now displayed immediately on launch, providing real-time visual feedback on the startup progress, including the current phase and a progress bar.
  • Decoupling: The core Presenter is 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.

  • Visual Demonstration: Upon launching the application, a splash screen appears, showing the app logo, a progress bar, and a status message that updates as the application proceeds through its initialization phases.
  • Reasoning: The splash screen provides immediate feedback to the user that the application is loading. This prevents the perception of the app being frozen or slow to start, creating a smoother and more professional user experience compared to the previous blank screen during startup.

Platform Compatibility Notes

  • The core lifecycle management logic is platform-agnostic and relies on standard Electron APIs.
  • The changes are expected to be fully compatible with Windows, macOS, and Linux without any platform-specific adjustments beyond standard Electron practices.

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
  • 视觉演示: 启动应用后,会出现一个启动画面,显示应用Logo、一个进度条和一个状态消息。这些信息会随着应用完成其初始化阶段而更新。
  • 原因: 启动画面为用户提供了即时反馈,告知他们应用正在加载。这避免了用户在启动过程中看到白屏而误认为应用已卡死或启动缓慢,从而创造了比以前更流畅、更专业的的用户体验。

平台兼容性注意事项

  • 核心的生命周期管理逻辑是平台无关的,并依赖于标准的 Electron API。
  • 除了标准的 Electron 实践外,这些更改预计将与 Windows、macOS 和 Linux 完全兼容,无需任何特定平台的代码调整。

附加背景
这是一个基础性的架构变更,显著改善了主进程的结构和健壮性。它为将来在应用的启动和关闭序列中添加新功能建立了一个清晰且可扩展的模式。

Summary by CodeRabbit

  • New Features

    • Added a visual splash screen with progress, status messages, and live updates during startup.
  • Improvements

    • Staged, lifecycle-driven startup for more reliable initialization of windows, tray, and shortcuts.
    • Coordinated shutdown sequence for cleaner app exit flows.
  • Behavior Changes

    • Tray "Quit" now exits immediately (no confirmation).
  • Documentation

    • New App Lifecycle Management guide and updated architecture/developer docs.
  • Chores

    • Build/rebrand updated to include splash assets.

- 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和测试文件,降低依赖复杂度
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 28, 2025

Walkthrough

Introduces 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

Cohort / File(s) Change summary
Lifecycle core & manager
src/main/presenter/lifecyclePresenter/index.ts, src/main/presenter/lifecyclePresenter/coreHooks.ts, src/main/presenter/lifecyclePresenter/types.ts, src/shared/lifecycle.ts, src/main/events.ts, src/main/index.ts
Add LifecycleManager, lifecycle phase enum and event payload types, LIFECYCLE_EVENTS, startup/shutdown orchestration, hook registration/ordering, splash integration, and rewire main startup to use the lifecycle system.
Lifecycle hooks collection
src/main/presenter/lifecyclePresenter/hooks/*, src/main/presenter/lifecyclePresenter/hooks/index.ts
Add centralized hook exports and many concrete hooks across phases (init, before-start, ready, after-start, before-quit) for config/db init, protocol registration, presenter init, event wiring, window/tray setup, and shutdown sequencing.
Splash window & renderer
src/main/presenter/lifecyclePresenter/SplashWindowManager.ts, src/preload/splash.ts, src/renderer/splash/*, tsconfig.web.json, electron.vite.config.ts
Add SplashWindowManager, splash preload script, splash renderer (HTML, Vue component, bootstrap, typings), include splash entries in Vite build and TS config.
Presenter refactor & wiring
src/main/presenter/index.ts, src/main/presenter/lifecyclePresenter/DatabaseInitializer.ts, src/main/index.ts
Refactor Presenter to be lifecycle-initialized via getInstance(lifecycleManager), add DatabaseInitializer, and centralize startup flow through LifecycleManager.
Presenter submodules & window APIs
src/main/presenter/floatingButtonPresenter/index.ts, src/main/presenter/windowPresenter/index.ts, src/main/presenter/shortcutPresenter.ts, src/main/presenter/trayPresenter.ts
Convert many presenters to use interface dependencies (IConfigPresenter etc.), update constructor signatures/fields, add window APIs (setApplicationQuitting, floating window controls), and make tray Quit unconditional (app.quit()).
LLM providers: type changes
src/main/presenter/llmProviderPresenter/baseProvider.ts, src/main/presenter/llmProviderPresenter/index.ts, src/main/presenter/llmProviderPresenter/providers/*
Replace concrete ConfigPresenter with IConfigPresenter across base provider, presenter, and all provider implementations (constructor/type changes only).
Lifecycle hook implementations
src/main/presenter/lifecyclePresenter/hooks/init/*, .../beforeStart/*, .../ready/*, .../after-start/*, .../beforeQuit/*
Add concrete hook implementations: configInitHook, databaseInitHook, protocolRegistrationHook, presenterInitHook, eventListenerSetupHook, traySetupHook, windowCreationHook, presenterDestroyHook, trayDestroyHook, floatingDestroyHook, builtinKnowledgeDestroyHook, windowQuittingHook.
Shared public types & declarations
src/shared/presenter.d.ts
Extend shared presenter declarations with lifecycle interfaces/types (ILifecycleManager, ISplashWindowManager, LifecycleContext, LifecycleHook, LifecycleState, LifecycleEventStats), expand presenter interfaces and MCP bridge methods, and make MCPToolCall.server optional.
Build / config / scripts
.env.example, electron.vite.config.ts, tsconfig.web.json, scripts/rebrand.js
Add VITE_APP_LIFECYCLE_HOOK_DELAY=0 to .env.example; include splash entries in Vite config and TS config; update rebrand script to include splash HTML.
Docs
docs/app-lifecycle.md, docs/deepchat-architecture-overview.md, docs/mcp-architecture.md, docs/developer-guide.md
Add App Lifecycle Management doc; update architecture overview and MCP init flow to lifecycle-driven model; add cross-reference in developer guide.

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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60–90 minutes

Possibly related PRs

Poem

I thump my nose: "Phases set right,"
Hooks queue up and hop in time.
A tiny splash with morning light,
Presenter wakes and tabs align.
I twitch my whiskers — startup's fine! 🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/app-lifecycle-management

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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbit in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbit in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbit gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbit read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbit help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbit ignore or @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbit summary or @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbit or @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Collaborator

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

coderabbitai[bot]

This comment was marked as resolved.

- 删除冗余的 LifecycleErrorHandler,实现错误处理内嵌在 LifecycleManager 中
- 新增生命周期钩子失败事件 HOOK_FAILED,丰富事件类型定义
- 优化生命周期阶段事件通知,简化事件数据结构
- 改进钩子执行逻辑,区分关键钩子和非关键钩子错误处理
- 启用钩子执行开始和完成的事件通知,增加失败事件通知和日志打印
- 移除 SplashWindowManager 中的无效阶段消息,调整启动和关闭流程事件
- 修改启动失败时通过对话框提示错误并允许退出应用
- 简化应用关闭流程,合并钩子失败日志和错误事件处理
- 移除旧有未使用的接口和方法,提升代码整洁性和可维护性
- 将 presenterInitHook 和 windowCreationHook 标记为关键钩子保证执行稳定
…nAIXYZ/deepchat into refactor/app-lifecycle-management
coderabbitai[bot]

This comment was marked as resolved.

… hooks

- 修改主进程入口,替换 lifecycle 导入路径至 presenter 目录
- 调整hooks目录结构,分类存放
- 简化 window-all-closed 事件处理,保留应用关闭逻辑以应对意外情况
- 将多个关闭前事件迁移到hook
coderabbitai[bot]

This comment was marked as resolved.

- 为 app.quit() 添加注释,明确退出触发原因
- 在生命周期管理器中新增强制退出事件监听,以支持更新时强制退出
- WindowPresenter 中移除重复退出事件监听,改为管理退出状态标志
- 增加 WindowPresenter 的退出状态设置方法和悬浮聊天窗相关接口声明
coderabbitai[bot]

This comment was marked as resolved.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 event

Also applies to: 41-41, 62-62


43-43: Use .js extension for splash preload path.

The preload script will be emitted as splash.js by the bundler, not splash.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 returns Promise<void>, but the aggregator's destroy() method in src/main/presenter/index.ts doesn'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 shutdown

Expose 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 typing

Keep 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 context

Returning 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 module

 File: 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 01a3707 and cff6e79.

📒 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.ts
  • src/main/presenter/lifecyclePresenter/hooks/ready/presenterInitHook.ts
  • src/main/presenter/lifecyclePresenter/DatabaseInitializer.ts
  • src/main/presenter/lifecyclePresenter/SplashWindowManager.ts
  • src/main/presenter/windowPresenter/index.ts
  • src/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.ts
  • src/main/presenter/lifecyclePresenter/hooks/ready/presenterInitHook.ts
  • src/main/presenter/lifecyclePresenter/DatabaseInitializer.ts
  • src/main/presenter/lifecyclePresenter/SplashWindowManager.ts
  • src/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.ts
  • src/main/presenter/lifecyclePresenter/hooks/ready/presenterInitHook.ts
  • src/main/presenter/lifecyclePresenter/DatabaseInitializer.ts
  • src/main/presenter/lifecyclePresenter/SplashWindowManager.ts
  • src/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.ts
  • src/main/presenter/lifecyclePresenter/hooks/ready/presenterInitHook.ts
  • src/main/presenter/lifecyclePresenter/DatabaseInitializer.ts
  • src/main/presenter/lifecyclePresenter/SplashWindowManager.ts
  • src/main/presenter/windowPresenter/index.ts
  • src/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 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.
Provider implementations must use a coreStream method that yields standardized stream events to decouple the main loop from provider-specific details.
The coreStream method 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 as formatMessages, convertToProviderTools, parseFunctionCalls, and prepareFunctionCallPrompt as 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., using convertToProviderTools) 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 appropriate stop_reason in 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.ts
  • src/main/presenter/lifecyclePresenter/hooks/ready/presenterInitHook.ts
  • src/main/presenter/lifecyclePresenter/DatabaseInitializer.ts
  • src/main/presenter/lifecyclePresenter/SplashWindowManager.ts
  • src/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.ts
  • src/main/presenter/lifecyclePresenter/hooks/ready/presenterInitHook.ts
  • src/main/presenter/lifecyclePresenter/DatabaseInitializer.ts
  • src/main/presenter/lifecyclePresenter/SplashWindowManager.ts
  • src/main/presenter/windowPresenter/index.ts
  • src/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.ts
  • src/main/presenter/lifecyclePresenter/hooks/ready/presenterInitHook.ts
  • src/main/presenter/lifecyclePresenter/DatabaseInitializer.ts
  • src/main/presenter/lifecyclePresenter/SplashWindowManager.ts
  • src/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.ts
  • src/main/presenter/windowPresenter/index.ts
  • 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 : 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.ts
  • src/main/presenter/lifecyclePresenter/hooks/ready/presenterInitHook.ts
  • 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/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.ts
  • 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 : 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.ts
  • src/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.ts
  • src/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 IConfigPresenter interface instead of the concrete ConfigPresenter implementation. 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 setApplicationQuitting method 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.

Comment on lines +8 to +12
export interface IDatabaseInitializer {
initialize(): Promise<SQLitePresenter>
migrate(): Promise<void>
validateConnection(): Promise<boolean>
}
Copy link
Contributor

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.

Suggested change
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.

Comment on lines +18 to +22
export class DatabaseInitializer implements IDatabaseInitializer {
private dbPath: string
private password?: string
private database?: SQLitePresenter

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

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().

Comment on lines +33 to +52
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
}
}
Copy link
Contributor

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.

Suggested change
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.

Comment on lines +35 to +87
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
Copy link
Contributor

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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 message

If 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 actions

Be 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 priority

File: 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.

📥 Commits

Reviewing files that changed from the base of the PR and between cff6e79 and 983be45.

📒 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).

Comment on lines +5 to +7
import { LifecycleHook, LifecycleContext } from '@shared/presenter'
import { presenter } from '@/presenter'
import { LifecyclePhase } from '@shared/lifecycle'
Copy link
Contributor

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.

Comment on lines +14 to +21
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()
}
Copy link
Contributor

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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 settings

Comparing 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 load

Currently 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 settings

Gate 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 logs

This 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 log

Keep 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 support

findLast 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 summaries

Aligns 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 parsing

Translate 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 cleanly

Breaking 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 helpers

Multiple @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.

📥 Commits

Reviewing files that changed from the base of the PR and between 983be45 and dbc38a0.

📒 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 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.
Provider implementations must use a coreStream method that yields standardized stream events to decouple the main loop from provider-specific details.
The coreStream method 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 as formatMessages, convertToProviderTools, parseFunctionCalls, and prepareFunctionCallPrompt as 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., using convertToProviderTools) 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 appropriate stop_reason in 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 — LGTM

Type-level import change aligns providers with shared presenter interface and reduces coupling.


646-665: Parity: consider applying safetySettings in non-streaming completions

Streaming 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.

@zerob13
Copy link
Collaborator

zerob13 commented Aug 28, 2025

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!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants