diff --git a/.cursor/rules/project-introduce.mdc b/.cursor/rules/project-introduce.mdc index ce9bde6bc8..c24d8908b5 100644 --- a/.cursor/rules/project-introduce.mdc +++ b/.cursor/rules/project-introduce.mdc @@ -6,18 +6,16 @@ alwaysApply: true You are developing an open-source, modern-design AI chat framework: lobehub(previous lobe-chat). -support platforms: +Supported platforms: - web desktop/mobile - desktop(electron) -- mobile app(react native). coming soon +- mobile app(react native), coming soon logo emoji: 🤯 ## Project Technologies Stack -read [package.json](mdc:package.json) to know all npm packages you can use. - - Next.js 15 - react 19 - TypeScript @@ -33,6 +31,6 @@ read [package.json](mdc:package.json) to know all npm packages you can use. - dayjs for time library - lodash-es for utility library - TRPC for type safe backend -- PGLite for client DB and PostgreSQL for backend DB +- PGLite for client DB and Neon PostgreSQL for backend DB - Drizzle ORM - Vitest for testing diff --git a/.cursor/rules/project-structure.mdc b/.cursor/rules/project-structure.mdc index 801496c990..05b6e1be44 100644 --- a/.cursor/rules/project-structure.mdc +++ b/.cursor/rules/project-structure.mdc @@ -5,11 +5,11 @@ alwaysApply: false # LobeChat Project Structure -note: some not very important files are not shown for simplicity. - ## Complete Project Structure -this project use common monorepo structure. The workspace packages name use `@lobechat/` namespace. +This project uses common monorepo structure. The workspace packages name use `@lobechat/` namespace. + +**note**: some not very important files are not shown for simplicity. ```plaintext lobe-chat/ @@ -28,10 +28,12 @@ lobe-chat/ │ │ │ ├── schemas/ │ │ │ └── repositories/ │ ├── model-bank/ +│ │ └── src/ +│ │ └── aiModels/ │ ├── model-runtime/ │ │ └── src/ -│ │ ├── openai/ -│ │ └── anthropic/ +│ │ ├── core/ +│ │ └── providers/ │ ├── types/ │ │ └── src/ │ │ ├── message/ @@ -96,14 +98,14 @@ lobe-chat/ - UI Components: `src/components`, `src/features` - Global providers: `src/layout` - Zustand stores: `src/store` -- Client Services: `src/services/` +- Client Services: `src/services/` cross-platform services - clientDB: `src/services//client.ts` - serverDB: `src/services//server.ts` - API Routers: - `src/app/(backend)/webapi` (REST) - `src/server/routers/{edge|lambda|async|desktop|tools}` (tRPC) - Server: - - Services(can access serverDB): `src/server/services` + - Services(can access serverDB): `src/server/services` server-used-only services - Modules(can't access db): `src/server/modules` (Server only Third-party Service Module) - Database: - Schema (Drizzle): `packages/database/src/schemas` @@ -113,8 +115,8 @@ lobe-chat/ ## Data Flow Architecture -- **Browser/PWA**: React UI → Client Service → Direct Model Access → PGLite (Web WASM) -- **Server**: React UI → Client Service → tRPC Lambda → Server Services → PostgreSQL (Remote) +- **Web with ClientDB**: React UI → Client Service → Direct Model Access → PGLite (Web WASM) +- **Web with ServerDB**: React UI → Client Service → tRPC Lambda → Server Services → PostgreSQL (Remote) - **Desktop**: - Cloud sync disabled: Electron UI → Client Service → tRPC Lambda → Local Server Services → PGLite (Node WASM) - Cloud sync enabled: Electron UI → Client Service → tRPC Lambda → Cloud Server Services → PostgreSQL (Remote) diff --git a/.cursor/rules/typescript.mdc b/.cursor/rules/typescript.mdc index 5f8037e6aa..0b65b06f0c 100644 --- a/.cursor/rules/typescript.mdc +++ b/.cursor/rules/typescript.mdc @@ -29,16 +29,11 @@ alwaysApply: false ## Code Structure and Readability -- Refactor repeated logic into reusable functions. - Prefer object destructuring when accessing and using properties. - Use consistent, descriptive naming; avoid obscure abbreviations. - Use semantically meaningful variable, function, and class names. - Replace magic numbers or strings with well-named constants. -- Keep meaningful code comments; do not remove them when applying edits. Update comments when behavior changes. -- Ensure JSDoc comments accurately reflect the implementation. -- Look for opportunities to simplify or modernize code with the latest JavaScript/TypeScript features where it improves clarity. - Defer formatting to tooling; ignore purely formatting-only issues and autofixable lint problems. -- Respect project Prettier settings. ## UI and Theming @@ -50,15 +45,14 @@ alwaysApply: false ## Performance - Prefer `for…of` loops to index-based `for` loops when feasible. -- Decide whether callbacks should be debounced or throttled based on UX and performance needs. -- Reuse existing npm packages rather than reinventing the wheel (e.g., `lodash-es/omit`). +- Reuse existing utils inside `packages/utils` or installed npm packages rather than reinventing the wheel. - Query only the required columns from a database rather than selecting entire rows. ## Time and Consistency - Instead of calling `Date.now()` multiple times, assign it to a constant once and reuse it to ensure consistency and improve readability. -## Some logging rules +## Logging - Never log user private information like api key, etc - Don't use `import { log } from 'debug'` to log messages, because it will directly log the message to the console. diff --git a/.github/workflows/desktop-pr-build.yml b/.github/workflows/desktop-pr-build.yml index 437868e90c..afc132d75e 100644 --- a/.github/workflows/desktop-pr-build.yml +++ b/.github/workflows/desktop-pr-build.yml @@ -95,7 +95,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [macos-latest, macos-13, windows-2025, ubuntu-latest] + os: [macos-latest, macos-15-intel, windows-2025, ubuntu-latest] steps: - uses: actions/checkout@v5 with: @@ -129,11 +129,11 @@ jobs: run: npm run desktop:build env: # 设置更新通道,PR构建为nightly,否则为stable - UPDATE_CHANNEL: 'nightly' + UPDATE_CHANNEL: "nightly" APP_URL: http://localhost:3015 - DATABASE_URL: 'postgresql://postgres@localhost:5432/postgres' + DATABASE_URL: "postgresql://postgres@localhost:5432/postgres" # 默认添加一个加密 SECRET - KEY_VAULTS_SECRET: 'oLXWIiR/AKF+rWaqy9lHkrYgzpATbW3CtJp3UfkVgpE=' + KEY_VAULTS_SECRET: "oLXWIiR/AKF+rWaqy9lHkrYgzpATbW3CtJp3UfkVgpE=" # macOS 签名和公证配置 CSC_LINK: ${{ secrets.APPLE_CERTIFICATE_BASE64 }} CSC_KEY_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} @@ -152,10 +152,10 @@ jobs: run: npm run desktop:build env: # 设置更新通道,PR构建为nightly,否则为stable - UPDATE_CHANNEL: 'nightly' + UPDATE_CHANNEL: "nightly" APP_URL: http://localhost:3015 - DATABASE_URL: 'postgresql://postgres@localhost:5432/postgres' - KEY_VAULTS_SECRET: 'oLXWIiR/AKF+rWaqy9lHkrYgzpATbW3CtJp3UfkVgpE=' + DATABASE_URL: "postgresql://postgres@localhost:5432/postgres" + KEY_VAULTS_SECRET: "oLXWIiR/AKF+rWaqy9lHkrYgzpATbW3CtJp3UfkVgpE=" NEXT_PUBLIC_DESKTOP_PROJECT_ID: ${{ secrets.UMAMI_NIGHTLY_DESKTOP_PROJECT_ID }} NEXT_PUBLIC_DESKTOP_UMAMI_BASE_URL: ${{ secrets.UMAMI_NIGHTLY_DESKTOP_BASE_URL }} # 将 TEMP 和 TMP 目录设置到 C 盘 @@ -168,10 +168,10 @@ jobs: run: npm run desktop:build env: # 设置更新通道,PR构建为nightly,否则为stable - UPDATE_CHANNEL: 'nightly' + UPDATE_CHANNEL: "nightly" APP_URL: http://localhost:3015 - DATABASE_URL: 'postgresql://postgres@localhost:5432/postgres' - KEY_VAULTS_SECRET: 'oLXWIiR/AKF+rWaqy9lHkrYgzpATbW3CtJp3UfkVgpE=' + DATABASE_URL: "postgresql://postgres@localhost:5432/postgres" + KEY_VAULTS_SECRET: "oLXWIiR/AKF+rWaqy9lHkrYgzpATbW3CtJp3UfkVgpE=" NEXT_PUBLIC_DESKTOP_PROJECT_ID: ${{ secrets.UMAMI_NIGHTLY_DESKTOP_PROJECT_ID }} NEXT_PUBLIC_DESKTOP_UMAMI_BASE_URL: ${{ secrets.UMAMI_NIGHTLY_DESKTOP_BASE_URL }} diff --git a/.github/workflows/release-desktop-beta.yml b/.github/workflows/release-desktop-beta.yml index f27e455a03..d7e5306cd4 100644 --- a/.github/workflows/release-desktop-beta.yml +++ b/.github/workflows/release-desktop-beta.yml @@ -82,7 +82,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [macos-latest, macos-13, windows-2025, ubuntu-latest] + os: [macos-latest, macos-15-intel, windows-2025, ubuntu-latest] steps: - uses: actions/checkout@v5 with: @@ -116,9 +116,9 @@ jobs: run: npm run desktop:build env: APP_URL: http://localhost:3015 - DATABASE_URL: 'postgresql://postgres@localhost:5432/postgres' + DATABASE_URL: "postgresql://postgres@localhost:5432/postgres" # 默认添加一个加密 SECRET - KEY_VAULTS_SECRET: 'oLXWIiR/AKF+rWaqy9lHkrYgzpATbW3CtJp3UfkVgpE=' + KEY_VAULTS_SECRET: "oLXWIiR/AKF+rWaqy9lHkrYgzpATbW3CtJp3UfkVgpE=" # macOS 签名和公证配置 CSC_LINK: ${{ secrets.APPLE_CERTIFICATE_BASE64 }} CSC_KEY_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} @@ -137,8 +137,8 @@ jobs: run: npm run desktop:build env: APP_URL: http://localhost:3015 - DATABASE_URL: 'postgresql://postgres@localhost:5432/postgres' - KEY_VAULTS_SECRET: 'oLXWIiR/AKF+rWaqy9lHkrYgzpATbW3CtJp3UfkVgpE=' + DATABASE_URL: "postgresql://postgres@localhost:5432/postgres" + KEY_VAULTS_SECRET: "oLXWIiR/AKF+rWaqy9lHkrYgzpATbW3CtJp3UfkVgpE=" NEXT_PUBLIC_DESKTOP_PROJECT_ID: ${{ secrets.UMAMI_BETA_DESKTOP_PROJECT_ID }} NEXT_PUBLIC_DESKTOP_UMAMI_BASE_URL: ${{ secrets.UMAMI_BETA_DESKTOP_BASE_URL }} @@ -152,8 +152,8 @@ jobs: run: npm run desktop:build env: APP_URL: http://localhost:3015 - DATABASE_URL: 'postgresql://postgres@localhost:5432/postgres' - KEY_VAULTS_SECRET: 'oLXWIiR/AKF+rWaqy9lHkrYgzpATbW3CtJp3UfkVgpE=' + DATABASE_URL: "postgresql://postgres@localhost:5432/postgres" + KEY_VAULTS_SECRET: "oLXWIiR/AKF+rWaqy9lHkrYgzpATbW3CtJp3UfkVgpE=" NEXT_PUBLIC_DESKTOP_PROJECT_ID: ${{ secrets.UMAMI_BETA_DESKTOP_PROJECT_ID }} NEXT_PUBLIC_DESKTOP_UMAMI_BASE_URL: ${{ secrets.UMAMI_BETA_DESKTOP_BASE_URL }} diff --git a/CLAUDE.md b/CLAUDE.md index 5af69f2029..3bd39b2b2c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -31,28 +31,18 @@ This repository adopts a monorepo structure. see @.cursor/rules/typescript.mdc -### Modify Code Rules - -- **Code Language**: - - For files with existing Chinese comments: Continue using Chinese to maintain consistency - - For new files or files without Chinese comments: MUST use American English. - - eg: new react tsx file and new test file -- Conservative for existing code, modern approaches for new features - ### Testing -Testing work follows the Rule-Aware Task Execution system above. - -- **Required Rule**: `testing-guide/testing-guide.mdc` +- **Required Rule**: read `@.cursor/rules/testing-guide/testing-guide.mdc` before writing tests - **Command**: - web: `bunx vitest run --silent='passed-only' '[file-path-pattern]'` - packages(eg: database): `cd packages/database && bunx vitest run --silent='passed-only' '[file-path-pattern]'` **Important**: -- wrapped the file path in single quotes to avoid shell expansion +- wrap the file path in single quotes to avoid shell expansion - Never run `bun run test` etc to run tests, this will run all tests and cost about 10mins -- If try to fix the same test twice, but still failed, stop and ask for help. +- If trying to fix the same test twice, but still failed, stop and ask for help. ### Typecheck @@ -61,15 +51,9 @@ Testing work follows the Rule-Aware Task Execution system above. ### i18n - **Keys**: Add to `src/locales/default/namespace.ts` -- **Dev**: Translate `locales/zh-CN/namespace.json` locale file only for preview +- **Dev**: Translate `locales/zh-CN/namespace.json` and `locales/en-US/namespace.json` locales file only for dev preview - DON'T run `pnpm i18n`, let CI auto handle it ## Rules Index -Some useful rules of this project. Read them when needed. - -**IMPORTANT**: All rule files referenced in this document are located in the `.cursor/rules/` directory. Throughout this document, rule files are referenced by their filename only for brevity. - -### 📋 Complete Rule Files - Some useful project rules are listed in @.cursor/rules/rules-index.mdc diff --git a/apps/desktop/build/Icon-beta.Assets.car b/apps/desktop/build/Icon-beta.Assets.car new file mode 100644 index 0000000000..998608f7ef Binary files /dev/null and b/apps/desktop/build/Icon-beta.Assets.car differ diff --git a/apps/desktop/build/Icon-dev.Assets.car b/apps/desktop/build/Icon-dev.Assets.car new file mode 100644 index 0000000000..59613d67c6 Binary files /dev/null and b/apps/desktop/build/Icon-dev.Assets.car differ diff --git a/apps/desktop/build/Icon-nightly.Assets.car b/apps/desktop/build/Icon-nightly.Assets.car new file mode 100644 index 0000000000..65378cc0c4 Binary files /dev/null and b/apps/desktop/build/Icon-nightly.Assets.car differ diff --git a/apps/desktop/build/Icon.Assets.car b/apps/desktop/build/Icon.Assets.car new file mode 100644 index 0000000000..fd2c2013db Binary files /dev/null and b/apps/desktop/build/Icon.Assets.car differ diff --git a/apps/desktop/electron-builder.js b/apps/desktop/electron-builder.js index eb69a966a2..73e50b58db 100644 --- a/apps/desktop/electron-builder.js +++ b/apps/desktop/electron-builder.js @@ -1,5 +1,7 @@ const dotenv = require('dotenv'); +const fs = require('node:fs/promises'); const os = require('node:os'); +const path = require('node:path'); dotenv.config(); @@ -32,11 +34,50 @@ const getProtocolScheme = () => { const protocolScheme = getProtocolScheme(); +// Determine icon file based on version type +const getIconFileName = () => { + if (isNightly) return 'Icon-nightly'; + if (isBeta) return 'Icon-beta'; + return 'Icon'; +}; + /** * @type {import('electron-builder').Configuration} * @see https://www.electron.build/configuration */ const config = { + /** + * AfterPack hook to copy pre-generated Liquid Glass Assets.car for macOS 26+ + * @see https://github.com/electron-userland/electron-builder/issues/9254 + * @see https://github.com/MultiboxLabs/flow-browser/pull/159 + * @see https://github.com/electron/packager/pull/1806 + */ + afterPack: async (context) => { + // Only process macOS builds + if (context.electronPlatformName !== 'darwin') { + return; + } + + const iconFileName = getIconFileName(); + const assetsCarSource = path.join(__dirname, 'build', `${iconFileName}.Assets.car`); + const resourcesPath = path.join( + context.appOutDir, + `${context.packager.appInfo.productFilename}.app`, + 'Contents', + 'Resources', + ); + const assetsCarDest = path.join(resourcesPath, 'Assets.car'); + + try { + await fs.access(assetsCarSource); + await fs.copyFile(assetsCarSource, assetsCarDest); + console.log(`✅ Copied Liquid Glass icon: ${iconFileName}.Assets.car`); + } catch { + // Non-critical: Assets.car not found or copy failed + // App will use fallback .icns icon on all macOS versions + console.log(`⏭️ Skipping Assets.car (not found or copy failed)`); + } + }, appId: isNightly ? 'com.lobehub.lobehub-desktop-nightly' : isBeta @@ -81,6 +122,7 @@ const config = { compression: 'maximum', entitlementsInherit: 'build/entitlements.mac.plist', extendInfo: { + CFBundleIconName: 'AppIcon', CFBundleURLTypes: [ { CFBundleURLName: 'LobeHub Protocol',