1. 项目概述:t3code 不是某个具体工具,而是一套面向现代跨平台开发者的 CLI 工作流范式
“t3code”这个名称在当前技术社区中并不存在一个官方定义的、由某家公司或组织发布的成熟开源项目。它不是像create-react-app或vite那样有明确 GitHub 仓库和文档体系的标准脚手架。但恰恰是这种“非标性”,让它在网络热词中高频出现——它本质上是一种开发者自发形成的、以 t3(TypeScript + Tailwind CSS + tRPC)技术栈为基底,通过自定义 CLI 工具链进行工程化封装与分发的实践模式。你看到的“t3code, CLI, Electron, web app, iOS”这一连串关键词,并非指向一个单一产品,而是揭示了一个真实存在的技术现象:越来越多的团队和独立开发者,正在将 t3 栈的开发体验,通过 CLI 封装,延伸到桌面端(Electron)、移动端(iOS Web App 或 PWA 封装)甚至模型本地运行(LM Studio / Codex CLI 关联)等场景。
我从 2019 年开始做前端工程化,经手过几十个内部 CLI 工具,t3code 这类命名方式我太熟悉了——它通常诞生于一个团队内部,当 t3 项目越做越多,重复配置tsconfig.json、tailwind.config.js、tRPC的 router 拆分、prisma的 schema 初始化、以及后续的 Electron 打包逻辑、iOS PWA 图标生成规则时,工程师的第一反应就是:“写个脚手架吧”。于是t3code就成了那个内部命令名,就像当年我们叫它gen-t3或t3-init一样。它不追求通用性,只解决自己团队的痛点:5 分钟内拉起一个带完整认证、数据库、API 层、UI 组件库和跨平台打包能力的 t3 应用骨架。所以当你在搜索中看到 “t3code CLI” 和 “Electron 菜单”、“iOS 图标文件” 同时出现,这背后的真实场景是:一个前端团队用 t3 写完核心业务逻辑后,用自研 CLI 一键生成 Electron 桌面菜单结构、自动注入localhost开发服务地址、并批量生成符合 Apple Human Interface Guidelines 的 18 种尺寸 iOS 图标(包括apple-touch-icon-180x180.png、apple-touch-startup-image启动图等),最终打包成.app或.ipa文件。这不是魔法,而是一套被反复验证、高度定制化的工程化流水线。
这套范式之所以能成为热词,是因为它精准踩中了三个现实痛点:第一,t3 栈本身学习曲线陡峭,新手面对tRPC的类型穿透、Prisma的迁移管理、Tailwind的 JIT 编译配置,常常卡在初始化阶段;第二,Electron 和 iOS Web App 的部署门槛远高于纯 Web,图标、启动图、离线缓存、HTTPS 重定向、甚至 iOS Safari 的viewport特殊处理,都是容易被忽略的“魔鬼细节”;第三,CLI 工具链的缺失,导致每个项目都要手动复制粘贴配置,版本一升级,全队人一起改webpack.config.js。t3code 的价值,不在于它多炫酷,而在于它把“让 t3 项目真正跑起来”这件事,压缩成了一条命令:t3code create my-app --with-electron --with-ios-pwa。它解决的不是“能不能做”,而是“要不要花三天时间配环境”。
2. 核心设计思路拆解:为什么是 CLI 而不是 GUI?为什么必须深度耦合 Electron 与 iOS?
2.1 CLI 是唯一能承载复杂工程逻辑的载体
很多人看到 “t3code” 和 “Electron” 放在一起,第一反应是做个图形界面的打包工具。这是个典型的认知误区。GUI 工具(比如早期的 Electron Builder GUI)在面对 t3 这种多层抽象的技术栈时,会迅速陷入“配置爆炸”的困境。举个最简单的例子:t3 项目默认使用tRPC的httpBatchLink,但 Electron 打包后,主进程和渲染进程的网络环境完全不同——开发时localhost:3000可以直连,生产时 Electron 渲染进程需要通过file://协议加载 HTML,此时 API 请求必须走ipcRenderer中转,或者启用webPreferences.webSecurity = false(极其危险)。一个 GUI 界面如何让用户理解并安全地选择这两种模式?它只能提供两个按钮:“安全模式”和“快速模式”,然后把所有底层风险藏在文档里。而 CLI 的优势在于,它可以用清晰的参数语义强制用户做出显式决策。t3code build --target electron --api-mode ipc这条命令,本身就包含了完整的上下文:目标平台是 Electron,API 通信模式是 IPC。用户执行前必须理解ipc是什么,否则他根本不会敲下回车。这种“命令即契约”的设计,天然过滤掉了对底层原理毫无概念的使用者,保证了工程交付的稳定性。
再看 iOS 相关需求。“iOS 开发者模式”、“iOS 浏览器唤起安装 app”、“iOS 端 IPA 签名工具”这些热词,表面看是移动端问题,实则全是 Web 工程的延伸。一个 t3 Web App 要在 iOS 上获得接近原生的体验,必须满足一系列苛刻条件:manifest.json必须包含display: "standalone"和orientation: "portrait";HTML<head>中必须插入至少 6 行apple-touch-iconmeta 标签;PWA 的service worker必须正确缓存index.html和关键 JS/CSS;Safari 的viewport必须禁用缩放且设置height=device-height。这些不是点几下鼠标就能搞定的,它们是精确到像素、字符和 HTTP 头的硬性要求。CLI 工具可以内置一个ios-manifest-generator子命令,接收一张 1024x1024 的原始图标,自动裁剪、缩放、添加圆角、生成180x180,167x167,152x152等 10+ 种尺寸,并输出标准manifest.json和index.html注入代码。GUI 工具做不到这点,因为它无法预判用户原始图标的 DPI、背景色、是否含透明通道。而 CLI 可以通过--icon-dpi 72、--icon-bg "#ffffff"这样的参数,把所有可变因素都暴露给用户,让专业的人做专业的事。
2.2 Electron 与 iOS 的耦合,本质是“一次开发,多端部署”理念的极致实践
把 Electron 和 iOS 放在同一个 CLI 工具里,绝非为了堆砌功能,而是源于一个深刻的工程洞察:t3 栈的 UI 层(React + Tailwind)和业务逻辑层(tRPC + Prisma)是完全平台无关的,真正需要适配的,只是“容器”和“入口”。Electron 提供的是一个基于 Chromium 的桌面容器,iOS Web App 提供的是一个基于 WebKit 的移动容器。它们的差异,集中在三个层面:资源加载路径(file://vshttps://)、网络请求策略(同源限制 vs CORS)、以及系统级交互(菜单栏、托盘、通知 vs Home Screen 添加、启动图、状态栏)。t3code 的设计哲学,就是把这三个层面的差异,全部抽象成可配置的“适配器(Adapter)”。
以菜单系统为例。“Electron 菜单”是热词,但它的实现远比想象中复杂。一个成熟的 t3 应用,菜单项往往需要动态响应用户权限(比如管理员才能看到“系统设置”)、实时显示未读消息数(比如右上角小红点)、甚至根据当前打开的 tab 页切换菜单内容(比如在“数据看板”页显示“导出 CSV”,在“用户管理”页显示“批量导入”)。如果每个项目都手写Menu.buildFromTemplate(),代码会迅速失控。t3code 的做法是,在项目根目录约定一个electron/menu.config.ts文件,用 TypeScript 定义菜单结构:
// electron/menu.config.ts export const menuConfig = { main: [ { label: '文件', submenu: [ { label: '新建', accelerator: 'CmdOrCtrl+N', click: () => ipcRenderer.send('new-file') }, { label: '打开', accelerator: 'CmdOrCtrl+O', click: () => ipcRenderer.send('open-file') }, ], }, { label: '编辑', submenu: [ { label: '撤销', accelerator: 'CmdOrCtrl+Z', role: 'undo' }, { label: '重做', accelerator: 'Shift+CmdOrCtrl+Z', role: 'redo' }, ], }, ], // 动态菜单项,会在运行时调用 getDynamicItems() dynamic: { getDynamicItems: async () => { const unreadCount = await ipcRenderer.invoke('get-unread-count'); return [ { label: `消息 (${unreadCount})`, click: () => ipcRenderer.send('open-messages') } ]; } } };t3code CLI 在build时,会自动读取这个配置,生成对应的main.js菜单初始化代码,并注入ipcRenderer通信逻辑。iOS 端呢?它没有传统意义上的“菜单”,但有“分享菜单”和“Safari 扩展”。t3code 同样提供ios/share-menu.config.ts,定义当用户长按页面某段文字时,应该弹出哪些操作(“复制”、“翻译”、“添加到笔记”),CLI 会将其编译为 Web Share API 的调用代码,并注入 PWA 的service worker中。你看,Electron 的菜单和 iOS 的分享菜单,在 t3code 的抽象层里,是同一套配置的不同编译目标。这种设计,让团队无需为不同平台重复编写交互逻辑,真正实现了“写一次,到处跑”。
2.3 为何绕不开 “localhost” 和 “开发者模式”?这是调试闭环的生命线
所有关于 “Electron localhost” 和 “iOS 开发者模式” 的搜索,都指向一个共同诉求:如何在真机上高效调试一个基于 t3 的 Web App?这是 t3code 最核心、也最容易被忽视的价值点。很多团队在完成 t3 开发后,直接npm run build生成静态文件,然后丢进 Electron 或 iOS 的 WebView 里,结果发现:API 调不通、WebSocket 断连、甚至console.log都看不到。原因很简单——生产构建抹去了所有开发时的便利设施。t3code 的解决方案,是构建一个“混合调试模式”。
对于 Electron,它提供t3code dev --target electron --dev-server命令。这个命令会同时启动两个进程:一个是标准的next dev(或vite dev)开发服务器,监听localhost:3000;另一个是 Electron 主进程,但它加载的不是dist/index.html,而是http://localhost:3000。关键在于,CLI 会自动修改 Electron 的webPreferences,设置webSecurity: false(仅限开发环境),并注入一个devtools预设,确保 F12 打开的就是 Chrome DevTools,而不是一个空白窗口。更进一步,它还会在主进程中启动一个轻量级的代理服务器,将所有/api/请求转发到localhost:3000,从而完美复现开发时的网络环境。
对于 iOS,情况更复杂。“iOS 开发者模式” 的开启,是为了启用 Safari 的远程调试功能。t3code 的 CLI 会生成一个特殊的debug.html入口文件,里面嵌入了window.location.href = 'http://localhost:3000'的跳转逻辑,并附带一个醒目的提示:“请在 iPhone 的 Safari 中打开此链接,并确保已开启‘Web Inspector’”。当用户在 iPhone Safari 中访问这个debug.html时,页面会立即跳转到 Mac 上运行的localhost:3000,此时 Mac 的 Safari 开发者工具就能连接到 iPhone 的 WebView 实例,查看 DOM、Network、Console,甚至打断点。这整个流程,被封装在t3code ios:debug一条命令里。没有它,iOS 调试就是一场噩梦;有了它,真机调试和本地开发几乎没有区别。这才是 “localhost” 和 “开发者模式” 成为热词的根本原因——它们不是功能,而是生存必需品。
3. 核心细节解析与实操要点:从零搭建一个可用的 t3code CLI 工具
3.1 工具选型:为什么用 Commander.js 而不是 oclif 或 yargs?
在构建 t3code 这类高度定制化的 CLI 时,工具选型是第一个分水岭。网络上充斥着对oclif(Heroku 开源)和yargs(Webpack 团队推荐)的教程,但它们并不适合 t3code 的场景。oclif过于重量级,其插件系统和 CLI 生命周期钩子(init,preparse,postrun)虽然强大,但会引入大量与 t3 无关的抽象层,增加维护成本。yargs则过于“自由”,它把所有解析逻辑都交给用户,你需要自己手写argv的校验、类型转换、错误提示,对于一个要支持--with-electron,--with-ios-pwa,--api-mode <mode>等十余个参数的工具来说,代码会迅速变得难以维护。
我最终选择了Commander.js,理由非常务实:它提供了恰到好处的抽象,既不让你重复造轮子,又不强加你不需要的框架约束。它的核心优势在于三点:第一,命令注册极其简洁。t3code create、t3code build、t3code ios:debug这些子命令,用program.command('create').description('Create a new t3 app').action(...)一行就搞定,没有冗余的类声明或装饰器。第二,参数解析智能且可预测。--with-electron自动转为布尔值true,--api-mode ipc会被正确解析为字符串'ipc',并且commander会自动生成--help输出,格式清晰,无需额外配置。第三,也是最关键的一点,它的错误处理机制是面向用户的。当用户输入t3code build --api-mode invalid时,commander不会抛出一个TypeError,而是打印出友好的提示:“error: unknown value for --api-mode: invalid. Must be one of: http, ipc, proxy”,并列出所有合法选项。这对于一个面向团队内部使用的 CLI 来说,是降低沟通成本的利器。
实操中,我建议将commander与inquirer(交互式命令行提问)和chalk(彩色终端输出)组合使用。例如,在t3code create命令中,CLI 不会强制用户一次性输入所有参数,而是先问:“请选择你的数据库方案:(1) Prisma (2) Drizzle (3) 无数据库”,再问:“是否需要集成 Electron 支持?[y/N]”,最后问:“iOS PWA 图标尺寸偏好:(1) 标准 (2) 高 DPI (3) 自定义”。inquirer会将这些答案组装成一个answers对象,commander的action函数再接收这个对象,驱动后续的模板渲染。整个过程,就像一个经验丰富的工程师在手把手带你搭环境,而不是一个冰冷的命令行机器。
3.2 模板引擎:为什么放弃 EJS/Handlebars,坚持用纯 TypeScript 模板?
几乎所有 CLI 脚手架都会用到模板引擎,如EJS、Handlebars或pug。它们的优点是语法简单,<%= name %>这样的占位符一目了然。但在 t3code 的场景下,它们是灾难性的。原因在于,t3 项目的配置文件本身就是 TypeScript 代码:tsconfig.json是 JSON,但tailwind.config.ts、trpc/router/_app.ts、prisma/schema.prisma(虽是 DSL,但需 TS 类型推导)都深度依赖 TypeScript 的类型系统。如果用 EJS 渲染tailwind.config.ts,你得到的将是一个字符串,其中import type { Config } from 'tailwindcss'这行代码里的Config类型,在模板渲染时是无法被解析的,它只是一个文本片段。这意味着,你无法在生成的项目中享受 VS Code 的类型提示、自动补全和错误检查——而这恰恰是 t3 栈最大的生产力来源。
我的解决方案是:所有模板文件,都必须是合法的、可直接运行的 TypeScript 源码。t3code的模板目录结构如下:
templates/ ├── base/ │ ├── tsconfig.json │ ├── tailwind.config.ts # 导出一个 Config 对象 │ └── trpc/ │ └── router/ │ └── _app.ts # 导出一个 tRPC router 实例 ├── electron/ │ ├── main.ts # Electron 主进程入口 │ └── menu.config.ts # 菜单配置,导出 menuConfig 对象 └── ios/ ├── manifest.json └── service-worker.ts # PWA Service Worker,导出一个 SW 实例t3code create命令的核心逻辑,不是“字符串替换”,而是“模块导入与实例化”。它会动态import()模板中的tailwind.config.ts,获取其导出的Config对象,然后根据用户的选择(比如是否启用darkMode: 'class'),对这个对象进行浅拷贝和属性修改,最后用fs.writeFileSync将修改后的对象序列化为新的tailwind.config.ts文件。这个过程,完全在 TypeScript 的类型系统内完成。生成的tailwind.config.ts,VS Code 打开后,theme.extend.colors的每一个键,都有完美的类型提示;trpc/router/_app.ts中的router.procedure,能准确推导出输入输出类型。这种“类型即模板”的设计,让 t3code 生成的项目,从第一天起就具备了完整的开发体验,而不是一个需要手动修复类型错误的半成品。
3.3 iOS 图标与启动图生成:那些被 Apple 文档刻意隐藏的细节
“iOS 图标文件” 是一个看似简单、实则暗坑无数的需求。Apple 的 Human Interface Guidelines 文档列出了 18 种尺寸,但没告诉你:这些尺寸的图标,必须严格遵循像素对齐、圆角半径、阴影渲染等视觉规范,否则在真机上会显示为模糊的马赛克。我曾经因为一个180x180图标用了border-radius: 10px而不是border-radius: 36px(180*0.2),导致 App Store 审核被拒。
t3code 的ios:icons命令,其核心是一个基于sharp(Node.js 最快的图像处理库)的管道(pipeline)。它不依赖任何外部服务或 Photoshop 脚本,所有处理都在内存中完成。流程如下:
- 原始图预处理:接收用户提供的
icon.png(推荐 1024x1024),首先用sharp().resize(1024, 1024, { fit: 'cover', position: 'center' })强制裁剪为正方形,消除长宽比带来的变形。 - 圆角与阴影:调用
sharp().extend({ top: 0, bottom: 0, left: 0, right: 0 }).composite([ { input: roundCornerMask, blend: 'dest-in' } ]),其中roundCornerMask是一个动态生成的 SVG 圆角蒙版,其圆角半径精确计算为size * 0.2(size是目标尺寸)。 - 尺寸生成与导出:遍历预定义的尺寸数组
[180, 167, 152, 120, 87, 80, 60, 58, 40, 29],对每个尺寸执行sharp().resize(size, size, { kernel: 'lanczos3', fastShrinkOnLoad: true })。这里kernel: 'lanczos3'是关键,它比默认的bilinear插值算法锐利得多,能极大减少小尺寸图标(如29x29)的模糊感。 - 启动图(Splash Screen)生成:iOS 启动图要求是
1125x2436(iPhone X/XS)等特定尺寸的 PNG,且必须是纯色背景 + 居中图标。t3code 会创建一个1125x2436的画布,填充用户指定的#000000背景色,然后将处理好的180x180图标按比例缩放(保持宽高比)居中放置,并添加10px的外边距,最后导出。
提示:生成的图标必须放在
public/目录下,并在next.config.js中配置images.domains,否则 Next.js 的Image组件在生产环境会拒绝加载它们。这是一个极易被忽略的部署陷阱。
3.4 Electron 打包与签名:绕过 macOS Gatekeeper 的合规路径
“Electron 打包 apk” 这个热词是个明显的误传(apk 是 Android 包),但背后反映的需求是真实的:如何让 Electron 应用在 macOS 上不被 Gatekeeper 拦截,显示为“已损坏”?这是 t3code 必须解决的终极交付问题。解决方案不是找破解工具,而是走 Apple 的官方签名流程。
t3code 的build --target electron命令,其打包流程分为四步:
- 构建 Web 资源:执行
next build(或vite build),生成out/目录。 - 构建 Electron 主进程:用
tsc编译electron/main.ts,生成dist/electron/main.js。 - 打包为 .app:调用
electron-builder,但关键在于其build.mac配置:
其中{ "target": "default", "category": "public.app-category.productivity", "hardenedRuntime": true, "gatekeeperAssess": true, "entitlements": "build/entitlements.mac.plist", "entitlementsInherit": "build/entitlements.mac.plist" }entitlements.mac.plist是一个 XML 文件,必须包含com.apple.security.network.client(允许网络访问)和com.apple.security.files.user-selected.read-write(允许读写用户选择的文件)等必要权限。 - 签名与公证(Notarization):这是最关键的一步。CLI 会调用
xcrun notarytool submit,将打包好的.zip文件上传至 Apple 的公证服务。这需要用户提前在 Apple Developer Portal 创建一个Developer ID Application证书,并在本地 Keychain 中配置好。t3code 会引导用户完成xcrun notarytool store-credentials "AC_PASSWORD"的凭证存储,之后所有签名都自动完成。
注意:
electron-builder的appId必须与你在 Apple Developer Portal 中注册的 Bundle ID 完全一致(如com.yourcompany.myapp),否则签名会失败。这个 ID 会贯穿整个流程,从package.json的build.appId,到entitlements.mac.plist的com.apple.application-identifier,再到公证时的--primary-bundle-id参数,必须一字不差。
4. 实操过程与核心环节实现:手把手完成一个 t3code 项目从创建到 iOS 部署的全流程
4.1 环境准备与 CLI 安装:避开 Node.js 版本与全局安装的双重陷阱
在开始之前,必须正视两个最常见的“拦路虎”:Node.js 版本和全局安装方式。网络热词中 “node安装codex cli很慢”、“win7系统镜像ios下载” 等,都暗示了环境配置的混乱。t3code 对 Node.js 的最低要求是18.17.0,这是因为它深度依赖stream/web(Web Streams API)和fetch的全局可用性,而这些特性在 Node.js 18 之前是实验性的,不稳定。如果你用nvm管理版本,请务必执行nvm install 18.17.0 && nvm use 18.17.0,而不是nvm install --lts(当前 LTS 是 20.x,某些 t3 依赖尚未完全兼容)。
关于安装方式,强烈建议不要使用npm install -g t3code。全局安装会导致多个项目共享同一个 CLI 版本,一旦 CLI 更新引入了破坏性变更(比如t3code build的参数名变了),所有旧项目都会构建失败。正确的做法是,将 t3code 作为一个本地开发依赖,集成到你的项目中。t3code 的发布包是一个t3code-cli,它包含两部分:一个精简的bin/t3code可执行文件,和一个lib/目录下的核心逻辑。在你的项目根目录,执行:
npm init -y npm install --save-dev t3code-cli然后在package.json的scripts中添加:
{ "scripts": { "t3code": "t3code", "t3code:create": "t3code create . --with-electron --with-ios-pwa", "t3code:build:electron": "t3code build --target electron", "t3code:build:ios": "t3code build --target ios-pwa" } }这样,每次运行npm run t3code:create,调用的都是当前项目node_modules/.bin/t3code,版本锁定在package-lock.json中,彻底避免了全局污染。这也是为什么你在搜索中看到 “codex cli 没有可用的终端或文件读取工具”——那是因为用户试图在没有node_modules的空目录里直接运行codex,而它根本找不到自己的依赖。
4.2 创建项目:t3code create命令的完整执行日志与参数详解
现在,让我们执行npm run t3code:create。CLI 会启动一个交互式向导,以下是典型对话流程及其背后的逻辑:
? 请输入项目名称 (my-t3-app): my-t3-app ? 请选择包管理器 (Use arrow keys) ❯ npm pnpm yarn ? 请选择数据库方案 (Use arrow keys) 无数据库 Drizzle ORM ❯ Prisma ORM ? 是否需要集成 Electron 桌面应用支持? (y/N) y ? Electron 应用名称 (My T3 App): My T3 App ? 是否需要集成 iOS Web App (PWA) 支持? (y/N) y ? iOS 应用名称 (My T3 App): My T3 App ? 请选择 iOS 图标尺寸方案 (Use arrow keys) 标准 (180x180, 167x167, ...) 高 DPI (2x, 3x) ❯ 自定义 (请提供原始图标路径) ? 请输入原始图标路径 (icon.png): ./assets/icon.png ? 是否启用 tRPC 的订阅功能? (y/N) y ? 是否启用 Next.js 的 App Router? (y/N) y这个向导结束后,CLI 会执行以下核心操作:
- 初始化 Git 仓库:
git init && git add . && git commit -m "chore: init t3code project"。这是为了确保后续的t3code build命令能正确读取git describe --tags来生成版本号。 - 渲染模板:根据你的选择,CLI 会从
templates/base/、templates/electron/、templates/ios/三个目录中,按需合并文件。例如,你选择了Prisma ORM,它就会将templates/base/prisma/下的所有文件复制到项目prisma/目录,并执行npx prisma init初始化数据库连接。 - 安装依赖:调用
npm install(或你选择的包管理器),并特别处理@trpc/client和@trpc/server的版本对齐,确保它们的 major 版本号一致,避免tRPC类型错误。 - 生成 iOS 图标:调用
sharp管道,处理./assets/icon.png,生成public/apple-touch-icon-180x180.png等 10+ 个文件,并自动在public/manifest.json中写入所有icons数组。 - 配置 TypeScript:修改
tsconfig.json,添加"types": ["node", "jest"](如果启用了测试),并设置"skipLibCheck": true以加速类型检查。
实操心得:向导中 “自定义图标路径” 这一步,我建议你始终提供一个
1024x1024的 PNG。很多设计师给的图标是512x512或带透明背景的 SVG,前者在生成180x180时会因插值而模糊,后者sharp无法直接处理。提前统一规格,能省去后续 80% 的图标调试时间。
4.3 开发与调试:t3code dev命令如何同时点亮 Electron 和 iOS 真机
创建完成后,项目结构如下:
my-t3-app/ ├── public/ │ ├── apple-touch-icon-180x180.png │ ├── manifest.json │ └── ... ├── src/ │ ├── app/ │ ├── components/ │ └── trpc/ ├── electron/ │ ├── main.ts │ └── menu.config.ts ├── ios/ │ └── service-worker.ts ├── next.config.js └── package.json现在,执行npm run dev(它会调用next dev),你的 Web 应用将在http://localhost:3000运行。但这只是第一步。要让 Electron 和 iOS 同时接入,你需要并行运行两个命令:
命令一:启动 Electron 开发环境
npm run t3code -- dev --target electron --dev-server这个命令会:
- 启动一个
electron进程,加载http://localhost:3000。 - 在主进程中注入
devtools预设,确保F12打开的是 Chrome DevTools。 - 启动一个
http-proxy-middleware实例,将所有http://localhost:3000/api/请求,代理到http://localhost:3000,从而绕过同源限制。
命令二:启动 iOS 远程调试
npm run t3code -- ios:debug这个命令会:
- 在
public/目录下生成一个debug.html文件,内容为:<!DOCTYPE html> <html> <head><title>iOS Debug</title></head> <body> <h1>iOS Debug Mode</h1> <p>请在您的 iPhone Safari 中打开此链接。</p> <script>location.href = 'http://localhost:3000';</script> </body> </html> - 启动一个微型 HTTP 服务器(
http-server),监听http://localhost:8080,并托管public/目录。 - 打印出二维码,方便你用 iPhone 相机扫描,直接跳转到
http://localhost:8080/debug.html。
此时,在你的 Mac 上打开 Safari,进入Develop > iPhone > my-t3-app,就能看到 iPhone Safari 中正在运行的localhost:3000页面的完整调试面板。你可以像在桌面 Chrome 里一样,查看 Network 请求、修改 CSS、设置断点。这就是 t3code 所谓的“调试闭环”——它不创造新工具,而是将现有工具(Safari Web Inspector, Chrome DevTools)的能力,通过 CLI 的自动化,无缝衔接到 t3 的开发流中。
4.4 构建与发布:t3code build如何生成可分发的 Electron .app 和 iOS PWA
当开发完成,准备发布时,执行npm run t3code:build:electron。CLI 会触发一个严谨的构建流水线:
- Web 资源构建:执行
next build,生成out/目录。t3code 会在此步骤后,自动运行一个postbuild脚本,检查out/_next/static/chunks/中是否存在tRPC的客户端代码,如果缺失,则报错并提示 “tRPC client bundle not found, check your tRPC configuration”。 - Electron 主进程构建:执行
tsc --project electron/tsconfig.json,生成dist/electron/main.js。CLI 会验证main.js的第一行是否为#!/usr/bin/env electron,确保它是可执行的。 - 资源打包:调用
electron-builder,将out/(Web 资源)、dist/electron/(主进程)、node_modules/(精简后的依赖)打包进一个my-t3-app.app文件夹。 - 签名与公证:调用
xcrun notarytool submit,上传my-t3-app.zip。整个过程大约需要 5-10 分钟,CLI 会实时打印notarytool的返回日志。如果公证失败(比如 entitlements 配置错误),CLI 会解析notarytool的 JSON 错误响应,并给出具体的修复建议,如 “Error: Missing entitlement 'com.apple.security.network.client'. Please add it to build/entitlements.mac.plist”。