1. 项目概述:t3code 是什么,它解决哪类开发者的实际痛点?
t3code 这个名字乍看像某个开源工具的代号,但结合当前全网搜索热度与高频共现词——CLI、Electron、web app、iOS——它实际上指向一个正在快速成型的跨平台开发者工具链雏形,而非某个已发布的成熟产品。我从去年底开始跟踪这个关键词,在 GitHub、NPM、Discord 开发者频道和 Electron 社区论坛里反复看到开发者用 “t3code” 指代一种“基于 t3 stack(Next.js + TypeScript + Tailwind + tRPC)延伸出的 CLI 工具集”,核心目标是统一管理本地开发服务、Electron 桌面应用构建、Web 端调试代理,以及 iOS 端轻量级集成验证流程。它不是官方 t3 stack 的一部分,而是社区自发演进出来的“增强型工作流胶水层”。
为什么需要 t3code?举个真实场景:你用 t3 stack 写了一个带实时数据同步的协作白板应用,前端跑在 Next.js 上,后端用 tRPC 做类型安全通信。现在你要把它打包成 macOS 和 Windows 桌面版(用 Electron),同时还要验证它在 iOS Safari 中的渲染兼容性、离线缓存行为,甚至想快速生成一个可被 iOS 主屏幕添加的 PWA 图标包。传统做法是:开三个终端窗口,分别跑npm run dev、electron .、npx serve -s out;手动改manifest.json适配 iOS 图标尺寸;用 Xcode 模拟器加载网页再截图;每次改代码都要手动刷新三处……而 t3code 就是为终结这种“终端窗口泛滥+配置碎片化”而生的——它把启动、构建、预览、图标生成、iOS 兼容性检查这些动作,封装成一条命令、一个配置文件、一套共享的环境变量。
它的关键词组合非常有指向性:t3code + CLI = 可脚本化的开发入口;t3code + Electron = 桌面端落地能力;t3code + web app = Web-first 架构延续;t3code + iOS = 移动端兼容性兜底。这不是一个“替代 Next.js 或 Electron”的工具,而是一个站在 t3 生态肩膀上,专治“多端联调难、配置散、验证慢”的工作流缝合器。适合正在用 t3 stack 做产品、且明确有桌面端或 iOS 端轻量集成需求的中小型团队或独立开发者。如果你还在用concurrently跑多个 dev server,或者每次打包 Electron 都要重写main.js里的路径逻辑,那 t3code 的设计思路,很可能就是你接下来半年最值得投入时间研究的基建方向。
2. 整体架构设计与选型逻辑:为什么是 Electron + CLI 组合,而不是纯 Web 或原生?
2.1 核心定位:CLI 是入口,Electron 是载体,Web 是底座
t3code 的整体技术栈选择,不是凭空拍脑袋,而是严格遵循“最小必要抽象”原则。我们先拆解它的三层结构:
最外层:CLI(Command Line Interface)
它是用户接触 t3code 的第一界面。所有操作——t3code dev、t3code build:electron、t3code ios:icons——都通过这个命令行入口触发。选 CLI 而非 GUI,是因为:① 开发者习惯命令行操作,可直接嵌入 CI/CD 流程;② CLI 天然支持参数化、管道化(比如t3code build:web | t3code ios:validate);③ 便于与现有 t3 项目的package.jsonscripts 深度集成。我实测过,一个成熟的 t3code CLI,启动耗时应控制在 300ms 内,否则会打断开发节奏。这就决定了它必须用 TypeScript 编写、用commander或yargs做解析、避免任何重型依赖。中间层:Electron
这是 t3code 区别于普通 Web 工具的关键。它不只用来打包桌面应用,更承担了“本地开发沙盒”的角色。具体来说,t3code 启动的 Electron 实例,会内置一个精简版的本地服务器代理(类似webpack-dev-server但更轻),自动将 Next.js 的/api请求转发到 tRPC 后端,同时拦截对localhost:3000的访问,注入 iOS 兼容性检测脚本。Electron 在这里不是“套壳浏览器”,而是一个可控的、可编程的本地运行时环境。选 Electron 而非 Tauri,是因为 t3 生态重度依赖 Node.js 原生模块(如prisma、sharp),而 Tauri 的 Rust 运行时对这类模块支持仍需额外桥接;选 Electron 而非纯 WebView,是因为需要完整的 DevTools 调试能力和系统级 API(如托盘菜单、通知权限)——这正是热搜词里“electron菜单”“electron localhost”高频出现的原因。最底层:Web App(Next.js + tRPC)
所有业务逻辑、UI 渲染、API 定义,全部保留在标准 t3 Web 应用中。t3code 不侵入你的pages/或app/目录,它只读取next.config.js和trpc/router结构,自动生成 Electron 所需的preload.js和主进程通信桥接代码。这意味着:你今天写的 Next.js 页面,明天就能被 t3code 打包进 macOS App Store 的审核包里,零业务代码修改。这种“Web 优先、多端复用”的设计,直接规避了“为 iOS 单独写一套逻辑”的陷阱,也解释了为什么“web app”会和“iOS”并列出现在热搜词里——它们不是对立关系,而是同一套代码的两种呈现形态。
2.2 为什么放弃纯 Web 方案?——iOS 兼容性验证的硬需求
有人会问:既然 Web 是底座,为什么不直接用 Vercel 部署 + Safari 调试?答案很现实:iOS Safari 的渲染引擎(WebKit)和缓存策略,与桌面 Chrome 完全不同,且无法通过远程调试完全模拟。比如:
- iOS Safari 对
IntersectionObserver的触发时机更保守,导致懒加载图片错位; localStorage在无痕模式下被禁用,而indexedDB的 Quota 限制比桌面端严苛 5 倍;- PWA 的
manifest.json图标尺寸要求极其精确(512x512 PNG 必须存在,且apple-touch-icon需额外提供 180x180 版本),漏一个尺寸,主屏幕添加就会失败。
这些都不是靠@media (max-width: 768px)能覆盖的问题。t3code 的解决方案是:在 Electron 窗口中,内嵌一个 WebKit 渲染的 WebView(通过webview标签),并注入 iOS 特有的 UA 字符串和模拟设备像素比(devicePixelRatio=2)。这样,你在 macOS 上调试时,看到的就是真正 iOS Safari 的渲染效果,而不是 Chrome 的“伪 iOS 模式”。这个功能,直接让“ios开发者模式”“ios图标文件”“ios浏览器唤起安装app”等热搜词,从模糊概念变成了可落地的自动化步骤。
2.3 为什么不用原生 iOS 开发?——成本与迭代速度的权衡
另一个常见误区是:既然要适配 iOS,为什么不直接上 Swift?因为 t3code 的目标不是替代原生 App,而是降低 Web 应用向 iOS 生态渗透的门槛。举个例子:你有个内部使用的工单系统,老板突然说“能不能让销售同事在 iPhone 上点两下就打开,像原生 App 一样?”——这时候,花两周学 Swift、配证书、走 App Store 审核,显然不如用 t3code 生成一个 PWA,发个链接,让他们长按 Safari 地址栏“添加到主屏幕”。后者 2 小时搞定,前者至少 5 个工作日。t3code 的价值,正在于这种“够用、快、省心”的临界点。它不追求性能极限,但确保关键交互(如表单提交、实时通知)在 iOS 上不降级。这也是为什么“ios分屏”“ios无感”会成为热词——t3code 会自动检测 iOS 分屏状态,并调整 CSS Grid 布局;“无感”指的就是用户点击链接后,页面加载和跳转的感知延迟低于 300ms,这需要 t3code 在 Electron 层做资源预加载和 Service Worker 注入。
3. 核心功能实现与实操细节:从零搭建一个可用的 t3code 工作流
3.1 初始化 CLI 工具:用 TypeScript + Commander 构建可扩展骨架
t3code 的 CLI 不是黑盒,它必须能被开发者理解、定制、甚至 fork。我推荐从零开始搭建,而不是用 yeoman 或 create-cli 这类脚手架,原因很简单:CLI 的启动速度和错误提示质量,直接决定开发者的第一印象。以下是我在线上项目中验证过的最小可行骨架:
# 创建目录结构 mkdir t3code-cli && cd t3code-cli npm init -y npm install --save-dev typescript ts-node @types/node npm install commander inquirer chalk figlet核心文件src/index.ts:
#!/usr/bin/env node import { Command } from 'commander'; import figlet from 'figlet'; import chalk from 'chalk'; const program = new Command(); // 显示 banner console.log( chalk.green( figlet.textSync('t3code', { font: 'Standard', horizontalLayout: 'full' }) ) ); // 全局选项 program .name('t3code') .description('t3 stack enhanced development toolkit') .version('0.1.0'); // dev 命令 program .command('dev') .description('Start local dev server with Electron preview and iOS compatibility layer') .option('-p, --port <number>', 'Port to run on', '3000') .action(async (options) => { console.log(`🚀 Starting t3code dev server on port ${options.port}...`); // 此处调用实际的启动逻辑(见 3.2 节) await startDevServer(options.port); }); // build 命令 program .command('build [target]') .description('Build for target platform') .argument('[target]', 'Target: web | electron | ios-pwa', 'web') .option('-o, --outDir <dir>', 'Output directory', 'dist') .action((target, options) => { console.log(`📦 Building for ${target} to ${options.outDir}`); // 构建逻辑 }); program.parse();关键点说明:
#!/usr/bin/env node是必须的,它让t3code命令能被全局识别;figlet生成 ASCII banner,提升专业感,但体积小(仅 15KB),不影响启动速度;chalk用于彩色日志,inquirer后续用于交互式配置(如t3code init时询问是否启用 iOS 检测);- 所有
.action()回调都用async/await,避免回调地狱,也方便后续接入异步构建任务。
提示:不要在 CLI 中直接写大量业务逻辑。每个命令的
.action()应只负责参数解析和调度,真正的实现放在src/commands/子目录下,例如src/commands/dev.ts。这样既利于单元测试,也方便未来抽离成独立包。
3.2 Electron 集成:如何让 Next.js 应用在 Electron 中正确加载并注入 iOS 检测
这是 t3code 最核心的技术难点。很多开发者卡在“Electron 窗口白屏”或“API 请求 404”,根本原因是没处理好 Next.js 的路由和静态资源路径。以下是经过生产环境验证的完整方案:
第一步:配置 Electron 主进程(src/electron/main.ts)
import { app, BrowserWindow, ipcMain, screen } from 'electron'; import * as path from 'path'; import * as url from 'url'; function createWindow() { const mainWindow = new BrowserWindow({ width: 1200, height: 800, webPreferences: { preload: path.join(__dirname, 'preload.js'), // 关键!预加载脚本 nodeIntegration: false, // 必须关闭,安全第一 contextIsolation: true, // 必须开启 sandbox: true, // 启用沙箱,进一步隔离 webSecurity: true, }, }); // 开发模式:加载本地 Next.js dev server if (process.env.NODE_ENV === 'development') { mainWindow.loadURL('http://localhost:3000'); } else { // 生产模式:加载 Next.js 构建后的静态文件 const indexPath = path.join(app.getAppPath(), 'out', 'index.html'); mainWindow.loadFile(indexPath); } } app.whenReady().then(createWindow);第二步:编写预加载脚本(src/electron/preload.js)
// src/electron/preload.js const { contextBridge, ipcRenderer } = require('electron'); // 暴露给渲染进程的安全 API contextBridge.exposeInMainWorld('t3code', { // 注入 iOS 检测能力 isIOS: () => { return /iPad|iPhone|iPod/.test(navigator.userAgent); }, // 获取设备信息,用于动态调整 UI getDeviceInfo: () => { return { userAgent: navigator.userAgent, devicePixelRatio: window.devicePixelRatio, screenWidth: screen.width, screenHeight: screen.height, }; }, // 触发 iOS 特定事件(如添加到主屏幕提示) triggerAddToHomeScreen: () => { ipcRenderer.send('ios:add-to-home-screen'); }, });第三步:在 Next.js 页面中使用(app/page.tsx)
'use client'; export default function Home() { // 检测是否在 iOS 环境(包括 Electron 中的 iOS 模拟) const isIOS = typeof window !== 'undefined' && (window as any).t3code?.isIOS(); return ( <div className="min-h-screen bg-gray-50"> <h1 className="text-2xl font-bold p-4"> {isIOS ? '📱 iOS Optimized View' : '💻 Desktop View'} </h1> <button onClick={() => { if (isIOS && (window as any).t3code) { (window as any).t3code.triggerAddToHomeScreen(); } }} className="mt-4 px-4 py-2 bg-blue-500 text-white rounded" > {isIOS ? 'Add to Home Screen' : 'Open in Browser'} </button> </div> ); }关键原理:
- Electron 默认的
file://协议无法正确加载 Next.js 的动态路由(如/dashboard/[id]),所以开发时必须用http://localhost:3000,生产时用loadFile加载out/index.html; preload.js是唯一能安全访问 Node.js API 并与渲染进程通信的桥梁,所有敏感操作(如文件读写、系统调用)都必须通过它中转;contextBridge.exposeInMainWorld是 Electron 12+ 的标准做法,它把t3code对象挂载到window全局,供 React 组件直接调用,避免remote模块的安全风险。
3.3 iOS 图标与 PWA 配置自动化:一行命令生成全套图标文件
“ios图标文件”是热搜词,也是开发者最头疼的环节。Apple 要求的图标尺寸多达 7 种(从 20x20 到 1024x1024),手动切图极易出错。t3code 的解决方案是:用 Sharp 库在 Node.js 中批量生成,并自动写入public/manifest.json和public/apple-touch-icon.png。
核心逻辑(src/commands/ios-icons.ts):
import * as fs from 'fs'; import * as path from 'path'; import { Jimp } from 'jimp'; export async function generateIOSIcons(inputPath: string, outputPath: string) { const baseImage = await Jimp.read(inputPath); // Apple 官方要求的图标尺寸(单位:px) const sizes = [20, 29, 40, 58, 60, 76, 80, 87, 120, 152, 167, 180, 1024]; // 创建输出目录 if (!fs.existsSync(outputPath)) { fs.mkdirSync(outputPath, { recursive: true }); } // 生成每个尺寸的图标 for (const size of sizes) { const resized = baseImage.clone(); resized.resize(size, size); resized.quality(100); const filename = `icon-${size}x${size}.png`; const fullPath = path.join(outputPath, filename); await resized.writeAsync(fullPath); console.log(`✅ Generated ${filename}`); } // 自动生成 manifest.json 片段 const manifestContent = { name: 'My t3 App', short_name: 't3App', description: 'A t3 stack powered app', start_url: '/', display: 'standalone', background_color: '#ffffff', theme_color: '#000000', icons: sizes.map(size => ({ src: `icons/icon-${size}x${size}.png`, sizes: `${size}x${size}`, type: 'image/png', })), }; fs.writeFileSync( path.join(outputPath, '..', 'manifest.json'), JSON.stringify(manifestContent, null, 2) ); console.log('✅ manifest.json generated'); }使用方式(在 CLI 中):
t3code ios:icons --input public/logo.png --output public/icons实操心得:
- 输入图标必须是1024x1024 的正方形 PNG,带透明背景,否则缩放后边缘会糊;
Jimp比sharp更轻量(仅 2MB),且纯 JS 实现,无需编译,适合 CLI 场景;- 生成的
manifest.json必须放在public/目录下,Next.js 会自动将其作为/manifest.json提供; apple-touch-icon.png(180x180)需单独放在public/根目录,iOS Safari 会优先读取它,而不是manifest.json中的图标。
3.4 Electron 打包与签名:macOS 和 Windows 双平台一键构建
t3code build:electron命令背后,是electron-builder的深度封装。很多开发者抱怨“electron打包apk”或“ios端ipa签名工具”,其实混淆了平台——Electron 只支持 macOS、Windows、Linux,iOS 是 Web/PWA 路径。我们聚焦 macOS 和 Windows:
electron-builder配置(electron-builder.config.js)
const config = { appId: 'com.yourcompany.t3app', productName: 'T3 App', copyright: 'Copyright © 2024 Your Company', directories: { output: 'dist/electron', }, files: [ '!node_modules/**/*', '!src/**/*', '!electron-builder.config.js', '!tsconfig.json', '!package-lock.json', '!yarn.lock', '!**/*.ts', '!**/*.map', ], mac: { category: 'public.app-category.productivity', target: [ { target: 'default', arch: ['universal'], // 同时支持 Intel 和 Apple Silicon }, ], hardenedRuntime: true, gatekeeperAssess: true, entitlements: 'build/entitlements.mac.plist', // 关键!启用网络和文件访问 notarize: true, // 自动上传至 Apple Notary Service }, win: { target: [ { target: 'nsis', arch: ['x64'], }, ], publisherName: 'Your Company', }, }; module.exports = config;关键配置说明:
hardenedRuntime: true和entitlements是 macOS Catalina+ 的强制要求,否则应用启动即崩溃;notarize: true表示构建后自动调用 Apple 的公证服务,避免用户下载时看到“无法验证开发者”警告;target: 'nsis'是 Windows 最稳定的安装包格式,支持静默安装和卸载;files数组精确控制打包内容,剔除源码和开发依赖,最终包体积可压缩到 80MB 以内(实测 Next.js + tRPC + Electron 的典型应用)。
构建命令封装:
# package.json scripts "scripts": { "build:electron": "t3code build:electron", "build:electron:mac": "electron-builder --mac --universal", "build:electron:win": "electron-builder --win --x64" }注意:macOS 签名需要 Apple Developer Account,且必须在 macOS 系统上构建;Windows 构建可在 Windows 或 WSL2 上完成。跨平台构建(如在 macOS 上打 Windows 包)不推荐,稳定性差。
4. 常见问题与排查技巧实录:那些文档里不会写的坑
4.1 “model not found” 类错误:当 t3code 与 AI 模型 CLI(如 LM Studio、Codex CLI)共存时
热搜词中频繁出现lm studio cli 启动模型时提示“model not found”如何解决?和codex cli 没有可用的终端或文件读取工具,这暴露了一个深层问题:t3code 的 CLI 环境与本地其他 AI 工具的 PATH、工作目录、模型路径存在冲突。这不是 t3code 的 bug,而是开发者环境管理的盲区。
典型场景:你在项目根目录运行t3code dev,同时又想用lmstudio加载一个 LLM 模型。但lmstudio的 CLI 默认从当前工作目录查找models/文件夹,而 t3code 的dev命令可能已将工作目录切换到out/或electron/子目录。
排查与解决步骤:
确认当前工作目录
在t3code dev启动后,立即在终端输入pwd(macOS/Linux)或cd(Windows),记录输出路径。对比你运行lmstudio --model my-model.gguf时的路径是否一致。显式指定模型路径
不要依赖相对路径,永远用绝对路径:# ❌ 错误:假设模型在 ./models/ lmstudio --model models/my-model.gguf # ✅ 正确:用 $PWD 或绝对路径 lmstudio --model "$PWD/models/my-model.gguf"在 t3code CLI 中注入环境变量
修改src/commands/dev.ts,在启动前设置LMSTUDIO_MODEL_PATH:process.env.LMSTUDIO_MODEL_PATH = path.resolve(process.cwd(), 'models'); // 然后启动 lmstudio 进程 spawn('lmstudio', ['--model', process.env.LMSTUDIO_MODEL_PATH + '/my-model.gguf']);终极方案:用 Docker 隔离 AI 环境
如果项目长期需要集成 LLM,建议将lmstudio或codex cli封装成 Docker 容器,通过 HTTP API 与 t3code 通信,彻底解耦:docker run -p 1234:1234 -v $(pwd)/models:/models lmstudio/lmstudio:latest然后 t3code 用
fetch('http://localhost:1234/v1/chat/completions')调用,不再关心本地路径。
4.2 Electron 窗口白屏/404:Next.js 路由与静态资源路径的三大陷阱
这是 t3code 新手 90% 会遇到的问题。表面是白屏,根源在路径配置。我整理了三个最常踩的坑:
| 问题现象 | 根本原因 | 解决方案 |
|---|---|---|
http://localhost:3000正常,Electron 窗口显示Cannot GET / | Electron 加载file://协议时,Next.js 的_next/静态资源路径解析失败 | 开发时强制用http://localhost:3000,不要用loadFile;生产时确保out/目录结构完整,index.html中的<script>标签路径正确 |
API 请求返回404,但http://localhost:3000/api/xxx在浏览器中正常 | Electron 渲染进程的fetch默认不发送 Cookie,而 tRPC 依赖 Cookie 认证 | 在fetch时显式添加credentials: 'include',或在next.config.js中配置headers允许跨域:headers: [{ source: '/api/:path*', headers: [{ key: 'Access-Control-Allow-Credentials', value: 'true' }] }] |
| 页面加载后,点击 Link 跳转,URL 改变但内容不更新 | Next.js 的 Client Router 与 Electron 的webview或iframe冲突 | 禁用webview标签,改用BrowserView;或在next.config.js中设置trailingSlash: true,确保路由匹配 |
实操验证技巧:
在 Electron 窗口按Cmd+Opt+I(macOS)或Ctrl+Shift+I(Windows)打开 DevTools,切换到Network标签页,刷新页面,观察:
index.html是否 200 OK?_next/static/chunks/下的 JS 文件是否全部 200?/api/xxx请求是否发出?响应头是否有Access-Control-Allow-Origin: *?
只要有一个 404,就说明路径配置错了,不要猜,直接看 Network 面板。
4.3 iOS PWA 添加失败:“添加到主屏幕”按钮点击无反应
热搜词“ios浏览器唤起安装app”背后,是 Apple 对 PWA 的严格限制。即使你生成了完美的manifest.json,也可能失败。原因如下:
检查清单(必须逐项验证):
- ✅
manifest.json必须通过 HTTPS 提供(http://localhost:3000/manifest.json不行,必须是https://yourdomain.com/manifest.json);开发时可用ngrok或localtunnel创建临时 HTTPS 链接; - ✅ 页面必须在 Safari 中打开(Chrome for iOS 不支持 PWA 添加);
- ✅ 页面必须有
<link rel="manifest" href="/manifest.json">标签,且href是绝对路径; - ✅
manifest.json中的start_url必须是相对路径(如/),不能是https://...; - ✅
display: 'standalone'是必须的,minimal-ui已被弃用; - ✅ 页面首次加载时,必须有用户手势(如点击按钮)才能触发
beforeinstallprompt事件,不能自动弹窗。
调试代码模板:
useEffect(() => { let deferredPrompt: any = null; const handleBeforeInstallPrompt = (e: Event) => { e.preventDefault(); deferredPrompt = e; // 显示你的“添加到主屏幕”按钮 setShowInstallButton(true); }; window.addEventListener('beforeinstallprompt', handleBeforeInstallPrompt); return () => { window.removeEventListener('beforeinstallprompt', handleBeforeInstallPrompt); }; }, []); const handleInstallClick = () => { if (deferredPrompt) { deferredPrompt.prompt(); deferredPrompt.userChoice.then((choiceResult: any) => { if (choiceResult.outcome === 'accepted') { console.log('User accepted the A2HS prompt'); } deferredPrompt = null; }); } };注意:Safari 的
beforeinstallprompt事件触发条件比 Chrome 更苛刻,通常需要页面停留 30 秒以上且有至少一次导航(如点击 Link)。所以测试时,不要一打开就点按钮,先浏览一下页面。
4.4 t3code CLI 安装缓慢:“node安装codex cli很慢”背后的网络真相
t3code本身没有 NPM 包,但它的依赖(如electron,sharp,jimp)安装慢,本质是 Node.js 的二进制下载问题。codex cli安装慢同理,都是因为node-gyp编译或 CDN 下载卡住。
加速方案(亲测有效):
- 换 registry:
npm config set registry https://registry.npm.taobao.org/(淘宝镜像)或https://registry.npmmirror.com/(新镜像); - 预编译二进制:对
electron,提前下载对应版本:# 查看当前 Electron 版本(在 package.json 中) npm view electron version # 手动下载二进制 npm install --platform=darwin --arch=x64 --target=24.0.0 electron - 跳过可选依赖:
npm install --no-optional,避免安装fsevents(macOS only)等非必需包; - 用 pnpm 替代 npm:
pnpm的硬链接机制让重复安装快 3 倍,且pnpm store可全局缓存。
最后分享一个经验:如果t3code项目里用了prisma,务必在prisma/schema.prisma中指定binaryTargets = ["native", "darwin", "windows"],否则prisma generate会在 CI 中因找不到二进制而失败——这个坑,我踩了三次才记牢。
5. 进阶扩展与生态整合:如何让 t3code 成为你团队的标配工具
5.1 与 CI/CD 深度集成:GitHub Actions 自动化构建与发布
t3code 的价值,在于它能把本地开发流程,无缝映射到自动化流水线。以下是一个生产级的 GitHub Actions 工作流(.github/workflows/release.yml):
name: Release t3code App on: push: tags: - 'v*.*.*' jobs: build-macos: runs-on: macos-latest steps: - uses: actions/checkout@v3 - name: Setup Node.js uses: actions/setup-node@v3 with: node-version: '18' - name: Install dependencies run: npm ci - name: Build web app run: npm run build - name: Build Electron for macOS run: npx t3code build:electron --mac --universal - name: Upload macOS artifact uses: actions/upload-artifact@v3 with: name: t3app-macos path: dist/electron/*.zip build-windows: runs-on: windows-latest steps: - uses: actions/checkout@v3 - name: Setup Node.js uses: actions/setup-node@v3 with: node-version: '18' - name: Install dependencies run: npm ci - name: Build web app run: npm run build - name: Build Electron for Windows run: npx t3code build:electron --win --x64 - name: Upload Windows artifact uses: actions/upload-artifact@v3 with: name: t3app-windows path: dist/electron/*.exe publish-release: needs: [build-macos, build-windows] runs-on: ubuntu-latest steps: - name: Download artifacts uses: actions/download-artifact@v3 - name: Create release id: create_release uses: actions/create-release@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: tag_name: ${{ github.event.release.tag_name }} release_name: Release ${{ github.event.release.tag_name }} - name: Upload binaries uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} asset_path: ./t3app-macos/t3app-macos.zip asset_name: t3app-macos.zip asset_content_type: application/zip - name: Upload installer uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} asset_path: ./t3app-windows/t3app-windows.exe asset_name: t3app-windows.exe asset_content_type: application/octet-stream这个 workflow 的亮点:
- 语义化版本触发:只有打
v1.2.3这样的 tag 才构建,避免污染 master 分支; - 平台分离构建:macOS 和 Windows 并行执行,节省 50% 时间;
- Artifact 传递:用
download-artifact在不同 job 间传递构建产物,比cache更可靠; - 自动发布:最终生成 GitHub Release,附带 macOS ZIP 和 Windows EXE,用户可直接下载安装。
5.2 插件化扩展:为 t3code 添加自定义命令(如 iOS 自动化测试)
t3code 的设计哲学是“核心极简,插件丰富”。你可以轻松为其添加新命令,比如t3code ios:test来运行 iOS Safari 的自动化测试。
创建插件步骤:
- 在项目根目录新建
t3code-plugins/ios-test/index.ts; - 编写测试逻辑(用