首页
/ Tabby 源码构建与插件开发指南:从依赖安装、打包安装程序到插件 Provider 扩展机制

Tabby 源码构建与插件开发指南:从依赖安装、打包安装程序到插件 Provider 扩展机制

2026-09-03 15:20:46作者:伍霜盼Ellen

本文基于 Tabby 仓库的 HACKING.md 开发指南,完整覆盖环境准备、源码构建、跨平台安装包生成、项目与插件目录结构,以及 Tabby 插件系统的加载与扩展机制;并结合 app/src/plugins.tsapp/src/entry.ts 等源码实现,说明插件发现、tabby-plugin 关键字过滤与 NgModule 注入的底层原理,帮助你在本地跑起 Tabby 源码并开发出自己的插件。

项目背景与技术栈

Tabby 是一个 Electron 桌面应用(README 定位为 "A terminal for a modern age"),前端使用 TypeScript 配合 Angular 框架编写,整体由 Webpack 构建。理解这一点对开发至关重要,因为整个代码库被拆分为两类部分:

  • 宿主应用app/ 目录):Electron 壳子,只包含"最基本的骨架",负责主进程启动、窗口管理、插件发现与加载、配置与错误处理;
  • 插件tabby-* 目录):核心 UI、标签页管理、本地 Shell、终端、SSH、串口等功能全部以插件形式实现,通过 Angular 的 NgModule 注入机制拼装进应用根模块。

