Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 48 additions & 18 deletions apps/desktop/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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。
25 changes: 25 additions & 0 deletions apps/desktop/build-resources/entitlements.mac.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- DeepCode needs to spawn child processes (sandbox-exec, plugins, bash). -->
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<key>com.apple.security.inherit</key>
<true/>
<!-- Network access for DeepSeek API + future MCP -->
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
<!-- File access — user explicitly grants per-project -->
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
</dict>
</plist>
57 changes: 57 additions & 0 deletions apps/desktop/electron-builder.yml
Original file line number Diff line number Diff line change
@@ -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
12 changes: 9 additions & 3 deletions apps/desktop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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:*",
Expand All @@ -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"
}
Expand Down
8 changes: 8 additions & 0 deletions apps/desktop/postcss.config.template.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// PostCSS — wires Tailwind + Autoprefixer for the Vite renderer build.

export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
11 changes: 6 additions & 5 deletions apps/desktop/src/index.css
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
13 changes: 13 additions & 0 deletions apps/desktop/src/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self' https://api.deepseek.com" />
<title>DeepCode</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="./main.tsx"></script>
</body>
</html>
31 changes: 31 additions & 0 deletions apps/desktop/tailwind.config.ts
Original file line number Diff line number Diff line change
@@ -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;
36 changes: 36 additions & 0 deletions apps/desktop/vite.config.template.ts
Original file line number Diff line number Diff line change
@@ -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'),
},
},
});
12 changes: 12 additions & 0 deletions apps/desktop/vitest.config.ts
Original file line number Diff line number Diff line change
@@ -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;
Loading