Tauri + React 搭建 monorepo 项目
桌面应用既需要用户看到的页面,也需要承载页面的窗口。搭建 AI 工作台时,可以用 React 编写聊天、项目列表和设置等界面,再交给 Tauri 放进桌面窗口中运行。
随着功能增加,一些代码会被不同部分共同使用。例如,页面和本机服务需要使用相同的数据格式,适合放在共享代码包,用 pnpm workspace 将桌面应用与共享代码放在同一个仓库中管理,建立项目的基础结构。
为什么使用这些技术
React 适合把界面拆成可以组合的组件。 例如,聊天中的消息、输入框和项目列表可以分别编写,再组合成完整页面。同一种按钮或消息样式也可以重复使用,减少在多个地方维护相似界面的工作。React 还会根据数据变化更新页面,适合展示不断增加的聊天消息和任务状态。
Tauri 让使用网页技术编写的界面进入桌面应用。 它支持 Windows、macOS 和 Linux,并提供调用原生能力的入口。Tauri 使用操作系统提供的网页渲染组件,不必随应用携带完整的浏览器引擎,有利于控制分发体积。不同系统的开发依赖和构建仍需分别处理,本文先配置本机桌面开发。
页面开发还需要工具支持。这里使用 Vite:开发时提供本地页面服务,修改代码后通过热更新反馈到页面,减少反复手动重启的操作;构建时将前端代码处理为可分发的静态文件,供 Tauri 加载。React 负责页面内容,Vite 负责页面的开发和构建过程。
pnpm workspace 用来管理仓库中的多个代码包。 桌面应用和共享包各自声明依赖,再由工作区建立本地连接。修改共享代码后,应用可以直接使用它,无需先把这个内部包发布到 npm。
这种把多个相关项目放在同一个代码仓库中管理的方式,称为 monorepo。本次将桌面应用放在 apps/desktop,共享代码放在 packages/contracts,再让页面引用共享包中的内容。它们属于同一个工程,但各自保留明确的用途。
Corepack 用来选择和运行项目指定版本的包管理器。 pnpm 负责安装依赖和管理工作区,Corepack 则负责按项目要求调用哪一个版本的 pnpm。例如,项目声明使用 pnpm 10.26.0,执行 corepack pnpm install 时,Corepack 会使用这个版本;如果本机尚未缓存它,会先下载。这样切换项目时,不必反复手动安装不同版本的 pnpm。
1. 准备开发环境
本文使用 Node.js 22.20.0 或更新的 22.x 版本、pnpm 10.26.0,以及 Rust stable。先在终端检查:
node --version
corepack --version
rustup --version
rustc --version
cargo --versionNode.js 用来运行前端工具,Rust 和 Cargo 用来编译桌面程序。尚未安装时,先安装 Node.js 22.x,再通过 Rust 的工具链管理器 rustup 安装 Rust 与 Cargo,完成后重新打开终端,运行上面的命令确认工具可用。
如果只有 corepack 不存在,可以安装它:
npm install --global corepack后面统一使用 corepack pnpm 调用 pnpm。Corepack 本身有独立的版本号,corepack --version 显示的是它自己的版本;在项目目录中执行 corepack pnpm --version,显示的才是当前项目使用的 pnpm 版本。
桌面开发还依赖操作系统提供的工具,按自己的系统准备即可:
macOS 可以使用 xcode-select --install 安装命令行工具。Windows 安装 C++ Build Tools 时需要选中对应的桌面开发工作负载;Linux 的依赖包名称随发行版不同,应匹配自己的系统版本。系统依赖没有准备好时,网页可能能打开,桌面程序仍然无法编译。
2. 创建工作区根目录
在你存放练习项目的位置执行:
mkdir aiwork
cd aiwork
mkdir apps packages接下来用编辑器打开 aiwork 文件夹。下文提到的路径都相对于这个文件夹;新建文件时,如果父目录不存在,就先创建父目录。
在根目录新建 package.json,写入完整内容:
{
"name": "aiwork",
"private": true,
"version": "0.1.0",
"packageManager": "pnpm@10.26.0",
"engines": {
"node": ">=22.20.0"
},
"scripts": {
"dev": "pnpm --filter @aiwork/desktop exec tauri dev",
"dev:web": "pnpm --filter @aiwork/desktop run dev",
"build:web": "pnpm --filter @aiwork/desktop run build",
"typecheck": "pnpm --filter @aiwork/desktop exec tsc --noEmit",
"build:desktop": "pnpm --filter @aiwork/desktop exec tauri build"
}
}packageManager 中的 pnpm@10.26.0 是给 Corepack 的版本声明。将这个文件随项目一起保存,其他人在项目目录中执行 corepack pnpm 时,也会按相同声明选择版本。pnpm-lock.yaml 则记录项目依赖的解析结果,两者分别固定包管理工具和依赖安装依据。
private: true 表示这个根包不准备发布到 npm。scripts 是从根目录操作桌面应用的快捷入口;桌面应用还没生成,这些命令稍后才能运行。
继续新建 pnpm-workspace.yaml:
packages:
- "apps/*"
- "packages/*"它告诉 pnpm:去这两组目录里寻找包含 package.json 的包。仅仅创建文件夹,还不会产生应用或依赖关系。
再新建 rust-toolchain.toml:
[toolchain]
channel = "stable"
profile = "minimal"这样进入项目时会选择 stable 工具链。如果电脑上还没有安装它,执行:
rustup toolchain install stable --profile minimal根目录还需要一份 .gitignore,避免之后把依赖和构建产物加入版本管理:
node_modules/
dist/
target/
.env
.env.*
!.env.example
.DS_Storepnpm-lock.yaml 和桌面应用生成的 Cargo.lock 应当保留,它们记录实际解析的依赖版本。
3. 在 apps 中生成桌面应用
现在让官方脚手架一次生成 React、TypeScript 和 Tauri 的基础文件。在根目录的终端执行:
cd apps
corepack pnpm dlx create-tauri-app@4.7.5 desktop --template react-ts --manager pnpm --identifier cn.example.aiwork --tauri-version 2 --yes
cd ..这里固定使用 create-tauri-app@4.7.5,减少脚手架升级造成的步骤差异。生成器版本与 Tauri 版本是两回事:--tauri-version 2 指定的是 Tauri 的主版本。--template react-ts 选择 React + TypeScript 模板,--manager pnpm 指定包管理器,--yes 使用默认选项并跳过交互提问。
desktop 是生成目录名,cn.example.aiwork 是练习用的应用标识;以后准备分发自己的应用时,应换成自己使用的唯一标识。不要在已有工作台仓库里重复执行生成命令,也不要用强制覆盖参数处理目录冲突。
生成完成后,打开 apps/desktop/package.json,只把 name 字段改成:
"name": "@aiwork/desktop"保留其他字段。这一行是局部修改,不是整个文件的内容。根目录命令中的 --filter @aiwork/desktop,正是通过这个名字找到桌面应用。
此时的主要目录应当类似下面这样,辅助文件已省略:
使用上下方向键浏览目录,左右方向键展开或收起,Enter 或空格切换展开状态。
src 中放 React 页面,src-tauri 中放桌面程序的 Rust 工程和 Tauri 配置。此时不必修改 Rust 代码,模板已经提供了打开窗口的基本入口。
4. 对齐页面地址与桌面启动命令
桌面窗口需要知道去哪里加载开发页面。本文统一使用 http://127.0.0.1:17420,与参考项目的前端开发地址一致。
将 apps/desktop/vite.config.ts 替换为以下完整内容:
import { defineConfig } from "vite"
import react from "@vitejs/plugin-react"
export default defineConfig({
plugins: [react()],
clearScreen: false,
server: {
host: "127.0.0.1",
port: 17420,
strictPort: true,
watch: {
ignored: ["**/src-tauri/**"]
}
}
})这份配置面向本机桌面开发。strictPort: true 表示端口被占用时直接报错,避免 Vite 自动换到另一个端口、桌面窗口还在访问原地址。
然后打开 apps/desktop/src-tauri/tauri.conf.json,将其中的 build 对象替换为:
"build": {
"beforeDevCommand": "pnpm dev",
"devUrl": "http://127.0.0.1:17420",
"beforeBuildCommand": "pnpm build",
"frontendDist": "../dist"
}保留文件中的其他配置。还可以把 productName 和 app.windows 中第一个窗口的 title 都改成 useAiWork,方便辨认应用。
这里有两组需要对应起来的关系:
- 开发时,
beforeDevCommand启动桌面包中的 Vite,窗口访问devUrl。 - 构建时,
beforeBuildCommand生成前端文件,Tauri 从frontendDist读取它们。
../dist 相对于 tauri.conf.json 所在目录,指向 apps/desktop/dist,而不是根目录下的 dist。
IMPORTANT
根目录的 dev 启动 Tauri;桌面包中的 dev 启动 Vite。不要把桌面包的 dev 也改成 tauri dev,否则 Tauri 启动前执行这个命令时,会再次启动自己。
5. 创建共享包,让页面用上它
接下来验证 monorepo 中最重要的一件事:一个包声明依赖后,能够使用另一个本地包的代码。
新建 packages/contracts/package.json:
{
"name": "@aiwork/contracts",
"version": "0.1.0",
"private": true,
"type": "module",
"exports": {
".": "./src/index.ts"
}
}再新建 packages/contracts/src/index.ts:
export interface WorkbenchInfo {
name: string
stage: string
}
export const workbenchInfo: WorkbenchInfo = {
name: "useAiWork",
stage: "桌面工程已就绪"
}这里用一个对象及其类型演示共享导出。后续应用和本机服务需要约定数据格式时,可以在这个包中维护实际共享的定义。
exports 声明包的入口。本次直接导出 TypeScript 源文件,由桌面应用的工具处理,因此共享包不需要先单独构建。这是当前练习采用的方式;以后让 Node.js 直接运行某个包时,还需要结合其运行环境确定输出方式。
回到 apps/desktop/package.json,在原有 dependencies 中增加这一项,保留已有依赖:
"@aiwork/contracts": "workspace:*"workspace:* 明确要求使用当前工作区里的包。它不是从 npm 下载一个同名包;本地找不到对应名称时,安装会失败。
最后,将 apps/desktop/src/App.tsx 替换为:
import { workbenchInfo } from "@aiwork/contracts"
export default function App() {
return (
<main>
<h1>{workbenchInfo.name}</h1>
<p>{workbenchInfo.stage}</p>
</main>
)
}这次没有使用 ../../packages/... 这样的相对路径,而是通过包名导入。以后移动应用内部的组件时,也不必重新计算到共享目录有几层距离。
6. 安装依赖,启动第一个窗口
确认终端已经回到 aiwork 根目录,再执行:
corepack pnpm install
corepack pnpm run dev依赖统一从根目录安装,pnpm 会处理工作区内各个包,并生成根目录的 pnpm-lock.yaml。之后重新安装已有工程时,可以使用 corepack pnpm install --frozen-lockfile,要求依赖声明与锁文件保持一致。
启动过程会先运行 Vite,再编译 Rust 桌面程序。第一次编译可能需要下载 Rust 依赖,终端暂时没有出现窗口时,先查看它是否仍在下载或编译。
预期结果是出现一个标题为 useAiWork 的桌面窗口,正文显示:
useAiWork
桌面工程已就绪要检查共享包是否真的参与了运行,可以修改 packages/contracts/src/index.ts 中的 stage,保存后观察页面文字是否更新。这比只确认窗口能打开,多检查了一层本地包之间的联系。
结束开发时,在启动命令所在的终端按 Ctrl+C,并确认本次窗口和开发进程已经退出。
如果只想先看 React 页面,可以从根目录运行:
corepack pnpm run dev:web再用浏览器打开 http://127.0.0.1:17420。这一命令只启动网页开发服务;检查过网页以后,先停止它,再运行桌面开发命令,避免两个 Vite 进程争用同一个端口。
7. 检查工程是否具备继续开发的条件
关闭开发进程后,在根目录执行:
corepack pnpm run typecheck
corepack pnpm run build:web第一条检查桌面页面及其引用代码的 TypeScript 类型,第二条生成前端构建产物。成功后,应能在 apps/desktop/dist 中找到 index.html 和静态资源。这两项检查都不代表桌面程序已经编译成功。
环境准备齐全时,还可以执行:
corepack pnpm run build:desktop它会进入 Tauri 的本机构建与打包流程。结果受当前操作系统及其依赖影响,不能把某个平台的成功构建当成其他平台也已通过。签名、安装包分发和自动更新留到后续处理。
遇到问题时,先根据现象缩小范围:
到这里,工程就有了三个可以分别核对的结果:React 页面能够构建,Tauri 能够加载页面,共享包能够被桌面应用引用。后续加入项目管理、聊天和本机服务时,可以在这份基础上逐项增加能力。
8. 约定各部分负责什么
把应用放进 apps、共享代码放进 packages,只是分好了目录。继续加功能时,还需要回答:一段代码放到哪里,它可以使用哪些其他代码,又由谁保存数据和执行操作。这些决定合起来,就是项目的架构约定。
先划清当前工程的边界
现在的工程只有一个 React 页面、Tauri 外壳和共享包,职责可以保持简单:
本篇的依赖方向是 desktop → contracts,意思是桌面应用可以导入共享包。反过来,contracts 不应导入 desktop:一旦共享包需要知道某个页面的内部写法,页面调整就会牵动原本独立的包,其他应用也难以复用它。
workbenchInfo 是为了检查包连接而放入的教学数据。后续真正接入服务时,contracts 应围绕双方共同使用的数据格式组织,例如请求、响应和错误类型,不应逐渐变成存放项目配置、聊天状态和各种帮助函数的杂物箱。
共享类型不等于执行请求。 页面导入一个请求类型,只是让 TypeScript 检查数据结构;它不会因此向服务发送消息。以后还需要实际的通信代码,并在接收请求时检查数据。TypeScript 类型本身不能验证来自网络或磁盘的内容。
功能增加以后,再拆页面内部的职责
现在的 App.tsx 只有几行,保持原样即可。等加入项目选择、聊天和设置后,可以按下面的职责拆分。这是后续组织方式,不要求本次创建空目录:
例如,聊天消息组件先放在聊天功能内;确实被多个功能共同使用的基础控件,再提取到公共位置。只因两个按钮长得相似,就提前建立庞大的共享组件包,反而可能让简单修改需要跨多个目录。
依赖也应沿职责组织:app 组装页面与功能,功能通过参数或明确的接口接收所需数据,不反过来导入 app 的内部实现。这样修改应用入口时,不必同时修改每个功能。
接入本机服务时,明确执行与数据归属
后续如果加入 apps/server,可以让它负责模型连接、项目配置以及文件操作,把 React 留在展示和交互一侧。Tauri 则继续负责窗口和必要的原生能力。这个划分是本工作台的设计选择,Tauri 本身并不要求所有项目都增加 Node 服务。
以“读取项目说明”为例,拟采用的流程是:页面提交项目标识和用户要求,本机服务找到该项目允许访问的目录,检查路径并读取文件,再把结果返回页面。页面传入的标识只能用于查找,不能直接变成拥有任意磁盘访问权的路径。
模型密钥也由执行请求的一侧读取和使用,不返回页面、不写入前端构建变量。页面只需要得到“是否已配置”和可以展示的错误信息。
这时可以形成 desktop → contracts、server → contracts 两条代码依赖。客户端和服务之间另通过接口通信,不能直接把服务端源码导入 React。共享包供双方理解数据,接口负责实际传递数据,两件事各有入口。
同样要明确每份状态由谁更新。例如,输入框里尚未发送的文字由客户端维护;将来的文件保存结果由实际执行保存的一方确认。页面应在收到成功结果后显示“已保存”,不能以按钮已经点击作为依据。目录分层和 AGENTS.md 可以帮助遵守这些规则,真正的访问限制仍要由执行代码检查。
9. 把约定留在项目中
只在聊天里说过的规则,不容易在下一次开发时完整保留。可以把规则分成两份文件:docs/architecture.md 解释当前设计及理由,根目录 AGENTS.md 说明人和 Codex 修改项目时应遵守什么。
架构文档记录已经作出的决定
在练习工程中新建 docs/architecture.md,写入下面这份起点:
# 工程架构约定
## 当前范围
当前工程包含 Tauri + React 桌面应用和 contracts 共享包。
共享包导出 TypeScript 源码,由桌面应用的工具处理。
本机服务、模型请求和文件工具尚未实现。
## 依赖与职责
- desktop 使用 contracts,contracts 不依赖任何应用。
- React 负责展示和交互,Tauri 负责窗口及明确接入的原生能力。
- contracts 保存实际共享的数据定义,不保存页面状态或执行文件操作。
## 后续增加服务时
本机服务负责配置、模型请求和文件操作,客户端通过接口访问。
服务与客户端可以共同依赖 contracts,不能相互导入内部实现。
新能力接入时,需要补充数据归属、失败反馈和访问范围。
## 为什么先保持简单
当前只验证页面、窗口和共享包之间的连接。
功能增加后再拆模块,不提前生成没有实现的服务或运行时包。这里把“当前已有”和“后续打算”分开,避免下一次让 Codex 读文档时,把尚未存在的服务当成已经可以调用的能力。以后改变依赖方向或增加数据存储,也在这里写明变动理由,而不只更新目录树。
AGENTS.md 给出可以执行的修改规则
继续在练习工程根目录新建 AGENTS.md:
# 项目开发约定
## 开始修改前
- 阅读现有实现、package.json 和 docs/architecture.md,检查 Git 状态。
- 只修改当前任务需要的文件,保留其他未提交改动。
- 已有规则有冲突时先指出具体冲突,不自行重建整个工程。
## 代码组织
- 手写代码采用 TypeScript、两空格缩进、LF 换行。
- 新增文件使用英文 kebab-case;现有脚手架文件名不必为此改名。
- 功能代码先放在所属功能内,实际有跨功能使用时再提取共享代码。
- 应用可以依赖共享包,共享包不得反向依赖 apps。
- 不把服务实现、模型密钥或任意文件操作放进 React 页面。
## 依赖与验证
- 使用 corepack pnpm,从根目录安装依赖并维护同一份 pnpm 锁文件。
- 新依赖声明在实际使用它的包中;跨包引用通过包名和 workspace 协议。
- 修改后运行根目录 typecheck 和 build:web,按变更补充针对性验证。
- 窗口、系统能力或打包配置改变时,补充对应的桌面验证。
- 报告实际检查结果及未验证项;未经要求不提交、推送或发布。规则应能对应具体行为。例如,“注意代码质量”很难检查;“共享包不得导入 apps”可以通过依赖声明和 import 直接核对。规范也不必罗列所有未来功能,新增一种实际能力后,再补充它需要遵守的规则。
两空格和换行约定还可以交给编辑器。新建根目录 .editorconfig:
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true支持 EditorConfig 的编辑器会读取这些设置。它不负责检查 React 用法和包依赖方向,也不替代格式化、类型检查或测试。本篇先记录最小约定;后续接入质量工具时,应让配置与这些规则一致。
10. 交给 Codex 搭建的提示词模板
也可以让 Codex 按照相同要求创建工程。在 Codex 中打开存放练习项目的父文件夹,再复制下面整段提示词。默认在当前目录下新建 aiwork;如果想换名字,发送前修改提示词中的目标目录即可。
提示词把前面的操作整理成明确的搭建要求,同时要求 Codex 实际检查结果。它生成的仍是这一篇的基础工程,聊天和文件处理功能后续再加入。
请直接在当前目录下创建 aiwork 文件夹,搭建一个 Tauri 2 + React + TypeScript 的 pnpm monorepo 工程。请完成文件创建、依赖安装和必要验证,不要只给方案或命令清单。
一、先检查工作位置和环境
1. 阅读适用的 AGENTS.md,检查当前目录、Git 状态和 aiwork 是否存在,保留已有文件和未提交修改。
2. 默认新建 aiwork。若该目录已存在且非空,先检查内容;可以继续完善同一练习工程,但不能强制覆盖或重新初始化无关项目。无法判断时再询问我。
3. 检查 Node.js、Corepack、Rust stable、Cargo 和当前系统的 Tauri 桌面依赖。Node.js 使用 22.20.0 或更新的兼容版本,pnpm 固定为 10.26.0。
4. 项目命令使用 corepack pnpm。缺少系统工具或需要管理员权限时,说明缺少什么以及安装办法,不要自行修改全局工具链或安装系统组件。
二、创建最小目录结构
aiwork/
package.json
pnpm-workspace.yaml
rust-toolchain.toml
.gitignore
.editorconfig
AGENTS.md
docs/
architecture.md
apps/
desktop/
package.json
vite.config.ts
src/App.tsx
src-tauri/tauri.conf.json
packages/
contracts/
package.json
src/index.ts
这只是关键文件清单,请保留脚手架生成的其他必要文件。
三、配置工作区和桌面应用
1. 根 package.json 设置 private: true、packageManager: pnpm@10.26.0,以及 engines.node: >=22.20.0。
2. pnpm-workspace.yaml 包含 apps/* 和 packages/*。
3. rust-toolchain.toml 选择 stable,profile 为 minimal。
4. 在 apps 目录中执行官方脚手架:
corepack pnpm dlx create-tauri-app@4.7.5 desktop --template react-ts --manager pnpm --identifier cn.example.aiwork --tauri-version 2 --yes
5. 将桌面包名改为 @aiwork/desktop,保留生成的依赖和必要脚本。桌面包 dev 运行 vite,build 运行 TypeScript 检查和 vite build。
6. Vite 保留 React 插件,设置 clearScreen: false;开发服务 host 为 127.0.0.1,port 为 17420,strictPort 为 true,忽略监听 **/src-tauri/**。
7. Tauri 的 productName 和窗口 title 使用 useAiWork。build.devUrl 为 http://127.0.0.1:17420,frontendDist 为 ../dist;beforeDevCommand 运行桌面包的 pnpm dev,beforeBuildCommand 运行桌面包的 pnpm build。不要在这些钩子中调用根目录的桌面启动命令,避免递归启动。
8. .gitignore 忽略 node_modules、dist、target 和本地环境文件,保留 .env.example。保留生成的 pnpm-lock.yaml 和 Cargo.lock。
四、加入共享包,并让页面实际引用
1. packages/contracts 的包名为 @aiwork/contracts,设置 private: true、type: module,exports 的入口指向 ./src/index.ts。
2. 导出 WorkbenchInfo 类型,包含字符串字段 name 和 stage;再导出符合该类型的 workbenchInfo 对象,name 为 useAiWork,stage 为“桌面工程已就绪”。
3. 桌面包在 dependencies 中声明 @aiwork/contracts: workspace:*。
4. App.tsx 通过 @aiwork/contracts 包名导入 workbenchInfo,分别用标题和段落显示 name、stage,不在页面中重复写死这两段文字。
5. 共享包本次由 Vite 和 TypeScript 直接处理源文件,不增加单独的构建流程。
五、提供根目录命令
dev:pnpm --filter @aiwork/desktop exec tauri dev
dev:web:pnpm --filter @aiwork/desktop run dev
build:web:pnpm --filter @aiwork/desktop run build
typecheck:pnpm --filter @aiwork/desktop exec tsc --noEmit
build:desktop:pnpm --filter @aiwork/desktop exec tauri build
六、记录项目约定与架构规范
1. 创建 docs/architecture.md,分别写明当前实现、依赖方向、设计理由和后续计划。当前只有 desktop 和 contracts,本机服务尚未实现,不生成空的服务或运行时包。
2. 记录 desktop 可以依赖 contracts,而 contracts 不能依赖 apps;contracts 不放页面状态、组件、密钥或系统执行代码。当前 workbenchInfo 只是检查共享导出的教学示例。
3. 后续 React 功能增加时,app 负责组装,pages 作为薄入口,features 按功能组织,lib 封装客户端与平台调用。现在 App.tsx 仍简单,不为此创建空目录或多余转发层。
4. 后续接入服务时,页面通过接口提交项目标识和用户要求;执行端解析允许目录、校验请求、读取密钥和执行操作。共享类型不代替运行时校验。页面状态与执行结果分别明确归属,避免重复维护。
5. 创建根 AGENTS.md,包含修改前检查、保留其他改动、两空格与 LF、TypeScript、依赖声明位置、跨包边界、验证命令以及交付规则。已有适用规则需要保留,不复制父仓库无关业务约定。
6. 创建 .editorconfig:root = true;所有文件使用 UTF-8、空格缩进、两空格、LF,并保留文件末尾换行。不额外安装格式或架构检查工具;说明 EditorConfig 的作用范围。
七、执行验证并报告证据
1. 在 aiwork 根目录运行 corepack pnpm install,确认工作区依赖连接成功。
2. 运行 corepack pnpm run typecheck 和 corepack pnpm run build:web,修复本次搭建产生的问题;确认 apps/desktop/dist/index.html 已生成。
3. 环境支持时,运行 corepack pnpm run dev 检查开发启动。能查看窗口时,确认标题和页面文字;无法查看时,区分“进程已启动”和“窗口已验证”。缺少系统依赖时,保留已完成的工程,报告具体阻塞原因。
4. 验证结束后清理自己启动的开发进程,不停止其他项目的进程。不要求本次执行桌面打包。
5. 最后列出创建的结构、启动命令、实际执行的检查、失败项和未验证项,不把网页构建通过说成桌面验收通过。
范围仅限工程搭建和共享包示例。不要添加 AI 接口、聊天业务、本机服务、UI 组件库、签名或自动更新,也不要提交、推送或发布。Codex 完成后,可以先看它报告的检查结果,再在新工程根目录执行 corepack pnpm run dev。如果它报告原生环境缺失,先处理对应依赖;只完成前端构建时,仍需补上桌面窗口的检查。