根目录的 package.json 中,electronwebpacktypescriptpug(模板语言)、sass@angular/* 等依赖印证了上述技术栈;app/package.jsonpeerDependencies 则列出了所有随发行版一起分发的内置插件包(tabby-coretabby-localtabby-terminal 等)。

环境准备与依赖安装

开发环境需要 Node.js 15 或更新版本 以及 Yarn 包管理器。克隆仓库后,在 tabby 根目录执行安装:

macOS 与 Windows:

yarn

Linux(以 Debian/Ubuntu 为例,需先安装 Electron 运行所需的系统库与编译工具):

# Linux (Debian/Ubuntu here as an example)
sudo apt install libfontconfig-dev libsecret-1-1-dev libarchive-tools libnss3 libatk1.0-0 libatk-bridge2.0-0 libgdk-pixbuf2.0-0 libgtk-3-0 libgbm1 cmake
yarn

HACKING.md 中有一个针对 fork 场景的重要提示:如果你 fork 了该仓库,在安装 node modules 之前可能需要先从上游仓库拉取 tags,否则依赖安装可能失败:

git pull --tags upstream master

从源码结构看,yarn 安装完成后会自动触发根 package.json 中的 postinstall 钩子:

"postinstall": "patch-package && node ./scripts/install-deps.mjs && node ./scripts/build-native.mjs"

即依次执行:用 patch-package 应用 patches/ 目录下的补丁、运行 scripts/install-deps.mjs 处理各插件子包的依赖、再运行 scripts/build-native.mjs 编译 node-ptyserialport 等原生模块。这也是 Linux 上必须先装好 cmake 和 C++ 工具链的原因。

开发模式构建与启动

完成依赖安装后,构建 Tabby:

yarn run build

该命令实际执行 npm run build:typings && node scripts/build-modules.mjs(见 package.jsonscripts.build),先编译各插件的 typings,再由 scripts/build-modules.mjs 构建所有插件模块。

然后启动开发模式:

yarn start

start 脚本定义为 cross-env TABBY_DEV=1 electron app -d --inspectpackage.json),有两个值得注意的点:

  1. TABBY_DEV=1 环境变量决定了运行模式。从 app/src/plugins.ts 可以看到,开发模式下内置插件的查找路径是源码检出目录本身(path.dirname(remote.app.getAppPath())),而不是生产模式下 resources/builtin-plugins 目录——这正是 HACKING.md 所说"开发模式下应用会从源码检出加载全部插件"的底层实现;
  2. -d --inspect 参数开启 Electron 调试与 Node inspector,方便断点调试主进程代码。

app/package.json"main": "dist/main.js" 说明主进程入口是 Webpack 打包产物,对应 app/webpack.config.main.mjsapp/lib/ 下的主进程 TypeScript 代码(index.tsplugins.ts 除外,见 app/tsconfig.main.json)打包为 dist/main.js;渲染进程入口则由 app/src/entry.ts 引导。

构建安装程序

要构建发行安装包,先完成上述"正常构建",然后执行:

node scripts/prepackage-plugins.mjs

node scripts/build-windows.mjs
# 或
node scripts/build-linux.mjs
# 或
node scripts/build-macos.mjs

构建产物输出在 dist 文件夹中。scripts/prepackage-plugins.mjs 负责预先打包各内置插件,而三个平台脚本对应 scripts/build-windows.mjsscripts/build-linux.mjsscripts/build-macos.mjs,底层使用 electron-builder,其目标平台、产物格式等参数由根目录的 electron-builder.yml 配置。

项目结构与插件结构

HACKING.md 给出的项目布局如下(注释来自原文档):

tabby
├─ app                                  # Electron app, just the bare essentials
|  ├─ src                               # Electron renderer code
|  └─ main.js                           # Electron main entry point
├─ build
├─ clink                                # Clink distribution, for Windows
├─ scripts                              # Maintenance scripts
├─ tabby-community-color-schemes     # Plugin that provides color schemes
├─ tabby-core                        # Plugin that provides base UI and tab management
├─ tabby-electron                    # Plugin that provides Electron-specific functions
├─ tabby-local                       # Plugin that provides local shells and profiles
├─ tabby-plugin-manager              # Plugin that installs other plugins
├─ tabby-settings                    # Plugin that provides the settings tab
├─ tabby-terminal                    # Plugin that provides terminal tabs
└─ tabby-web                         # Plugin that provides web-specific functions

对照当前仓库,文档中的布局依然是骨架,只是插件数量有所增长:除文档所列之外,当前仓库还包含 tabby-ssh/(SSH 会话)、tabby-serial/(串口)、tabby-telnet/tabby-linkifier/(链接识别)、tabby-web-demo/ 等插件;Windows 的 Clink 发行版对应 extras/clink/ 目录,本地化翻译文件位于 locale/

单插件的标准目录结构(文档原文定义):

tabby-pluginname
├─ src                                  # Typescript code
|  ├─ components                        # Angular components
|  |  ├─ foo.component.ts               # Code
|  |  ├─ foo.component.scss             # Styles
|  └─ foo.component.pug                # Template
|  ├─ services                          # Angular services
|  |  └─ foo.service.ts
|  ├─ api.ts                            # Publicly exported API
|  └─ index.ts                          # Module entry point
├─ package.json
├─ tsconfig.json
└─ webpack.config.js

tabby-local/ 为例可以核对这一定义:其 src/components/ 下有 *.component.ts*.component.scss*.component.pug 三件套,src/services/terminal.service.ts 提供 Angular service,src/api.ts 导出公共 API,src/index.ts 是模块入口,根目录配套 package.jsontsconfig.jsonwebpack.config.mjs(当前仓库中 webpack 配置已统一为 .mjs 后缀)。

插件加载机制:三个来源与关键字过滤

Tabby 会从以下位置加载插件(HACKING.md 原文归纳):

  1. 开发模式下从源码检出目录加载(依赖 TABBY_DEV 环境变量,见前文 app/src/plugins.ts 的路径选择逻辑);
  2. 始终从用户插件目录加载(在应用内 Settings > Plugins 页面点击 Open Plugins Directory 可打开该目录);
  3. 始终TABBY_PLUGINS 环境变量指定的目录加载。

app/src/plugins.tsinitModuleLookup() 展示了具体实现:把用户插件目录的 node_modules、应用自身路径,以及 TABBY_PLUGINS 中用 : 分隔的多个路径全部压入 Node 模块查找路径(NODE_PATH),从而让渲染进程可以通过 require 找到插件包。

只有 package.jsonkeywords 中包含 tabby-plugin 的模块才会被加载。 源码中该判断位于 app/src/plugins.ts

if (!info.keywords || !(info.keywords.includes('terminus-plugin') || info.keywords.includes('terminus-builtin-plugin') || info.keywords.includes('tabby-plugin') || info.keywords.includes('tabby-builtin-plugin'))) {
    return null
}

可以推断 terminus-* 关键字是为兼容 Tabby 更名前(Terminus)时代发布的旧插件而保留的。此外,插件目录名需以 tabby- 或旧前缀 terminus- 开头(app/src/plugins.ts 中的 PLUGIN_PREFIX / LEGACY_PLUGIN_PREFIX),同名插件按"内置优先、非旧版覆盖旧版"的规则去重(app/src/plugins.ts),并支持通过 config.pluginBlacklist 配置项禁用特定插件(app/src/entry.ts)。

如果你正在自己的插件目录中开发插件,推荐按文档给出的方式启动:

TABBY_PLUGINS=$(pwd) tabby --debug

在 Linux/macOS 上,也可以用源码模式等效运行:TABBY_PLUGINS=$(pwd) yarn startyarn start 已自动附加 TABBY_DEV=1 与调试参数)。

插件模块契约:NgModule 默认导出

一个插件只应提供一个默认导出,它应是一个 NgModule 类(在适用场景下可以是 NgModuleWithDependencies)。该模块会作为依赖注入到应用根模块中:

import { NgModule } from '@angular/core'

@NgModule()
export default class MyModule {
  constructor () {
    console.log('Angular engaged, cap\'n.')
  }
}

加载链路在源码中清晰可查:app/src/entry.ts 在收到主进程的 start IPC 消息后调用 findPlugins()loadPlugins(),随后以插件模块列表构建根模块并 bootstrapModuleapp/src/plugins.ts 中的这一行解释了 NgModuleWithDependencies 的含义——若默认导出带有 forRoot() 静态方法,则会先调用它:

const pluginModule = packageModule.default.forRoot ? packageModule.default.forRoot() : packageModule.default

app/src/app.module.tsgetRootModule() 把所有插件模块加入 imports,并要求至少一个插件提供 bootstrap 组件(否则抛出 "Did not find any bootstrap components" 错误);渲染进程若 bootstrap 失败会自动降级为 safe mode,只加载 isBuiltin 的内置插件(app/src/entry.ts),这对排查第三方插件导致的崩溃很有用。

通过 Provider 扩展功能

插件通过导出单个或多实例(multi: true)provider 来提供功能。以下是 HACKING.md 给出的完整示例——向工具栏添加一个按钮,通过继承 tabby-coreToolbarButtonProvider 实现:

import { NgModule, Injectable } from '@angular/core'
import { ToolbarButtonProvider, ToolbarButton } from 'tabby-core'

@Injectable()
export class MyButtonProvider extends ToolbarButtonProvider {
    provide (): ToolbarButton[] {
        return [{
            icon: 'star',
            title: 'Foobar',
            weight: 10,
            click: () => {
                alert('Woohoo!')
            }
        }]
    }
}

@NgModule({
    providers: [
        { provide: ToolbarButtonProvider, useClass: MyButtonProvider, multi: true },
    ],
})
export default class MyModule { }

可用的扩展点(provider 基类)在各插件的 API 模块中定义。HACKING.md 指引查看 tabby-core/src/api.tstabby-settings/src/api.tstabby-local/src/api.tstabby-terminal/src/api.ts;在当前仓库中,tabby-core 的扩展点已组织为 tabby-core/src/api/ 目录,包含 profileProvider.ts(自定义 Profile)、toolbarButtonProvider.ts(工具栏按钮)、hotkeyProvider.ts(快捷键)、menu.tscommands.tsconfigProvider.tstabContextMenuProvider.tstheme.ts 等;tabby-settingstabby-local 对应 tabby-settings/src/api.tstabby-local/src/api.ts,终端相关扩展点在 tabby-terminal/src/api/。编写插件前通读这些文件,是找到正确 provider 基类最快的方式。

发布插件到 NPM 与插件管理器

最后一步:将插件发布到 NPM,并在 package.jsonkeywords 中加入 tabby-plugin 关键字,该插件就会出现在 Tabby 的插件管理器(Plugin Manager)中供用户安装。

从源码看,插件管理器的安装/卸载实现在 app/lib/pluginManager.ts:它在进程内直接使用 npm 官方的 Arborist 安装引擎(new Arborist({ path: targetPath, ... }).reify({ add: [name@version] }))解析并安装完整依赖树,避免了打包 18 MB 的 npm CLI;tabby-plugin-manager/ 插件则负责管理器的前端界面(tabby-plugin-manager/src/services/pluginManager.service.ts)。

小结

按 HACKING.md 的流程走一遍:安装 Node 15+ 与 Yarn → yarn(Linux 先装系统库)→ yarn run buildyarn start 即可进入开发模式;出包则执行 scripts/prepackage-plugins.mjs 加对应平台的 build-*.mjs 脚本,产物位于 dist。开发插件时,牢记三个关键点:目录名以 tabby- 开头且 package.jsontabby-plugin 关键字、唯一默认导出为 NgModule(可带 forRoot)、用 multi: true provider 挂载到 tabby-core 等插件定义的扩展点上,再用 TABBY_PLUGINS=$(pwd) tabby --debug 完成本地联调。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
981
502
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384