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 工厂

相关推荐
合调于形几秒前
Jicsuanji shzjue《计算机视觉》词条汉语拼音字母标调拼写实测案例
人工智能·自然语言处理·人机交互·语音识别·学习方法
Shulex7 分钟前
Shopify 独立站弃购挽回与客服实时转化:Shulex 实战架构指南
大数据·人工智能·架构·智能客服·跨境电商
sunshine88511 分钟前
关联交易抵消与内部往来清理:合并报表中的“排雷”策略
大数据·数据库·人工智能
牧羊人.33315 分钟前
自然语言处理基础 03|门控循环单元 GRU
网络·人工智能·rnn·深度学习·自然语言处理·gru
李福春16 分钟前
技术思考问题23:AI Coding秒交差,谁替它验收?
人工智能·ai工程化·验收标准·深圳同盟·腾讯云架构师技术同盟·反演思维·ci门禁
枫叶丹422 分钟前
知识库为什么搜不到答案:多向量检索与 Late Interaction
人工智能·chatgpt·开源·agent·codex
天府云创22 分钟前
SRE之DeepSeek 10月15日故障分析、旁推和思考
网络·数据库·人工智能·故障处理·deepseek·api恢复
青桔柚子24 分钟前
2026企业AI办公工具选型指南:如何匹配组织的智能任务需求
人工智能
johnsong26 分钟前
AI会自保吗?
人工智能