lobehub/apps/desktop/electron-builder.mjs
Innei 687b36c81c
♻️ refactor: migrate frontend from Next.js App Router to Vite SPA (#12404)
* init plan

* 📝 docs: update SPA plan for dev mode Worker cross-origin handling

- Clarified the handling of Worker cross-origin issues in dev mode, emphasizing the need for `workerPatch` to wrap cross-origin URLs as blob URLs.
- Enhanced the explanation of the dev mode's resource URL rewriting process for better understanding.

Signed-off-by: Innei <tukon479@gmail.com>

* 🔧 refactor: Phase 1 - 环境变量整治

- Fix Pyodide env var mismatch (NEXT_PUBLIC_PYPI_INDEX_URL → pythonEnv.NEXT_PUBLIC_PYODIDE_PIP_INDEX_URL)
- Consolidate python.ts to use pythonEnv instead of direct process.env
- Remove NEXT_PUBLIC_ prefix from server-side MARKET_BASE_URL (5 files)

* 🏗️ chore: Phase 2 - Vite 工程搭建

- Add vite.config.ts with dual build (desktop/mobile via MOBILE env)
- Add index.html SPA template with __SERVER_CONFIG__ placeholder
- Add entry.desktop.tsx and entry.mobile.tsx SPA entry points
- Add dev:spa, dev:spa:mobile, build:spa, build:spa:copy scripts
- Install @vitejs/plugin-react and linkedom

* ♻️ refactor: Phase 3 - 第一方包 Next.js 解耦

- Replace next/link with <a> in builtin-tool-web-browsing (4 files, external links)
- Replace next/image with <img> in builtin-tool-agent-builder/InstallPlugin.tsx
- Add Vite import.meta.env compat for isDesktop in const/version.ts, builtin-tool-gtd, builtin-tool-group-management

* ♻️ refactor: Phase 4a - Auth 页面改用直接 next/navigation 和 next/link

- 9 auth files: @/libs/next/navigation → next/navigation
- 5 auth files: @/libs/next/Link → next/link
- Auth pages remain in Next.js App Router, need direct Next.js imports

* ♻️ refactor: Phase 4b - Next.js 抽象层替换为 react-router-dom/vanilla React

- navigation.ts: useRouter/usePathname/useSearchParams/useParams → react-router-dom
- navigation.ts: redirect/notFound → custom error throws
- navigation.ts: useServerInsertedHTML → no-op for SPA
- Link.tsx: next/link → react-router-dom Link adapter (href→to, external→<a>)
- Image.tsx: next/image → <img> wrapper with fill/style support
- dynamic.tsx: next/dynamic → React.lazy + Suspense wrapper

*  feat: Phase 5 - 新建 SPAGlobalProvider

- Create SPAServerConfig type (analyticsConfig, clientEnv, theme, featureFlags, locale)
- Add window.__SERVER_CONFIG__ and __MOBILE__ to global.d.ts
- Create SPAGlobalProvider (client-only Provider tree mirroring GlobalProvider)
- Includes AuthProvider for user session support
- Update entry.desktop.tsx and entry.mobile.tsx to wrap with SPAGlobalProvider

* ♻️ refactor: add SPA catch-all route handler with Vite dev proxy

- Create (spa)/[[...path]]/route.ts for serving SPA HTML
- Dev mode: proxy Vite dev server, rewrite asset URLs, inject Worker patch
- Prod mode: read pre-built HTML templates
- Build SPAServerConfig with analytics, theme, clientEnv, featureFlags
- Update middleware to pass SPA routes through to catch-all

* ♻️ refactor: skip auth checks for SPA routes in middleware

SPA pages are all public (no sensitive data in HTML).
Auth is handled client-side by SPAGlobalProvider's AuthProvider.
Only Next.js auth routes and API endpoints go through session checks.

* ♻️ refactor: replace Next.js-specific analytics with vanilla JS

- Google.tsx: replace @next/third-parties/google with direct gtag script
- ReactScan.tsx: replace react-scan/monitoring/next with generic script
- Desktop.tsx: replace next/script with native script injection

* ♻️ refactor: migrate @t3-oss/env-nextjs to @t3-oss/env-core

Replace framework-specific env validation with framework-agnostic version.
Add clientPrefix where client schemas exist.

* ♻️ refactor: replace next-mdx-remote/rsc with react-markdown

Use client-side react-markdown for MDX rendering instead of
Next.js RSC-dependent next-mdx-remote.

* 🔧 chore: update build scripts and Dockerfile for SPA integration

- build:docker now includes SPA build + copy steps
- dev defaults to Vite SPA, dev:next for Next.js backend
- Dockerfile copies public/spa/ assets for production
- Add public/spa/ to .gitignore (build artifact)

* 🗑️ chore: remove old Next.js route segment files and serwist PWA

- Delete [variants] page.tsx, error.tsx, not-found.tsx, loading.tsx
- Delete root loading.tsx and empty [[...path]] directory
- Delete unused loaders directory
- Remove @serwist/next PWA wrapper from Next.js config

* plan2

*  feat: add locale detection script to index.html for SPA dev mode

* ♻️ refactor: remove locale and theme from SPAServerConfig

*  feat: add [locale] segment with force-static and SEO meta generation

* ♻️ refactor: remove theme/locale reads from SPAGlobalProvider

*  feat: set vite base to /spa/ for production builds

*  feat: auto-generate spaHtmlTemplates from vite build output

* 🔧 chore: register dev:next task in turbo.json for parallel dev startup

* ♻️ refactor: rename (spa) route group to spa segment, rewrite SPA routes via middleware

*  feat: add Vite-compatible i18n/locale modules with import.meta.glob and resolve aliases

* 🔧 fix: use custom Vite plugin for module redirects instead of resolve.alias

* very important

* build

* 🔧 chore: update build scripts and clean up Vite configuration by removing unused plugin and code

Signed-off-by: Innei <tukon479@gmail.com>

* 🗑️ refactor: remove all electron modifier scripts

Modifiers are no longer needed with Vite SPA renderer build.

*  feat: add Vite renderer entry to electron-vite config

Add renderer build configuration to electron-vite, replacing the old
Next.js shadow workspace build flow. Delete buildNextApp.mts and
moveNextExports.ts, update package.json scripts accordingly.

*  feat: add .desktop suffix files for eager i18n loading

Create 4 .desktop files that use import.meta.glob({ eager: true })
for synchronous locale access in Electron desktop builds, replacing
the async lazy-loading used in web SPA builds.

* 🔧 refactor: adapt Electron main process for Vite renderer

Replace nextExportDir with rendererDir, update protocol from
app://next to app://renderer, simplify file resolution to SPA
fallback pattern, update _next/ asset paths to /assets/.

* 🔧 chore: update electron-builder files config for Vite renderer

Replace dist/next references with dist/renderer, remove Next.js
specific exclusion rules no longer applicable to Vite output.

* 🗑️ chore: remove @ast-grep/napi dependency

No longer needed after removing electron modifier scripts.

* 🔧 refactor: unify isDesktop to __ELECTRON__ compile-time constant

Remove NEXT_PUBLIC_IS_DESKTOP_APP and VITE_IS_DESKTOP_APP env vars.
Unify isDesktop in @lobechat/const using __ELECTRON__ defined by Vite.
Re-export from builtin-tool packages. Scripts use DESKTOP_BUILD.

* update

Signed-off-by: Innei <tukon479@gmail.com>

* 🔧 refactor: use electron-vite ELECTRON_RENDERER_URL instead of hardcoded port 3015

Replace hardcoded http://localhost:3015 with process.env.ELECTRON_RENDERER_URL
injected by electron-vite dev server. Clean up stale Next.js references.

* 🐛 fix: use local renderer-entry shim to resolve Vite root path issue

HTML entry ../../src/entry.desktop.tsx resolves to /src/entry.desktop.tsx
in URL space, which Vite cannot find within apps/desktop/ root. Add a
local shim that imports across root via module resolver instead.

* 🔧 refactor: extract shared renderer Vite config into sharedRendererConfig

Deduplicate plugins (nodeModuleStub, platformResolve, tsconfigPaths) and
define (__MOBILE__, __ELECTRON__, process.env) between root vite.config.ts
and electron.vite.config.ts renderer section.

* 🔧 refactor: move all renderer plugins and optimizeDeps into shared config

sharedRendererPlugins now includes react, codeInspectorPlugin alongside
nodeModuleStub, platformResolve, tsconfigPaths. Add sharedOptimizeDeps
for pre-bundling list. Both root and electron configs consume shared only.

* 🐛 fix: set electron renderer root to monorepo root for correct glob resolution

import.meta.glob with absolute paths (e.g. /node_modules/antd/...) resolved
within apps/desktop/ instead of monorepo root. Change renderer root to ROOT_DIR,
add electronDesktopHtmlPlugin middleware to rewrite / to /apps/desktop/index.html,
and remove the now-unnecessary renderer-entry.ts shim.

* desktop vite !!

Signed-off-by: Innei <tukon479@gmail.com>

* sync import !!

Signed-off-by: Innei <tukon479@gmail.com>

* clean ci!!

Signed-off-by: Innei <tukon479@gmail.com>

* 🔧 refactor: update SPA path structure and clean up dependencies

- Changed the path in .gitignore and related files from [locale] to [variants] for SPA templates.
- Updated index.html to set body height to 100%.
- Cleaned up package.json by removing unused dependencies and reorganizing devDependencies.
- Refactored RendererUrlManager to use a constant for SPA entry HTML path.
- Removed obsolete route.ts file from the SPA structure.
- Adjusted proxy configuration to reflect the new SPA path structure.

Signed-off-by: Innei <tukon479@gmail.com>

* 🔧 chore: update build script to include mobile SPA build

- Modified the build script in package.json to add the mobile SPA build step.
- Ensured the build process accommodates both desktop and mobile SPA versions.

Signed-off-by: Innei <tukon479@gmail.com>

* 🔧 chore: update build scripts and improve file encoding consistency

- Modified the build script in package.json to ensure the SPA copy step runs after the build.
- Updated file encoding in generateSpaTemplates.mts from 'utf-8' to 'utf8' for consistency.

Signed-off-by: Innei <tukon479@gmail.com>

* 🔧 fix: correct Blob import syntax and update global server config type

- Fixed the Blob import syntax in route.ts to ensure proper module loading.
- Updated the global server configuration type in global.d.ts for improved type safety.

Signed-off-by: Innei <tukon479@gmail.com>

* 🔧 test: update RendererUrlManager test to reflect new file path

- Modified the mock implementation in RendererUrlManager.test.ts to check for the updated file path '/mock/export/out/apps/desktop/index.html'.
- Adjusted the expected resolved path in the test to match the new structure.

Signed-off-by: Innei <tukon479@gmail.com>

* 🔧 refactor: remove catch-all example file and update imports

- Deleted the catch-all example file `catch-all.eg.ts` to streamline the codebase.
- Updated import paths in `ClientResponsiveLayout.tsx` and `ClientResponsiveContent/index.tsx` to use the new dynamic import location.
- Added type declarations for HTML templates in `spaHtmlTemplates.d.ts`.
- Adjusted `tsconfig.json` to include the updated file structure.
- Enhanced type definitions in `global.d.ts` and fixed locale loading in `locale.vite.ts`.

Signed-off-by: Innei <tukon479@gmail.com>

* e2e

Signed-off-by: Innei <tukon479@gmail.com>

* 🔧 chore: remove unused build script for Vercel deployment

- Deleted the `build:vercel` script from package.json to streamline the build process.
- Ensured the remaining build scripts are organized and relevant.

Signed-off-by: Innei <tukon479@gmail.com>

* 🔧 config: update Vite build input for mobile support

- Changed the build input path in vite.config.ts to conditionally use 'index.mobile.html' for mobile builds, enhancing support for mobile SPA versions.

Signed-off-by: Innei <tukon479@gmail.com>

* 🔧 feat: add compatibility checks for import maps and cascade layers

- Implemented functions to check for browser support of import maps and CSS cascade layers.
- Redirected users to a compatibility page if their browser does not support the required features.
- Updated the build script in package.json to use the experimental analyze command for better performance.

Signed-off-by: Innei <tukon479@gmail.com>

* chore: rename

Signed-off-by: Innei <tukon479@gmail.com>

* 🔧 feat: refactor authentication layout and introduce global providers

- Created a new `RootLayout` component to streamline the layout structure.
- Removed the old layout file for variants and integrated necessary features into the new layout.
- Added `AuthGlobalProvider` to manage authentication context and server configurations.
- Introduced language and theme selection components for enhanced user experience.
- Updated various components to utilize the new context and improve modularity.

Signed-off-by: Innei <tukon479@gmail.com>

* 🔧 config: exclude build artifacts from serverless functions

- Updated the `next.config.ts` to exclude SPA, desktop, and mobile build artifacts from serverless functions.
- Added paths for `public/spa/**`, `dist/**`, `apps/desktop/build/**`, and `packages/database/migrations/**` to the exclusion list.

Signed-off-by: Innei <tukon479@gmail.com>

* 🔧 config: refine exclusion of build artifacts from serverless functions

- Updated `next.config.ts` to specify exclusion paths for desktop and mobile build artifacts.
- Changed exclusions from `dist/**` and `apps/desktop/build/**` to `dist/desktop/**`, `dist/mobile/**`, and `apps/desktop/**` for better clarity and organization.

Signed-off-by: Innei <tukon479@gmail.com>

* 🔧 fix: update BrowserRouter basename for local development

- Modified the `ClientRouter` component to conditionally set the `basename` of `BrowserRouter` based on the `__DEBUG_PROXY__` variable, improving local development experience.

Signed-off-by: Innei <tukon479@gmail.com>

* 🔧 feat: implement mobile SPA workflow and S3 asset management

- Added a new workflow for building and uploading mobile SPA assets to S3, including environment variable configurations in `.env.example`.
- Updated `package.json` to include a new script for the mobile SPA workflow.
- Enhanced the Vite configuration to support dynamic CDN base paths.
- Refactored the template generation script to handle mobile HTML templates more effectively.
- Introduced new modules for uploading assets to S3 and generating mobile HTML templates.

Signed-off-by: Innei <tukon479@gmail.com>

* 🐛 fix: extract origin from MOBILE_S3_PUBLIC_DOMAIN to prevent double key prefix

* 🔧 fix: update mobile HTML template to use the latest asset versions

- Modified the mobile HTML template to reference the updated JavaScript asset version for improved functionality.
- Ensured consistency in the template structure while maintaining existing styles and scripts.

Signed-off-by: Innei <tukon479@gmail.com>

* 🔧 chore: update dependencies and refine service worker integration

- Removed outdated dependencies related to Serwist from package.json and tsconfig.json.
- Added vite-plugin-pwa to enhance PWA capabilities in the Vite configuration.
- Updated service worker registration logic in the PWA installation component.
- Introduced a new local development proxy route for debugging purposes.

Signed-off-by: Innei <tukon479@gmail.com>

* 🔧 chore: refactor development scripts and remove Turbo configuration

- Updated the `dev` script in `package.json` to use a new startup sequence script for improved development workflow.
- Removed the outdated `turbo.json` configuration file as it is no longer needed.
- Introduced `devStartupSequence.mts` to manage the startup of Next.js and Vite processes concurrently.

Signed-off-by: Innei <tukon479@gmail.com>

* 🔧 feat: update entry points and introduce debug proxy for local development

- Changed the main entry point in `index.html` from `entry.desktop.tsx` to `entry.web.tsx` for improved web compatibility.
- Added an `initialize.ts` file to enable `immer`'s `enableMapSet` functionality.
- Introduced a new `__DEBUG_PROXY__` variable in global types to support local development proxy features.
- Implemented a debug proxy route to facilitate local development with dynamic HTML injection and script handling.
- Removed outdated mobile routing components to streamline the codebase.

Signed-off-by: Innei <tukon479@gmail.com>

* 🔧 refactor: replace BrowserRouter with RouterProvider for improved routing

- Updated entry points for desktop, mobile, and web to utilize RouterProvider and createAppRouter for better routing management.
- Removed the deprecated renderRoutes function in favor of a more streamlined router configuration.
- Enhanced router setup to support error boundaries and dynamic routing.

Signed-off-by: Innei <tukon479@gmail.com>

* 🔧 refactor: remove direct access handling for SPA routes in proxy configuration

- Eliminated the handling of direct access to pre-rendered SPA pages in the proxy configuration.
- Simplified the request processing logic by removing checks for SPA routes, streamlining the middleware response flow.

Signed-off-by: Innei <tukon479@gmail.com>

* update

* 🔧 refactor: enhance Worker instantiation logic in mobile HTML template

* 🐛 fix: remove duplicate waitForPageWorkspaceReady calls in page CRUD e2e steps

* 🔧 refactor: simplify createTracePayload function by using btoa for base64 encoding

* 🔧 refactor: specify locales in import.meta.glob for dayjs and antd

* 🔧 refactor: replace Node.js Buffer with web-compatible btoa for base64 encoding in file upload

* 🐛 fix: disable consistent-type-imports rule for mdx files to prevent eslint crash

* 🔧 refactor: add height style to root div for consistent layout

* 🔧 refactor: replace btoa with Buffer for base64 encoding in trace and file upload handling

* 🔧 refactor: extract nextjsOnlyRoutes to a separate file for better organization

* 🔧 refactor: enable Immer MapSet plugin in tests for better state management

Signed-off-by: Innei <tukon479@gmail.com>

* 🔧 refactor: integrate sharedRollupOutput configuration and increase cache size for better performance

Signed-off-by: Innei <tukon479@gmail.com>

* 🗑️ chore: remove obsolete desktop.routes.test.ts file as it is no longer needed

Signed-off-by: Innei <tukon479@gmail.com>

* 🐛 fix: use cross-env for env vars in npm scripts (Windows CI)

Co-authored-by: Cursor <cursoragent@cursor.com>

* 🔧 chore: update Dockerfile for web-only build and adjust npm scripts to use pnpm

Signed-off-by: Innei <tukon479@gmail.com>

* 🔧 chore: enhance Dockerfile prebuild process with environment checks and add new dependencies

- Updated Dockerfile to include environment checks before removing desktop-only code.
- Added new dependencies in package.json: @aws-sdk/client-bedrock-runtime, @opentelemetry/auto-instrumentations-node, @opentelemetry/resources, @opentelemetry/sdk-metrics, and ajv.
- Configured Rollup to exclude @aws-sdk/client-bedrock-runtime from the SPA bundle.
- Introduced dockerPrebuild.mts script for environment variable validation and information logging.

Signed-off-by: Innei <tukon479@gmail.com>

* 🔧 chore: enhance Vite and Electron configurations with environment loading and trace encoding improvements

- Updated Vite and Electron configurations to load environment variables using loadEnv.
- Modified trace encoding in utils to use TextEncoder for better compatibility.
- Adjusted sharedRendererConfig to expose only necessary public environment variables.

Signed-off-by: Innei <tukon479@gmail.com>

* 🗑️ chore: remove plans directory (migrated to discussion)

* ♻️ refactor: inject NEXT_PUBLIC_* env per key in Vite define

Co-authored-by: Cursor <cursoragent@cursor.com>

*  feat: add loading screen with animation to enhance user experience

- Introduced a loading screen with a brand logo and animations for better visual feedback during loading times.
- Implemented CSS styles for the loading screen and animations in index.html.
- Removed the loading screen from the DOM once the layout is ready using useLayoutEffect in SPAGlobalProvider.

Signed-off-by: Innei <tukon479@gmail.com>

* 🗑️ chore: remove unnecessary external dependency from Vite configuration

- Eliminated the external dependency '@aws-sdk/client-bedrock-runtime' from the Vite configuration to streamline the build process for the SPA bundle.

Signed-off-by: Innei <tukon479@gmail.com>

*  feat: add web app manifest link in index.html and enable PWA support in Vite configuration

- Added a link to the web app manifest in index.html to enhance PWA capabilities.
- Enabled manifest support in Vite configuration for improved service worker functionality.

Signed-off-by: Innei <tukon479@gmail.com>

* 🔧 chore: update link rel attributes for improved SEO and consistency

- Modified link rel attributes in multiple components to remove 'noreferrer' and standardize to 'nofollow'.
- Adjusted imports in PageContent components for better organization.

Signed-off-by: Innei <tukon479@gmail.com>

* update provider

*  feat: enhance loading experience and update package dependencies

- Added a loading screen with animations and a brand logo in index.html for improved user feedback during loading times.
- Introduced CSS styles for the loading screen and animations.
- Updated package.json files across multiple packages to include "@lobechat/const" as a dependency.

Signed-off-by: Innei <tukon479@gmail.com>

* fix: update proxy

Signed-off-by: Innei <tukon479@gmail.com>

* 🗑️ chore: remove GlobalLayout and Locale components

- Deleted GlobalLayout and Locale components from the GlobalProvider directory to streamline the codebase.
- This removal is part of a refactor to simplify the layout structure and improve maintainability.

Signed-off-by: Innei <tukon479@gmail.com>

* chore: clean up console logs and improve component structure

- Removed unnecessary console log statements from AgentForkTag components in both agent and community directories to enhance code cleanliness.
- Refactored UserAgentList component for better readability by restructuring the useUserDetailContext hook and adjusting the layout of Flexbox components.

Signed-off-by: Innei <tukon479@gmail.com>

* chore: remove console log from MemoryAnalysis component

* chore: update mobile HTML template with new asset links

- Replaced the previous asset links in the mobile HTML template with updated versions to ensure the latest resources are utilized.
- Adjusted the link rel attributes for module preloading to enhance performance and loading efficiency.

Signed-off-by: Innei <tukon479@gmail.com>

* fix: correct variable assignment in createClientTaskThread integration test

- Updated the assignment of the second parent message in the createClientTaskThread integration test to improve clarity and ensure proper data handling.
- Changed the variable name from 'secondParentMsg' to 'inserted' for better context before extracting the first message from the inserted results.

Signed-off-by: Innei <tukon479@gmail.com>

* refactor: simplify authentication check in define-config

- Removed the dependency on the isDesktop variable in the authentication check to streamline the logic.
- Enhanced the clarity of the redirection process for protected routes by focusing solely on the isLoggedIn status.

Signed-off-by: Innei <tukon479@gmail.com>

*  feat(dev): enhance local development setup with debug proxy instructions

- Added detailed instructions for starting the development environment in CLAUDE.md, including commands for SPA and full-stack modes.
- Updated README.md and README.zh-CN.md to reflect new commands and the debug proxy URL for local development.
- Introduced a Vite plugin to print the debug proxy URL upon server start, facilitating easier local development against the production backend.
- Corrected the debug proxy route in entry.web.tsx and define-config.ts for consistency.

This improves the developer experience by providing clear guidance and tools for local development.

Signed-off-by: Innei <tukon479@gmail.com>

* optimize perf

* optimize perf

* optimize perf

* remove speedy plugin

* add dayjs vendor

* Revert "remove speedy plugin"

This reverts commit bf986afeb1.

---------

Signed-off-by: Innei <tukon479@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-28 00:01:01 +08:00

299 lines
9.4 KiB
JavaScript

import fs from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import dotenv from 'dotenv';
import {
copyNativeModules,
copyNativeModulesToSource,
getAsarUnpackPatterns,
getNativeModulesFilesConfig,
} from './native-deps.config.mjs';
dotenv.config();
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const packageJSON = JSON.parse(await fs.readFile(path.join(__dirname, 'package.json'), 'utf8'));
const channel = process.env.UPDATE_CHANNEL;
const arch = os.arch();
const hasAppleCertificate = Boolean(process.env.CSC_LINK);
// 自定义更新服务器 URL (用于 stable 频道)
const updateServerUrl = process.env.UPDATE_SERVER_URL;
console.log(`🚄 Build Version ${packageJSON.version}, Channel: ${channel}`);
console.log(`🏗️ Building for architecture: ${arch}`);
// Channel identity derived solely from UPDATE_CHANNEL env var.
// Adding a new channel won't break stable detection.
const isStable = !channel || channel === 'stable';
const isNightly = channel === 'nightly';
const isBeta = channel === 'beta';
// 根据 channel 配置不同的 publish provider
// - Stable + UPDATE_SERVER_URL: 使用 generic (自定义 HTTP 服务器)
// - Beta/Nightly: 仅使用 GitHub
const getPublishConfig = () => {
const githubProvider = {
owner: 'lobehub',
provider: 'github',
repo: 'lobe-chat',
};
// Stable channel: 使用自定义服务器 (generic provider)
if (isStable && updateServerUrl) {
console.log(`📦 Stable channel: Using generic provider (${updateServerUrl})`);
const genericProvider = {
provider: 'generic',
url: updateServerUrl,
};
// 同时发布到自定义服务器和 GitHub (GitHub 作为备用/镜像)
return [genericProvider, githubProvider];
}
// Beta/Nightly channel: 仅使用 GitHub
console.log(`📦 ${channel || 'default'} channel: Using GitHub provider`);
return [githubProvider];
};
// Keep only these Electron Framework localization folders (*.lproj)
// (aligned with previous Electron Forge build config)
const keepLanguages = new Set(['en', 'en_GB', 'en-US', 'en_US']);
// https://www.electron.build/code-signing-mac#how-to-disable-code-signing-during-the-build-process-on-macos
if (!hasAppleCertificate) {
// Disable auto discovery to keep electron-builder from searching unavailable signing identities
process.env.CSC_IDENTITY_AUTO_DISCOVERY = 'false';
console.log('⚠️ Apple certificate link not found, macOS artifacts will be unsigned.');
}
// 根据版本类型确定协议 scheme
const getProtocolScheme = () => {
if (isNightly) return 'lobehub-nightly';
if (isBeta) return 'lobehub-beta';
return 'lobehub';
};
const protocolScheme = getProtocolScheme();
// Determine icon file based on version type
const getIconFileName = () => {
if (isStable) return 'Icon';
if (isBeta) return 'Icon-beta';
// nightly, canary, and any future pre-release channels share nightly icon
return 'Icon-nightly';
};
/**
* @type {import('electron-builder').Configuration}
* @see https://www.electron.build/configuration
*/
const config = {
/**
* BeforePack hook to resolve pnpm symlinks for native modules.
* This ensures native modules are properly included in the asar archive.
*/
beforePack: async () => {
await copyNativeModulesToSource();
},
/**
* AfterPack hook for post-processing:
* 1. Copy native modules to asar.unpacked (resolving pnpm symlinks)
* 2. Copy Liquid Glass Assets.car for macOS 26+
* 3. Remove unused Electron Framework localizations
*
* @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) => {
const isMac = ['darwin', 'mas'].includes(context.electronPlatformName);
// Determine resources path based on platform
let resourcesPath;
if (isMac) {
resourcesPath = path.join(
context.appOutDir,
`${context.packager.appInfo.productFilename}.app`,
'Contents',
'Resources',
);
} else {
// Windows and Linux: resources is directly in appOutDir
resourcesPath = path.join(context.appOutDir, 'resources');
}
// Copy native modules to asar.unpacked, resolving pnpm symlinks
const unpackedNodeModules = path.join(resourcesPath, 'app.asar.unpacked', 'node_modules');
await copyNativeModules(unpackedNodeModules);
// macOS-specific post-processing
if (!isMac) {
return;
}
const iconFileName = getIconFileName();
const assetsCarSource = path.join(__dirname, 'build', `${iconFileName}.Assets.car`);
const assetsCarDest = path.join(resourcesPath, 'Assets.car');
// Remove unused Electron Framework localizations to reduce app size
const frameworkResourcePath = path.join(
context.appOutDir,
`${context.packager.appInfo.productFilename}.app`,
'Contents',
'Frameworks',
'Electron Framework.framework',
'Versions',
'A',
'Resources',
);
try {
const entries = await fs.readdir(frameworkResourcePath);
await Promise.all(
entries.map(async (file) => {
if (!file.endsWith('.lproj')) return;
const lang = file.split('.')[0];
if (keepLanguages.has(lang)) return;
await fs.rm(path.join(frameworkResourcePath, file), { force: true, recursive: true });
}),
);
} catch {
// Non-critical: folder may not exist depending on packaging details
}
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
? 'com.lobehub.lobehub-desktop-beta'
: 'com.lobehub.lobehub-desktop',
appImage: {
artifactName: '${productName}-${version}.${ext}',
},
// Native modules must be unpacked from asar to work correctly
asarUnpack: getAsarUnpackPatterns(),
detectUpdateChannel: true,
directories: {
buildResources: 'build',
output: 'release',
},
dmg: {
artifactName: '${productName}-${version}-${arch}.${ext}',
background: 'resources/dmg.png',
contents: [
{ type: 'file', x: 150, y: 240 },
{ type: 'link', path: '/Applications', x: 450, y: 240 },
],
iconSize: 80,
window: {
height: 400,
width: 600,
},
},
electronDownload: {
mirror: 'https://npmmirror.com/mirrors/electron/',
},
files: [
'dist',
'resources',
'dist/renderer/**/*',
'!resources/locales',
'!resources/dmg.png',
// Exclude all node_modules first
'!node_modules',
// Then explicitly include native modules using object form (handles pnpm symlinks)
...getNativeModulesFilesConfig(),
],
generateUpdatesFilesForAllChannels: true,
linux: {
category: 'Utility',
maintainer: 'electronjs.org',
target: ['AppImage', 'snap', 'deb', 'rpm', 'tar.gz'],
},
mac: {
compression: 'maximum',
entitlementsInherit: 'build/entitlements.mac.plist',
extendInfo: {
CFBundleIconName: 'AppIcon',
CFBundleURLTypes: [
{
CFBundleURLName: 'LobeHub Protocol',
CFBundleURLSchemes: [protocolScheme],
},
],
NSAppleEventsUsageDescription:
'Application needs to control System Settings to help you grant Full Disk Access automatically.',
NSCameraUsageDescription: "Application requests access to the device's camera.",
NSDocumentsFolderUsageDescription:
"Application requests access to the user's Documents folder.",
NSDownloadsFolderUsageDescription:
"Application requests access to the user's Downloads folder.",
NSMicrophoneUsageDescription: "Application requests access to the device's microphone.",
NSScreenCaptureUsageDescription:
'Application requests access to record and analyze screen content for AI assistance.',
},
gatekeeperAssess: false,
hardenedRuntime: hasAppleCertificate,
notarize: hasAppleCertificate,
...(hasAppleCertificate ? {} : { identity: null }),
target: [
{ arch: [arch === 'arm64' ? 'arm64' : 'x64'], target: 'dmg' },
{ arch: [arch === 'arm64' ? 'arm64' : 'x64'], target: 'zip' },
],
},
npmRebuild: true,
nsis: {
allowToChangeInstallationDirectory: true,
artifactName: '${productName}-${version}-setup.${ext}',
createDesktopShortcut: 'always',
installerHeader: './build/nsis-header.bmp',
installerSidebar: './build/nsis-sidebar.bmp',
oneClick: false,
shortcutName: '${productName}',
uninstallDisplayName: '${productName}',
uninstallerSidebar: './build/nsis-sidebar.bmp',
},
protocols: [
{
name: 'LobeHub Protocol',
schemes: [protocolScheme],
},
],
publish: getPublishConfig(),
// Release notes 配置
// 可以通过环境变量 RELEASE_NOTES 传入,或从文件读取
// 这会被写入 latest-mac.yml / latest.yml 中,供 generic provider 使用
releaseInfo: {
releaseNotes: process.env.RELEASE_NOTES || undefined,
},
win: {
executableName: 'LobeHub',
},
};
export default config;