remobi-setup

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

remobi-setup

remobi-setup

Interactive onboarding skill for remobi — monitor and control tmux from your phone.
This skill walks the user through the full setup journey in one conversation. Each phase builds on the last; skip phases the user doesn't need.
remobi打造的交互式入门技能——可通过手机监控和控制tmux。
该技能会在一次对话中引导用户完成完整的设置流程,每个阶段都基于上一阶段推进;用户可跳过不需要的阶段。

Workflow

工作流程

Phase 1: Assess environment

阶段1:环境评估

Check what's installed and help fill gaps.
bash
node --version          # need >= 22
which ttyd              # must be on PATH
tmux -V                 # target multiplexer
which remobi            # npm install -g remobi
If anything is missing, help install it:
  • Node: suggest mise, nvm, or direct install
  • ttyd:
    brew install ttyd
    (macOS), distro package or source build (Linux) — see ttyd installation
  • tmux:
    brew install tmux
    or distro package
  • remobi:
    npm install -g remobi
Move on once all four are present.
检查已安装的组件并帮助补充缺失项。
bash
node --version          # 需要 >= 22
which ttyd              # 必须在PATH中
tmux -V                 # 目标多路复用器
which remobi            # npm install -g remobi
若有组件缺失,帮助用户安装:
  • Node:推荐使用mise、nvm或直接安装
  • ttyd:macOS使用
    brew install ttyd
    ;Linux使用发行版包或源码构建——详见ttyd安装文档
  • tmux:使用
    brew install tmux
    或发行版包
  • remobi
    npm install -g remobi
所有四个组件都安装完成后再进入下一阶段。

Phase 2: Inspect tmux setup

阶段2:检查tmux设置

Gather the user's tmux configuration to inform config generation.
bash
tmux show-options -g prefix                    # prefix key
tmux list-keys                                 # all bindings
tmux show-options -g mouse                     # mouse mode
tmux show-options -g status-left               # status bar
tmux list-keys | grep display-popup            # popup bindings
If tmux isn't running, fall back to reading the config file directly:
bash
cat ~/.config/tmux/tmux.conf 2>/dev/null || cat ~/.tmux.conf 2>/dev/null
Note down:
  • Prefix key and byte (Ctrl-B =
    \x02
    , Ctrl-A =
    \x01
    , etc.)
  • Custom bindings worth surfacing as buttons (especially popup bindings for lazygit, yazi, neovim, fzf pickers, gh-dash, scratch shells)
  • Whether mouse mode is on
  • Status bar complexity (affects mobile width recommendations)
  • Plugin manager (tpm, etc.)
If the user has no tmux config at all, offer to help set up a basic one before continuing.
收集用户的tmux配置信息,为生成remobi配置提供依据。
bash
tmux show-options -g prefix                    # 前缀键
tmux list-keys                                 # 所有绑定
tmux show-options -g mouse                     # 鼠标模式
tmux show-options -g status-left               # 状态栏
tmux list-keys | grep display-popup            # 弹窗绑定
若tmux未运行,则直接读取配置文件:
bash
cat ~/.config/tmux/tmux.conf 2>/dev/null || cat ~/.tmux.conf 2>/dev/null
记录以下信息:
  • 前缀键及其字节码(Ctrl-B =
    \x02
    ,Ctrl-A =
    \x01
    等)
  • 值得设置为手机端按钮的自定义绑定(尤其是针对lazygit、yazi、neovim、fzf选择器、gh-dash、临时shell的弹窗绑定)
  • 鼠标模式是否开启
  • 状态栏复杂度(影响移动端宽度建议)
  • 插件管理器(如tpm)
若用户完全没有tmux配置,可先帮助其设置基础配置,再继续后续流程。

Phase 3: Interview the user

阶段3:用户访谈

