205、【Agent】【OpenCode】TUI 内部:.tsx 与 .ts 的分界线

【声明】本博客所有内容均为个人业余时间创作,所述技术案例均来自公开开源项目(如Github,Apache基金会),不涉及任何企业机密或未公开技术,如有侵权请联系删除

标题

205、【Agent】【OpenCode】TUI 内部:.tsx 与 .ts 的分界线

背景

上篇 blog

【Agent】【OpenCode】TUI 内部:SDK 客户端与事件流

拆了 TUI 消费 thread.ts 服务的现场:app.tsx:144-150 把 transport(url/fetch/events/directory)交给 SDKProvider,context/sdk.tsx 用 createOpencodeClient 造出 client;事件来源按 internal/external 分叉(订阅 RPC 事件源 vs 走真实 SSE);事件先入队、16ms 窗口合并、batch() 一次渲染;切 workspace 重建 SDK。204 走进了 TUI 内部,本篇再往下看一层文件系统层面的规律 :同样在 cli/cmd/tui 目录下,为什么 app.tsx 是 .tsx 而 thread.ts 是 .ts?这个后缀不是随手命名的

OpenCode

app.tsx 和 thread.ts 只差一个 x,含义却完全不同:.tsx = TypeScript + JSX 。这个差异决定了编译器怎么解读文件里的 <。


🧩 本质区别:JSX 解析

.ts 是纯 TypeScript,文件里的 < 一律当泛型 或比较运算符 。.tsx 开启了 JSX 解析------HTML 风格的标签语法(<组件>...</组件>)会被当成组件标记:

tsx 复制代码
// app.tsx 里的 JSX:< 是标签
<SDKProvider url={input.url} directory={input.directory}>
  <SyncProvider>...</SyncProvider>
</SDKProvider>

对比 thread.ts 里的 <------它是泛型参数,不是标签:

ts 复制代码
// thread.ts 里的 <:是泛型
type RpcClient = ReturnType<typeof Rpc.client<typeof rpc>>   // thread.ts:22
client.on<Event>("event", handler)                            // thread.ts:44

同一个 <,在 .tsx 里是标签、在 .ts 里是泛型------编译器靠后缀决定怎么解析。这就是后缀不能随便写的原因。


📊 TUI 目录的分布规律

实测 cli/cmd/tui 目录下 30+ 个文件,后缀与"层"严格对应:

后缀 层 典型文件
.tsx 渲染/组件层 app.tsx、context/*.tsx、component/*.tsx、routes/home.tsx、toast.tsx
.ts 纯逻辑层 thread.ts、worker.ts、win32.ts、attach.ts、event.ts、directory.ts、clipboard.ts、selection.ts、editor.ts

凡是"要渲染 UI、要进组件树 "的文件用 .tsx;纯逻辑、工具、后端文件 用 .ts。


🔬 判据:是"属于组件层",不是"字面含 JSX"

最容易被忽略的一点:.tsx 与否取决于文件在组件树里的角色,而不是它有没有写 JSX 标签。三个证据:

1. sdk.tsx 几乎没 JSX,却是 .tsx

ts 复制代码
// sdk.tsx:11
export const { use: useSDK, provider: SDKProvider } = createSimpleContext({...})

它自身只有泛型 <...>,没有任何 JSX 标签------但它是组件树的提供者 (返回 SDKProvider 组件,被 app.tsx 放进树里),所以按 .tsx。

2. helper.tsx 的 createSimpleContext 生成真 JSX

tsx 复制代码
// helper.tsx:10-15
provider: (props: ParentProps<Props>) => {
  return (
    <Show when={...}>
      <ctx.Provider value={init}>{props.children}</ctx.Provider>
    </Show>
  )
}

这里有真 JSX (<Show>、<ctx.Provider>)------生成 provider 组件的工厂,必须 .tsx。

3. context/directory.ts 提供数据但不渲染,所以是 .ts

它给 TUI 提供目录上下文数据,却不产出任何组件 ,纯逻辑 → .ts。


⚠️ 别被骗:.ts 里的 <...> 是泛型

thread.ts:22 的 <typeof rpc>、thread.ts:44 的 <Event>、win32.ts:14 的 <typeof kernel>------这些都是泛型参数 ,不是 JSX。判断标准:泛型 <> 里是类型 ,JSX <> 里是组件或标签。


💡 TUI 用的是 Solid.js

app.tsx 的渲染入口是 Solid:

ts 复制代码
// app.tsx:6
import { Switch, Match, createEffect, ErrorBoundary, ... } from "solid-js"
// app.tsx:132
render(
  () => { return ( ...整棵组件树... ) },
  { targetFps: 60, exitOnCtrlC: false, ... },
)

JSX 是 React/Solid/Preact/Vue 等共享的模板语法,都用 .tsx。TUI 用的是 Solid.js ,它的响应式(signal/batch)也解释了 204 篇里"事件 batch() 一次渲染"的设计。


📊 总结

维度 .tsx .ts
含义 TypeScript + JSX 纯 TypeScript
< 的语义 组件标签 泛型 / 比较
属于 渲染/组件层 纯逻辑/工具/后端
典型 app / context 组件 / component / route thread / worker / win32 / clipboard

📌 一句话记忆

.tsx 是 TypeScript + JSX:编译器靠后缀决定 < 当标签还是泛型。TUI 里 .tsx 属于组件/渲染层(app、provider、component),.ts 属于纯逻辑层(thread、worker、win32);判据是"是否进组件树"而非"是否写了 JSX"------sdk.tsx 没 JSX 也 .tsx,directory.ts 提供数据不渲染所以 .ts。


OK,本篇先到这里,如有疑问,欢迎评论区留言讨论,祝各位功力大涨,技术更上一层楼!!!更多内容见下篇 blog

【Agent】【OpenCode】TUI 内部:装配层与 context 工厂

相关推荐
子兮曰4 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
回眸&啤酒鸭4 天前
【回眸】Minicart 电商购物车核心功能落地指南
人工智能
一隅论数智4 天前
给AI一张“业务概念地图“:本体如何从哲学走向企业智能
大数据·人工智能·经验分享·笔记·学习·学习方法·政务
AI的探索之旅4 天前
97 个 OpenCV 实例(三十):双目立体,从标定到点云
人工智能·opencv·计算机视觉
AlbertZein4 天前
Step-5-Preview 上手实测:3D 游戏、金融分析、网页设计一次跑完
人工智能·aigc
LaughingZhu4 天前
Product Hunt 每日热榜 | 2026-09-19
人工智能·深度学习·神经网络·搜索引擎·百度
美狐美颜SDK开放平台4 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
1点东西4 天前
做了近两年的Agent开发,其实真正要学的就是这五件事
llm·agent·ai编程
wukangjupingbb4 天前
智能网联汽车安全能力框架
人工智能