1040 字
5 分钟
给 OpenCode 换个 Logo —— TUI 插件开发入门

最近给 OpenCode 换了个启动 Logo,把默认的 OpenCode 字样换成了自己的品牌名「LorenzoFeng」。整个过程踩了几个坑,记录一下完整的 TUI 插件开发流程。

为什么不能用配置文件#

直觉上,换个 Logo 应该是配置项的事。OpenCode 确实有过一个 PR(#12017)试图在 opencode.jsonc 里加 "logo" 字段,但被维护者关了,理由是:

“You can customize it with tui plugins.”

也就是说,OpenCode 的 TUI 定制走的是插件机制而非配置项。Logo、侧边栏、底部状态栏都通过 slot 机制暴露给插件。

TUI 插件的 Slot 机制#

OpenCode 的 TUI 界面由一系列 slot(插槽) 组成,插件可以注册到其中进行替换或追加:

Slot 名称位置说明
home_logo首页 Logo我们要替换的目标
home_prompt首页输入框可替换为自定义输入组件
home_bottom首页底部OS / Provider 信息
home_footer页脚状态栏下方
sidebar_title侧边栏标题会话标题
sidebar_content侧边栏内容文件变更 / MCP / LSP 状态

注册方式分两种模式:"replace" 替换整个 slot,不传 mode 则添加进去。

插件目录结构#

关键踩坑点:TUI 插件必须package.json 并声明 exports["./tui"],光放一个 .tsx 文件不生效。正确的结构:

~/.config/opencode/plugins/custom-logo/
├── package.json
└── tui.tsx

package.json 长这样:

{
  "name": "custom-logo",
  "version": "1.0.0",
  "type": "module",
  "exports": {
    "./tui": "./tui.tsx"
  },
  "peerDependencies": {
    "@opencode-ai/plugin": "*",
    "@opentui/core": "*",
    "@opentui/solid": "*",
    "solid-js": "*"
  }
}

OpenCode 通过 exports["./tui"] 来定位 TUI 入口,没有这个声明插件就不会被加载。

插件代码#

插件基于 Solid.js@opentui/solid 渲染。把「Lorenzo」和「Feng」拆成两个独立数组,用 flex 布局左右排列,分别上蓝色和正文色:

/** @jsxImportSource @opentui/solid */
import type { TuiPlugin, TuiPluginModule, TuiThemeCurrent } from "@opencode-ai/plugin/tui"

const LORENZO = [
`
██╗      ██████╗ ██████╗ ███████╗███╗   ██╗███████╗ ██████╗ 
██║     ██╔═══██╗██╔══██╗██╔════╝████╗  ██║╚══███╔╝██╔═══██╗
██║     ██║   ██║██████╔╝█████╗  ██╔██╗ ██║  ███╔╝ ██║   ██║
██║     ██║   ██║██╔══██╗██╔══╝  ██║╚██╗██║ ███╔╝  ██║   ██║
███████╗╚██████╔╝██║  ██║███████╗██║ ╚████║███████╗╚██████╔╝
╚══════╝ ╚═════╝ ╚═╝  ╚═╝╚══════╝╚═╝  ╚═══╝╚══════╝ ╚═════╝ 
`
]

const FENG = [
`
███████╗███████╗███╗   ██╗ ██████╗ 
██╔════╝██╔════╝████╗  ██║██╔════╝ 
█████╗  █████╗  ██╔██╗ ██║██║  ███╗
██╔══╝  ██╔══╝  ██║╚██╗██║██║   ██║
██║     ███████╗██║ ╚████║╚██████╔╝
╚═╝     ╚══════╝╚═╝  ╚═══╝ ╚═════╝ 
`
]

const SUBTITLE = "Web Site: https://lorenzofeng.top/"

function HomeLogo(props: { theme: TuiThemeCurrent }) {
  const lorenzoColor = props.theme.info      // 蓝色
  const fengColor = props.theme.text          // 正文色
  const muted = props.theme.textMuted
  const gap = "    "

  return (
    <box flexDirection="column" alignItems="center">
      <box flexDirection="row">
        {/* LORENZO —— 蓝色 */}
        <box flexDirection="column">
          {LORENZO.map((line) => (
            <text fg={lorenzoColor}>{line}</text>
          ))}
        </box>
        {/* 间距 */}
        <box flexDirection="column">
          {LORENZO.map(() => (
            <text fg={muted}>{gap}</text>
          ))}
        </box>
        {/* FENG —— 正文色 */}
        <box flexDirection="column">
          {FENG.map((line) => (
            <text fg={fengColor}>{line}</text>
          ))}
        </box>
      </box>
      <text> </text>
      <box flexDirection="row" gap={1}>
        <text fg={muted}>━</text>
        <text fg={muted} bold>{SUBTITLE}</text>
        <text fg={muted}>━</text>
      </box>
      <text> </text>
    </box>
  )
}

const tui: TuiPlugin = async (api) => {
  api.slots.register({
    mode: "replace",
    slots: {
      home_logo(ctx) {
        return <HomeLogo theme={ctx.theme.current} />
      },
    },
  })
}

export default { id: "custom-logo", tui }

几个要点:

  • /** @jsxImportSource @opentui/solid */ 声明 JSX 编译目标,这句不能丢
  • Logo 拆成两个数组 → <box flexDirection="row"> 横向排列 → 左边的 map 遍历 LORENZO,右边的 map 遍历 FENG
  • slots.register() 接受 { mode, slots } 对象,"replace" 表示完全替换 OpenCode 默认 Logo
  • slot 渲染函数接收 ctx 上下文,通过 ctx.theme.current 拿到当前主题色

颜色配置#

变量用途
theme.primary主色
theme.accent强调色
theme.info蓝色系
theme.success绿色系
theme.warning黄色系
theme.error红色系

也可以用硬编码色值 fg="#ff6b6b",但不会随主题切换。

ASCII 艺术字生成#

Logo 用的字体风格是 ANSI Shadow,生成工具推荐 patorjk.com/software/taag,有上百种字体可选。推荐几个适合终端显示的:

字体效果
ANSI Shadow带阴影的块状字
Big简洁大方
Block实心方块
Doom游戏风格
Graffiti涂鸦风

注册插件#

插件写好后需要注册。有两种方式:

方式一——自动发现:把插件放在 ~/.config/opencode/plugins/ 目录下,OpenCode 启动时自动扫描加载。

方式二——显式声明:在 opencode.jsonctui.jsonplugin 数组中加绝对路径:

{
  "plugin": [
    "~/.config/opencode/plugins/custom-logo"
  ]
}

两种方式我都用了,确保万无一失。插件支持热更新,改 tui.tsx 后重进 TUI 即可生效,不需要重启 OpenCode。

总结#

OpenCode 的 TUI 插件系统比预期灵活很多——slot 机制让几乎所有界面元素都可替换。虽然 Logo 不能通过配置文件直接改,但写一个十几行的插件就能搞定,而且能做的事情远不止换 Logo:侧边栏、输入框、状态栏都能深度定制。

给 OpenCode 换个 Logo —— TUI 插件开发入门
https://lorenzofeng.top/posts/opencode-tui-logo-customization/
作者
Lorenzo Feng
发布于
2026-06-02
许可协议
CC BY-NC-SA 4.0