Ask questions one at a time — don't dump a list. Adapt based on what you learned in phase 2.
  1. What do you primarily use tmux for? (coding agents, dev workflow, server monitoring, all of the above)
  2. Do you use popup bindings for tools? Which ones? (lazygit, yazi, neovim, scratch shell, gh-dash, session picker)
  3. Do you want touch scrolling? What strategy? (
    wheel
    for mouse-event scrolling,
    keys
    for PageUp/PageDown paging)
  4. Auto-zoom on mobile? When you open remobi on your phone, should the current pane zoom to full screen automatically?
  5. Floating zoom button? A persistent button overlaid on the terminal for one-tap zoom toggle
  6. Custom theme or Catppuccin Mocha? (Catppuccin Mocha is the default and looks great — only ask if the user's tmux theme is clearly different)
  7. Font preference? (default: JetBrainsMono NFM)
  8. Any other tmux bindings you want on your phone? (This catches anything the inspection missed)
Skip questions where you already know the answer from phase 2. Summarise what you've gathered before moving to config generation.
逐个提问——不要一次性列出所有问题。根据阶段2获取的信息调整问题。
  1. 你主要用tmux做什么?(编码代理、开发工作流、服务器监控,或以上所有)
  2. **你是否为工具设置了弹窗绑定?**具体是哪些?(lazygit、yazi、neovim、临时shell、gh-dash、会话选择器)
  3. **你需要触摸滚动吗?**采用哪种策略?(
    wheel
    表示鼠标事件滚动,
    keys
    表示PageUp/PageDown翻页)
  4. **移动端自动缩放?**在手机上打开remobi时,当前窗格是否应自动全屏缩放?
  5. **悬浮缩放按钮?**在终端上添加一个持久显示的按钮,一键切换缩放状态
  6. 自定义主题还是Catppuccin Mocha?(Catppuccin Mocha是默认主题,视觉效果出色——仅当用户的tmux主题明显不同时才询问)
  7. 字体偏好?(默认:JetBrainsMono NFM)
  8. 还有其他想要在手机端使用的tmux绑定吗?(这可以捕捉到阶段2检查中遗漏的内容)
若从阶段2已能得到答案,可跳过对应问题。在进入配置生成阶段前,总结已收集到的信息。

Phase 4: Generate
remobi.config.ts

阶段4:生成
remobi.config.ts

Write the config using
defineConfig()
. Only include keys that differ from defaults — omit everything else.
typescript
import { defineConfig } from 'remobi'

export default defineConfig({
  // Only non-default overrides here
})
After writing, validate:
bash
remobi build --dry-run
A zero exit with "Dry run: build" output means valid. Fix any errors and re-validate until clean.
See Config reference below for the full schema, allowed keys, action types, and escape codes.
使用
defineConfig()
编写配置文件。仅包含与默认值不同的配置项——其余项可省略。
typescript
import { defineConfig } from 'remobi'

export default defineConfig({
  // 仅在此处填写非默认的配置项
})
编写完成后进行验证:
bash
remobi build --dry-run
若退出码为0且输出“Dry run: build”,则配置有效。修复所有错误后重新验证,直至验证通过。
完整的配置 schema、允许的配置键、操作类型及转义码,请查看下方的配置参考

Phase 5: Suggest tmux mobile optimisations

阶段5:提供tmux移动端优化建议

Ask: "Would you like suggestions for making your tmux config more mobile-friendly?"
If yes, run the checks below. If tmux isn't running, read the config file directly. For full context and examples, read
references/mobile-tmux.md
and
references/mobile-panes.md
.
CheckCommandGood signSuggestion if missing
Responsive status-left
tmux show -g status-left
Contains
#{client_width}
Add width breakpoints to strip content on narrow terminals
Responsive status-right
tmux show -g status-right
Contains
#{client_width}
or calls a script
Progressive content stripping
Popup sizing
tmux list-keys | grep display-popup
Uses
%
dimensions
Replace fixed char sizes with
95%
/
100%
Zoom indicator
tmux show -g status-left
Contains
window_zoomed_flag
Add
#{?window_zoomed_flag,[Z] ,}
Mouse mode
tmux show -g mouse
on
set -g mouse on
Window renumbering
tmux show -g renumber-windows
on
set -g renumber-windows on
Zoom-aware navigation
tmux list-keys | grep 'select-pane.*resize-pane'
PresentAdd zoom-aware
n
/
p
bindings (see
references/mobile-panes.md
)
For each missing item, offer a concrete snippet the user can paste into
tmux.conf
. Suggest snippets only — never modify
tmux.conf
without explicit permission.
询问用户:“是否需要让tmux配置更适配移动端的建议?”
若用户同意,执行以下检查。若tmux未运行,则直接读取配置文件。完整的背景信息和示例,请查看
references/mobile-tmux.md
references/mobile-panes.md
检查项命令合格标准缺失时的建议
响应式左侧状态栏
tmux show -g status-left
包含
#{client_width}
添加宽度断点,在窄终端上精简内容
响应式右侧状态栏
tmux show -g status-right
包含
#{client_width}
或调用脚本
逐步精简内容
弹窗尺寸
tmux list-keys | grep display-popup
使用
%
作为单位
将固定字符尺寸替换为
95%
/
100%
缩放指示器
tmux show -g status-left
包含
window_zoomed_flag
添加
#{?window_zoomed_flag,[Z] ,}
鼠标模式
tmux show -g mouse
on
set -g mouse on
窗口重新编号
tmux show -g renumber-windows
on
set -g renumber-windows on
缩放感知导航
tmux list-keys | grep 'select-pane.*resize-pane'
已配置添加支持缩放感知的
n
/
p
绑定(详见
references/mobile-panes.md
对于每一项缺失的配置,提供可直接粘贴到
tmux.conf
的代码片段。仅提供代码片段——未经明确许可,切勿修改用户的
tmux.conf

Phase 6: Deployment guidance

阶段6:部署指导

Ask: "How do you want to access remobi from your phone?"
remobi is a remote-control surface for your terminal — never expose it to the public internet. All deployment options below keep access private.
Common options:
  • Tailscale Serve (recommended) — HTTPS over your private tailnet. Read
    references/tailscale-serve.md
    for the full guide.
  • Cloudflare Tunnel + Access — private tunnel with Cloudflare Access policies controlling who can connect (e.g. restrict by email, IdP group, device posture). Do not use unauthenticated quick tunnels.
  • Local network only
    remobi serve
    on localhost behind your own VPN or private network.
For macOS users, mention
--no-sleep
and point to
references/keep-awake.md
for persistent options.
For users who want manual ttyd control, point to
references/ttyd-flags.md
.
询问用户:“你希望如何从手机访问remobi?”
remobi是终端的远程控制界面——切勿将其暴露在公网中。以下所有部署选项均保证访问的私密性。
常见选项:
  • Tailscale Serve(推荐)——通过你的私有tailnet实现HTTPS访问。完整指南请查看
    references/tailscale-serve.md
  • Cloudflare Tunnel + Access——私有隧道,通过Cloudflare Access策略控制访问权限(例如按邮箱、身份提供商组、设备状态限制)。请勿使用未认证的快速隧道。
  • 仅本地网络访问——在本地主机上运行
    remobi serve
    ,并通过自己的VPN或私有网络访问。
针对macOS用户,提及
--no-sleep
选项,并指向
references/keep-awake.md
查看持久运行的方案。
对于希望手动控制ttyd的用户,指向
references/ttyd-flags.md

Phase 7: Summarise

阶段7:总结

Tell the user:
  1. What was configured and why (prefix byte, custom bindings, gestures, theme)
  2. How to start:
    remobi serve
  3. How to access from their phone (URL from deployment choice)
  4. PWA install: on mobile, tap "Add to Home Screen" for a standalone app experience

告知用户:
  1. 已配置的内容及原因(前缀字节码、自定义绑定、手势、主题)
  2. 启动方法:
    remobi serve
  3. 手机端访问方式(根据部署选择提供对应URL)
  4. PWA安装:在移动端点击“添加到主屏幕”,获得独立应用体验

Config reference

配置参考

Allowed root keys

允许的根配置键

Exactly these — validation rejects anything else:
name  theme  font  toolbar  drawer  gestures  mobile  floatingButtons  pwa  reconnect
仅允许以下配置键——验证器会拒绝其他所有键:
name  theme  font  toolbar  drawer  gestures  mobile  floatingButtons  pwa  reconnect

ButtonAction union

ButtonAction 联合类型

type
Required fieldsNotes
send
data: string
Optional
keyLabel?: string
for help overlay
ctrl-modifier
(none)Opens Ctrl+key combo UI
paste
(none)Paste from clipboard
combo-picker
(none)Opens Ctrl/Alt + key modal
drawer-toggle
(none)Opens/closes command drawer
Non-
send
actions must NOT have
data
or
keyLabel
— the validator rejects them.
type
必填字段说明
send
data: string
可选
keyLabel?: string
用于帮助浮层
ctrl-modifier
打开Ctrl+键组合界面
paste
从剪贴板粘贴
combo-picker
打开Ctrl/Alt + 键选择模态框
drawer-toggle
打开/关闭命令抽屉
send
类型的操作不得包含
data
keyLabel
——否则会被验证器拒绝。

ControlButton shape

ControlButton 结构

Every button in toolbar rows, drawer, and floatingButtons uses this schema:
typescript
{
  id: string           // unique within its array
  label: string        // text shown on the button
  description: string  // shown in help overlay — keep user-facing and clear
  action: ButtonAction
}
工具栏、抽屉和悬浮按钮中的所有按钮均使用以下结构:
typescript
{
  id: string           // 在所属数组中唯一
  label: string        // 按钮上显示的文字
  description: string  // 在帮助浮层中显示——需清晰易懂,面向用户
  action: ButtonAction
}

Button array forms (
toolbar.row1
,
toolbar.row2
,
drawer.buttons
)

按钮数组形式(
toolbar.row1
,
toolbar.row2
,
drawer.buttons

Two forms — pick the least invasive:
typescript
// 1. Replace entirely (plain array)
toolbar: { row1: [{ id, label, description, action }, ...] }

// 2. Transform (function receives defaults, returns new array)
toolbar: { row2: (defaults) => defaults.filter(b => b.id !== 'q') }

// Function form covers all operations via standard JS:
// - Append:  (d) => [...d, newBtn]
// - Prepend: (d) => [newBtn, ...d]
// - Remove:  (d) => d.filter(b => b.id !== 'q')
// - Replace: (d) => d.map(b => b.id === 'tmux-prefix' ? newBtn : b)
// - Insert:  (d) => { const i = d.findIndex(b => b.id === 'tab'); return [...d.slice(0,i), newBtn, ...d.slice(i)] }
两种形式——选择侵入性最低的一种:
typescript
// 1. 完全替换(普通数组)
toolbar: { row1: [{ id, label, description, action }, ...] }

// 2. 转换(函数接收默认值,返回新数组)
toolbar: { row2: (defaults) => defaults.filter(b => b.id !== 'q') }

// 函数形式可通过标准JS实现所有操作:
// - 追加:  (d) => [...d, newBtn]
// - 前置:  (d) => [newBtn, ...d]
// - 删除:  (d) => d.filter(b => b.id !== 'q')
// - 替换:  (d) => d.map(b => b.id === 'tmux-prefix' ? newBtn : b)
// - 插入:  (d) => { const i = d.findIndex(b => b.id === 'tab'); return [...d.slice(0,i), newBtn, ...d.slice(i)] }

Floating buttons

悬浮按钮

Must use the grouped shape — a flat
ControlButton[]
is rejected:
typescript
floatingButtons: [
  {
    position: 'top-left',           // required
    direction: 'row',               // optional: 'row' | 'column' (default 'row')
    buttons: [{ id, label, description, action }],
  },
]
Valid positions:
top-left | top-right | top-centre | bottom-left | bottom-right | bottom-centre | centre-left | centre-right
必须使用分组结构——扁平的
ControlButton[]
会被拒绝:
typescript
floatingButtons: [
  {
    position: 'top-left',           // 必填
    direction: 'row',               // 可选: 'row' | 'column'(默认'row')
    buttons: [{ id, label, description, action }],
  },
]
有效的位置值:
top-left | top-right | top-centre | bottom-left | bottom-right | bottom-centre | centre-left | centre-right

Escape-code cheat sheet

转义码速查表

Use these in
action.data
and gesture
left
/
right
fields:
KeyEscape sequenceNotes
Ctrl-B (prefix)
\x02
Default tmux prefix
Ctrl-A (prefix)
\x01
screen/byobu/custom prefix
Ctrl-C
\x03
Interrupt
Ctrl-D
\x04
EOF / exit shell
Escape
\x1b
Tab
\t
Shift+Tab
\x1b[Z
Enter
\r
Alt+Enter
\x1b\r
Backspace
\x7f
DEL character
Up arrow
\x1b[A
Down arrow
\x1b[B
Right arrow
\x1b[C
Left arrow
\x1b[D
Page Up
\x1b[5~
Page Down
\x1b[6~
Space
' '
literal space
可在
action.data
和手势的
left
/
right
字段中使用以下转义码:
按键转义序列说明
Ctrl-B(前缀)
\x02
tmux默认前缀
Ctrl-A(前缀)
\x01
screen/byobu/自定义前缀
Ctrl-C
\x03
中断
Ctrl-D
\x04
EOF / 退出shell
Escape
\x1b
Tab
\t
Shift+Tab
\x1b[Z
Enter
\r
Alt+Enter
\x1b\r
Backspace
\x7f
DEL字符
上箭头
\x1b[A
下箭头
\x1b[B
右箭头
\x1b[C
左箭头
\x1b[D
Page Up
\x1b[5~
Page Down
\x1b[6~
空格
' '
字面空格

Composing tmux key sequences

组合tmux按键序列

tmux bindings are
prefix
+
key
. Concatenate the bytes:
Ctrl-B + c  →  '\x02c'   (new window)
Ctrl-B + n  →  '\x02n'   (next window)
Ctrl-B + p  →  '\x02p'   (previous window)
Ctrl-B + z  →  '\x02z'   (zoom pane)
Ctrl-B + %  →  '\x02%'   (split vertical — stock tmux)
Ctrl-B + "  →  '\x02"'   (split horizontal — stock tmux)
Ctrl-B + [  →  '\x02['   (copy mode)
Ctrl-B + d  →  '\x02d'   (detach)
For a custom prefix (e.g. Ctrl-A): replace
\x02
with
\x01
.
tmux绑定为
前缀
+
按键
。将字节码拼接即可:
Ctrl-B + c  →  '\x02c'   (新建窗口)
Ctrl-B + n  →  '\x02n'   (下一个窗口)
Ctrl-B + p  →  '\x02p'   (上一个窗口)
Ctrl-B + z  →  '\x02z'   (缩放窗格)
Ctrl-B + %  →  '\x02%'   (垂直拆分——默认tmux)
Ctrl-B + "  →  '\x02"'   (水平拆分——默认tmux)
Ctrl-B + [  →  '\x02['   (复制模式)
Ctrl-B + d  →  '\x02d'   (断开连接)
对于自定义前缀(如Ctrl-A):将
\x02
替换为
\x01
即可。

Example configs

配置示例

Minimal — default Ctrl-B prefix, custom name only

极简配置——默认Ctrl-B前缀,仅自定义名称

typescript
import { defineConfig } from 'remobi'

export default defineConfig({
  name: 'dev',
})
typescript
import { defineConfig } from 'remobi'

export default defineConfig({
  name: 'dev',
})

Custom prefix — Ctrl-A (screen/byobu style)

自定义前缀——Ctrl-A(screen/byobu风格)

Replace the default
tmux-prefix
button and update swipe gestures:
typescript
import { defineConfig } from 'remobi'

export default defineConfig({
  name: 'dev',
  toolbar: {
    row1: (defaults) => defaults.map(b =>
      b.id === 'tmux-prefix'
        ? { ...b, description: 'Send tmux prefix key (Ctrl-A)', action: { type: 'send', data: '\x01' } }
        : b
    ),
  },
  gestures: {
    swipe: {
      left: '\x01n',
      right: '\x01p',
      leftLabel: 'Next tmux window',
      rightLabel: 'Previous tmux window',
    },
  },
  drawer: {
    buttons: (defaults) => defaults.map(b => {
      // Remap tmux-prefixed buttons from Ctrl-B (\x02) to Ctrl-A (\x01)
      if (b.action.type === 'send' && b.action.data.startsWith('\x02')) {
        return { ...b, action: { ...b.action, data: '\x01' + b.action.data.slice(1) } }
      }
      return b
    }),
  },
})
替换默认的
tmux-prefix
按钮并更新滑动手势:
typescript
import { defineConfig } from 'remobi'

export default defineConfig({
  name: 'dev',
  toolbar: {
    row1: (defaults) => defaults.map(b =>
      b.id === 'tmux-prefix'
        ? { ...b, description: '发送tmux前缀键(Ctrl-A)', action: { type: 'send', data: '\x01' } }
        : b
    ),
  },
  gestures: {
    swipe: {
      left: '\x01n',
      right: '\x01p',
      leftLabel: '下一个tmux窗口',
      rightLabel: '上一个tmux窗口',
    },
  },
  drawer: {
    buttons: (defaults) => defaults.map(b => {
      // 将tmux前缀绑定从Ctrl-B(\x02)重新映射为Ctrl-A(\x01)
      if (b.action.type === 'send' && b.action.data.startsWith('\x02')) {
        return { ...b, action: { ...b.action, data: '\x01' + b.action.data.slice(1) } }
      }
      return b
    }),
  },
})

Floating buttons + mobile auto-zoom

悬浮按钮 + 移动端自动缩放

typescript
import { defineConfig } from 'remobi'

export default defineConfig({
  mobile: {
    initData: '\x02z',    // zoom focused pane on mobile load
    widthThreshold: 768,
  },
  floatingButtons: [
    {
      position: 'top-left',
      buttons: [
        {
          id: 'zoom',
          label: 'Zoom',
          description: 'Toggle pane zoom',
          action: { type: 'send', data: '\x02z' },
        },
      ],
    },
  ],
})
typescript
import { defineConfig } from 'remobi'

export default defineConfig({
  mobile: {
    initData: '\x02z',    // 移动端加载时缩放当前聚焦的窗格
    widthThreshold: 768,
  },
  floatingButtons: [
    {
      position: 'top-left',
      buttons: [
        {
          id: 'zoom',
          label: '缩放',
          description: '切换窗格缩放状态',
          action: { type: 'send', data: '\x02z' },
        },
      ],
    },
  ],
})

Guardrails

注意事项

  • Never invent root keys. The validator rejects unknown keys with a path-based error.
  • Use
    drawer.buttons
    , never
    drawer.commands
    — the latter was renamed and no longer works.
  • send
    actions require
    data
    — omitting it fails validation.
  • Non-
    send
    actions must not have
    data
    or
    keyLabel
    — validator rejects them.
  • floatingButtons
    is an array of groups
    — wrap buttons in
    { position, buttons }
    .
  • toolbar
    has
    row1
    and
    row2
    — there is no
    row3
    or flat
    buttons
    key on toolbar.
  • mobile.initData
    is
    string | null
    — set to
    null
    to disable, not
    false
    or
    ''
    .
  • reconnect
    has only
    enabled: boolean
    — defaults to
    true
    . Set
    { enabled: false }
    to disable.
  • 切勿自行添加根配置键。验证器会拒绝未知键,并返回基于路径的错误。
  • 使用
    drawer.buttons
    ,切勿使用
    drawer.commands
    ——后者已重命名,不再生效。
  • send
    类型操作必须包含
    data
    ——缺失会导致验证失败。
  • send
    类型操作不得包含
    data
    keyLabel
    ——验证器会拒绝此类配置。
  • floatingButtons
    是分组数组
    ——需将按钮包裹在
    { position, buttons }
    中。
  • toolbar
    包含
    row1
    row2
    ——不存在
    row3
    或直接在toolbar下的
    buttons
    键。
  • **
    mobile.initData
    **的类型为
    string | null
    ——设置为
    null
    以禁用,切勿使用
    false
    ''
  • **
    reconnect
    **仅包含
    enabled: boolean
    ——默认值为
    true
    。设置
    { enabled: false }
    可禁用该功能。

Validation

验证

bash
remobi build --dry-run          # validates config, prints plan, exits without building
remobi build --dry-run -c ./remobi.config.ts   # explicit path
A zero exit with "Dry run: build" output means the config is valid. Any error output means fix the reported paths before proceeding.
bash
remobi build --dry-run          # 验证配置,输出计划,不执行实际构建
remobi build --dry-run -c ./remobi.config.ts   # 指定配置文件路径
若退出码为0且输出“Dry run: build”,则配置有效。若有错误输出,需修复报告的路径问题后再继续。

Common validation errors

常见验证错误

ErrorCauseFix
config.<unknown-key>
Invented or legacy root keyRemove it; only allowed root keys are valid
config.drawer.commands
Old key nameRename to
drawer.buttons
config.toolbar.buttons
Wrong toolbar shapeUse
toolbar.row1
and/or
toolbar.row2
action.type: expected 'send' | ...
Wrong type stringUse exact literal from ButtonAction union
action.data: expected string, received undefined
send
action missing
data
Add
data: '\x...'
action.data: expected undefined
data
on non-
send
action
Remove
data
from non-
send
actions
floatingButtons[0]: expected object
Flat
ControlButton[]
Wrap in group:
{ position: 'top-left', buttons: [...] }
mobile.initData: expected string or null
false
or
0
passed
Use
null
to disable, or a string to send
错误信息原因修复方法
config.<unknown-key>
自行添加或使用了旧版根配置键删除该键;仅使用允许的根配置键
config.drawer.commands
使用了旧版键名重命名为
drawer.buttons
config.toolbar.buttons
toolbar结构错误使用
toolbar.row1
和/或
toolbar.row2
action.type: expected 'send' | ...
操作类型字符串错误使用ButtonAction联合类型中的精确字面量
action.data: expected string, received undefined
send
类型操作缺失
data
添加
data: '\x...'
action.data: expected undefined
send
类型操作包含
data
从非
send
类型操作中移除
data
floatingButtons[0]: expected object
使用了扁平的
ControlButton[]
将其包裹为分组:
{ position: 'top-left', buttons: [...] }
mobile.initData: expected string or null
传入了
false
0
使用
null
禁用,或传入字符串用于发送