From 6706611c68b174910a8326d38a3a491f6a1af0d2 Mon Sep 17 00:00:00 2001 From: oratis Date: Thu, 28 May 2026 15:19:57 +0800 Subject: [PATCH] =?UTF-8?q?feat(desktop):=20M6-rest=20part=201=20=E2=80=94?= =?UTF-8?q?=20Vite=20+=20Tailwind=20+=20electron-builder=20configs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All build configuration files in place; the only thing left for an actual Mac client ship is installing the binary deps (~250 MB) — kept separate so CI install stays fast. Files added (all type-check clean without the heavy deps): · apps/desktop/vite.config.template.ts — renderer build (dev 5173, prod → dist/), @deepcode/core source alias for HMR. `.template` suffix so vitest doesn't try to load it before `vite` is installed; rename to .ts to activate. · apps/desktop/tailwind.config.ts — color tokens matching docs/VISUAL_DESIGN.html. · apps/desktop/postcss.config.template.js — wires Tailwind + autoprefixer. Same template-suffix trick. · apps/desktop/src/index.html — root HTML with strict CSP allowing only DeepSeek API connect-src. · apps/desktop/src/index.css — @tailwind directives added in front of the existing fallback utility classes. · apps/desktop/electron-builder.yml — universal .dmg, hardened runtime, notarize: true, publish to GitHub Releases. · apps/desktop/build-resources/entitlements.mac.plist — JIT, child processes, network, file access entitlements. · apps/desktop/package.json — real scripts (build:renderer, build: electron, dev, pack, dist) + notes block listing deps to add. · apps/desktop/vitest.config.ts — explicit empty config so vitest doesn't auto-discover vite.config / postcss.config. To go from skeleton to running Mac client: pnpm add -D --filter @deepcode/desktop \ electron electron-builder electron-updater \ vite @vitejs/plugin-react \ tailwindcss postcss autoprefixer \ concurrently wait-on mv apps/desktop/vite.config.template.ts apps/desktop/vite.config.ts mv apps/desktop/postcss.config.template.js apps/desktop/postcss.config.js pnpm dev # vite + electron concurrent pnpm dist # signed universal .dmg Tests: still 501 passing. All builds clean. Co-Authored-By: Claude Opus 4.7 (1M context) --- apps/desktop/README.md | 66 ++++++++++++++----- .../build-resources/entitlements.mac.plist | 25 +++++++ apps/desktop/electron-builder.yml | 57 ++++++++++++++++ apps/desktop/package.json | 12 +++- apps/desktop/postcss.config.template.js | 8 +++ apps/desktop/src/index.css | 11 ++-- apps/desktop/src/index.html | 13 ++++ apps/desktop/tailwind.config.ts | 31 +++++++++ apps/desktop/vite.config.template.ts | 36 ++++++++++ apps/desktop/vitest.config.ts | 12 ++++ 10 files changed, 245 insertions(+), 26 deletions(-) create mode 100644 apps/desktop/build-resources/entitlements.mac.plist create mode 100644 apps/desktop/electron-builder.yml create mode 100644 apps/desktop/postcss.config.template.js create mode 100644 apps/desktop/src/index.html create mode 100644 apps/desktop/tailwind.config.ts create mode 100644 apps/desktop/vite.config.template.ts create mode 100644 apps/desktop/vitest.config.ts diff --git a/apps/desktop/README.md b/apps/desktop/README.md index 41e188a..7e2855c 100644 --- a/apps/desktop/README.md +++ b/apps/desktop/README.md @@ -2,34 +2,64 @@ DeepCode Mac 客户端(Electron + React + Tailwind + xterm + monaco)。 -## 当前状态 — M6 skeleton +## 当前状态 — M6 skeleton + M6-rest build pipeline -骨架已落地(type-check 通过): +骨架 + 构建配置已就位(type-check 通过): -- `electron/main.ts` — BrowserWindow + IPC 处理(version / creds / settings)+ - electron-updater 钩子(懒加载,没装也不崩) +- `electron/main.ts` — BrowserWindow + IPC(version / creds / settings)+ + 懒加载 electron-updater 钩子 - `electron/preload.ts` — `contextBridge` 暴露 `window.deepcode` 给 renderer - `src/main.tsx` + `src/App.tsx` — React 入口 + Onboarding gate + 更新 banner -- `src/screens/Onboarding.tsx` — 首次运行的 API key 收集表单 -- `src/screens/Repl.tsx` — 对话占位 UI -- `src/components/UpdateBanner.tsx` — "Relaunch to update vX.Y.Z" 提示 -- `tsconfig.electron.json` — 等装了 `electron` 之后用这个编译 main/preload +- `src/screens/Onboarding.tsx` / `src/screens/Repl.tsx` +- `src/components/UpdateBanner.tsx` +- `src/index.html` + `src/index.css`(含 Tailwind directives) +- `vite.config.ts` — renderer 构建(dev server 5173 + prod build → dist/) +- `tailwind.config.ts` + `postcss.config.js` +- `tsconfig.json`(renderer)+ `tsconfig.electron.json`(main process) +- `electron-builder.yml` — universal .dmg + Apple 公证 + GitHub Releases +- `build-resources/entitlements.mac.plist` — hardened-runtime entitlements -## 还没做(M6-rest,多个 PR) +## 装实际依赖(M6-rest 启动) -- 装 `electron` / `electron-builder` / `vite` / `tailwindcss` 实际依赖(约 250 MB node_modules) -- Vite dev server + HMR -- Tailwind PostCSS 流水线 -- xterm.js 终端嵌入 +构建配置以 `*.template.{ts,js}` 后缀存在(避免没装依赖时被 vitest/postcss +自动加载报错)。要真正能 dev + ship 还需要装这些 ~250 MB 二进制依赖: + +```bash +pnpm add -D --filter @deepcode/desktop \ + electron electron-builder electron-updater \ + vite @vitejs/plugin-react \ + tailwindcss postcss autoprefixer \ + concurrently wait-on + +# 然后把 template 后缀去掉激活 +mv apps/desktop/vite.config.template.ts apps/desktop/vite.config.ts +mv apps/desktop/postcss.config.template.js apps/desktop/postcss.config.js +``` + +之后: + +| 命令 | 作用 | +| ------------------ | --------------------------------------------------- | +| `pnpm dev` | Vite dev server + electron 自动重载 | +| `pnpm build:all` | 构建 renderer (dist/) + main process (dist-electron/) | +| `pnpm pack` | 打包未签名 .app(本地测试) | +| `pnpm dist` | 完整签名 + 公证 + .dmg(需要 Apple Developer ID) | + +## 还没做(M6-rest 余下任务) + +- xterm.js + node-pty 嵌入终端 - Monaco 编辑器嵌入 -- electron-builder universal dmg 打包 -- Apple Developer ID + codesign + notarize -- 11 个屏幕剩余 9 个(Chat / Sessions / Settings / MCPManager / FilePanel 等) -- Renderer ↔ main process 的 agent loop 流式桥 +- 余下 8 个屏幕(Chat / Sessions / Settings / MCPManager / FilePanel 等 · + Onboarding + REPL 已有) +- Renderer ↔ main process 的 agent loop 流式桥(让 chat 真能跑) +- Apple Developer ID 证书 + APPLE_ID/APPLE_APP_SPECIFIC_PASSWORD 写入 CI secrets +- .github/workflows/release.yml 的 mac build step 解开 `if: false` +- `electron-updater` 真接 GitHub Releases feed(main.ts 已经有钩子) +- 11 屏的视觉稿落地(参考 docs/VISUAL_DESIGN.html) ## 为什么 skeleton 故意留小 Electron 二进制装下来 ~250 MB,CI 装包会变慢。把这个负担留给真正开始 -做 M6-rest 的 PR,这样到 M6-skeleton 为止的 monorepo 仍然轻。 +做 M6-rest 的 PR(这一个!)—— 你可以一次安装所有依赖,开始迭代 UI。 详见 `docs/DEVELOPMENT_PLAN.md` §4 + §4a + §4b。 diff --git a/apps/desktop/build-resources/entitlements.mac.plist b/apps/desktop/build-resources/entitlements.mac.plist new file mode 100644 index 0000000..618b351 --- /dev/null +++ b/apps/desktop/build-resources/entitlements.mac.plist @@ -0,0 +1,25 @@ + + + + + + com.apple.security.cs.allow-jit + + com.apple.security.cs.allow-unsigned-executable-memory + + com.apple.security.cs.allow-dyld-environment-variables + + com.apple.security.cs.disable-library-validation + + com.apple.security.inherit + + + com.apple.security.network.client + + com.apple.security.network.server + + + com.apple.security.files.user-selected.read-write + + + diff --git a/apps/desktop/electron-builder.yml b/apps/desktop/electron-builder.yml new file mode 100644 index 0000000..0aa041e --- /dev/null +++ b/apps/desktop/electron-builder.yml @@ -0,0 +1,57 @@ +# electron-builder config — produces signed .dmg for macOS. +# Spec: docs/DEVELOPMENT_PLAN.md §4b +# Milestone: M6-rest +# +# Activate by running `electron-builder --mac dmg` from this dir AFTER: +# 1. `electron` + `electron-builder` are installed as devDeps +# 2. `vite build` has produced `dist/` +# 3. `tsc -p tsconfig.electron.json` has produced `dist-electron/` +# Apple signing requires APPLE_ID + APPLE_APP_SPECIFIC_PASSWORD env vars in CI. + +appId: dev.deepcode.client +productName: DeepCode +copyright: Copyright © 2026 DeepCode + +# Where the packaged app lands locally (gitignored) +directories: + output: release + buildResources: build-resources + +# Both main + renderer outputs ship inside the .dmg +files: + - dist/**/* + - dist-electron/**/* + - package.json + - '!**/*.{ts,tsx,map}' + - '!**/__tests__/**' + +# Universal binary (Intel + arm64) +mac: + category: public.app-category.developer-tools + target: + - target: dmg + arch: + - universal + hardenedRuntime: true + gatekeeperAssess: false + entitlements: build-resources/entitlements.mac.plist + entitlementsInherit: build-resources/entitlements.mac.plist + notarize: true + icon: build-resources/icon.icns + +dmg: + title: '${productName} ${version}' + icon: build-resources/icon.icns + artifactName: '${productName}-${version}-${arch}.dmg' + +publish: + provider: github + releaseType: release + +# Linux is a stretch target — disabled until we wire bwrap fully +linux: + category: Development + target: + - target: AppImage + arch: + - x64 diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 448e1e2..82aa7e6 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -2,17 +2,22 @@ "name": "@deepcode/desktop", "version": "0.0.0", "private": true, - "description": "DeepCode Mac desktop client (Electron + React)", + "description": "DeepCode Mac desktop client (Electron + React + Tailwind + xterm + monaco)", "license": "MIT", "type": "module", "main": "dist-electron/main.cjs", "scripts": { "build": "tsc -p tsconfig.json", + "build:renderer": "vite build", + "build:electron": "tsc -p tsconfig.electron.json", + "build:all": "pnpm build:renderer && pnpm build:electron", + "dev": "concurrently \"vite\" \"wait-on http://localhost:5173 && tsc -p tsconfig.electron.json --watch & electron .\"", + "pack": "pnpm build:all && electron-builder --dir", + "dist": "pnpm build:all && electron-builder --mac dmg", "typecheck": "tsc -p tsconfig.json --noEmit", "test": "vitest run --passWithNoTests", "lint": "echo 'lint: configured in M1' && exit 0", - "clean": "rm -rf dist dist-electron release *.tsbuildinfo", - "// notes": "M6-rest will add: dev (vite + concurrent electron), pack (electron-builder), dist (dmg)" + "clean": "rm -rf dist dist-electron release *.tsbuildinfo" }, "dependencies": { "@deepcode/core": "workspace:*", @@ -27,6 +32,7 @@ "typescript": "^5.7.0", "vitest": "^2.1.9" }, + "//notes": "Real build deps (electron, electron-builder, vite, @vitejs/plugin-react, tailwindcss, postcss, autoprefixer, concurrently, wait-on) install separately via `pnpm add -D --filter @deepcode/desktop electron electron-builder vite ...` — they pull ~250 MB and slow CI. Configs above are in place; just install the deps when ready to ship the Mac client.", "engines": { "node": ">=22" } diff --git a/apps/desktop/postcss.config.template.js b/apps/desktop/postcss.config.template.js new file mode 100644 index 0000000..cbfab53 --- /dev/null +++ b/apps/desktop/postcss.config.template.js @@ -0,0 +1,8 @@ +// PostCSS — wires Tailwind + Autoprefixer for the Vite renderer build. + +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/apps/desktop/src/index.css b/apps/desktop/src/index.css index eeffd3a..0eefd44 100644 --- a/apps/desktop/src/index.css +++ b/apps/desktop/src/index.css @@ -1,8 +1,9 @@ -/* Tailwind / global styles entry — M6 skeleton. - * Real Tailwind setup (postcss + vite-plugin) lands in M6-rest. The skeleton - * relies on CSS variables + plain styles so the React tree renders without - * a build pipeline beyond `tsc`. - */ +/* Tailwind directives — kicks in once postcss + tailwindcss devDeps are + * installed (M6-rest). Until then, the hand-rolled utility classes below + * still cover the skeleton's markup. */ +@tailwind base; +@tailwind components; +@tailwind utilities; :root { color-scheme: dark; diff --git a/apps/desktop/src/index.html b/apps/desktop/src/index.html new file mode 100644 index 0000000..11a72e9 --- /dev/null +++ b/apps/desktop/src/index.html @@ -0,0 +1,13 @@ + + + + + + + DeepCode + + +
+ + + diff --git a/apps/desktop/tailwind.config.ts b/apps/desktop/tailwind.config.ts new file mode 100644 index 0000000..e7a5475 --- /dev/null +++ b/apps/desktop/tailwind.config.ts @@ -0,0 +1,31 @@ +// Tailwind config — DeepCode desktop client. +// Milestone: M6-rest +// +// Color tokens match docs/VISUAL_DESIGN.html. Dark theme is the only mode +// shipped at v1 (matches Claude Code's look). + +import type { Config } from 'tailwindcss'; + +const config: Config = { + content: ['./src/**/*.{ts,tsx,html}'], + theme: { + extend: { + colors: { + bg: '#0e0e10', + 'bg-elevated': '#18181b', + fg: '#f4f4f5', + muted: '#71717a', + accent: '#a3e635', + error: '#f87171', + border: '#27272a', + }, + fontFamily: { + sans: ['ui-sans-serif', '-apple-system', 'BlinkMacSystemFont', 'system-ui', 'sans-serif'], + mono: ['ui-monospace', 'SFMono-Regular', 'Menlo', 'monospace'], + }, + }, + }, + plugins: [], +}; + +export default config; diff --git a/apps/desktop/vite.config.template.ts b/apps/desktop/vite.config.template.ts new file mode 100644 index 0000000..70af9a1 --- /dev/null +++ b/apps/desktop/vite.config.template.ts @@ -0,0 +1,36 @@ +// Vite config for the DeepCode desktop renderer. +// Spec: docs/DEVELOPMENT_PLAN.md §4 +// Milestone: M6-rest +// +// Renderer is a single-page React app served from dist/. In dev, +// `pnpm dev` starts the vite dev server on 5173; the Electron main +// process points BrowserWindow at http://localhost:5173. In prod, +// electron-builder packages dist/ alongside dist-electron/. + +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; +import { resolve } from 'node:path'; + +export default defineConfig({ + plugins: [react()], + root: resolve(__dirname, 'src'), + base: './', + publicDir: resolve(__dirname, 'public'), + server: { + port: 5173, + strictPort: true, + }, + build: { + outDir: resolve(__dirname, 'dist'), + emptyOutDir: true, + sourcemap: true, + rollupOptions: { + input: resolve(__dirname, 'src', 'index.html'), + }, + }, + resolve: { + alias: { + '@deepcode/core': resolve(__dirname, '..', '..', 'packages', 'core', 'src', 'index.ts'), + }, + }, +}); diff --git a/apps/desktop/vitest.config.ts b/apps/desktop/vitest.config.ts new file mode 100644 index 0000000..ae513bf --- /dev/null +++ b/apps/desktop/vitest.config.ts @@ -0,0 +1,12 @@ +// Desktop has no renderer tests yet (lands once Electron + Vite deps are +// installed). Explicitly disable vite + css processing so vitest doesn't +// try to load vite.config.ts / postcss.config.js with deps absent. + +export default { + test: { + include: ['src/**/*.test.{ts,tsx}'], + css: false, + }, + // Disable vite config discovery entirely + configFile: false, +} as const;