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)交给 SDKProvidercontext/sdk.tsxcreateOpencodeClient 造出 client;事件来源按 internal/external 分叉(订阅 RPC 事件源 vs 走真实 SSE);事件先入队、16ms 窗口合并、batch() 一次渲染;切 workspace 重建 SDK。204 走进了 TUI 内部,本篇再往下看一层文件系统层面的规律 :同样在 cli/cmd/tui 目录下,为什么 app.tsx.tsxthread.ts.ts?这个后缀不是随手命名的

OpenCode

app.tsxthread.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.tsxcontext/*.tsxcomponent/*.tsxroutes/home.tsxtoast.tsx
.ts 纯逻辑层 thread.tsworker.tswin32.tsattach.tsevent.tsdirectory.tsclipboard.tsselection.tseditor.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 工厂

相关推荐
武子康1 小时前
中文 Prosody-Aware Eval:从四象限样本到可诊断的实时语音评测
人工智能·llm·agent
Wang's Blog1 小时前
Vibe Coding一人即团队系列61: AI驱动PPT全自动生成实战——配图集成、模板定制与多源内容适配
人工智能·powerpoint
my05921 小时前
跳出单一信息工具局限:奥米豆构建认知与心智并行的学习范式
大数据·人工智能·python·学习
俊哥V1 小时前
每日 AI 研究简报 · 2026-09-04
人工智能·ai
漂着的圆木1 小时前
GLM-5.3-Flash 接入 Agent Loop:图片验收别只查 OCR
人工智能·计算机视觉·aigc·ai agent
β添砖java1 小时前
深度学习30双向循环神经网路、机器翻译数据集、序列到序列学习、束搜索
人工智能·深度学习
俊哥V1 小时前
AI 今日研究简报 · 2026-09-05
人工智能·ai
qq_425516181 小时前
一句话生成PPT的录音APP:会议材料生成能力对比
大数据·人工智能·powerpoint
新知图书1 小时前
2.2 网页端Qwen3的使用
人工智能·智能体