cmux-custom-sidebar
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chinesecmux Custom Sidebar
cmux 自定义侧边栏
cmux renders custom sidebars from a small SwiftUI-style file at runtime: no Xcode, no build step, no signing. The file hot-reloads on save, binds to live cmux state (workspaces, tabs, git, PRs, clock), and can run real cmux commands on tap.
The person asking is usually describing a result ("a sidebar that shows my workspaces and lets me jump between them"), not an implementation. Turn that into a clean, native-looking sidebar and make the engineering decisions for them. Do not ask them about SwiftUI, files, or syntax.
cmux 可在运行时通过一个小型SwiftUI风格的文件渲染自定义侧边栏:无需Xcode、无需构建步骤、无需签名。文件保存后会自动hot reload,可绑定到cmux的实时状态(工作区、标签页、git、PR、时钟),并且点击时可执行真实的cmux命令。
提出需求的用户通常描述的是期望结果(“显示我的工作区并允许我在它们之间跳转的侧边栏”),而非实现方式。你需要将其转化为简洁、原生风格的侧边栏,并替用户做出工程决策。不要询问用户关于SwiftUI、文件或语法的问题。
Full reference
完整参考
This skill is the workflow summary. The complete authoring contract (every supported view, modifier, language feature, and data field) is one command away; read it before writing a non-trivial sidebar:
bash
cmux docs sidebars
curl -fsSL https://raw.githubusercontent.com/manaflow-ai/cmux/main/docs/custom-sidebars.md本技能是工作流程总结。完整的创作规范(所有支持的视图、修饰器、语言特性和数据字段)只需一条命令即可获取;在编写非简单侧边栏前请先阅读:
bash
cmux docs sidebars
curl -fsSL https://raw.githubusercontent.com/manaflow-ai/cmux/main/docs/custom-sidebars.mdWorkflow
工作流程
- Enable the beta (once). Custom sidebars are behind Settings → Beta features → Custom sidebars (). If a written sidebar does not appear in the picker, this flag is the first thing to check.
customSidebars.beta.enabled - Write a named file. The name becomes the menu label; use short kebab-case:
The file is a single SwiftUI-style view expression (no
~/.config/cmux/sidebars/<name>.swift, nostruct, no imports). Avar bodyvariant exists for static layouts; prefer.jsonfor anything dynamic..swift - Validate and select it:
The user can also pick it manually: right-click the sidebar toggle button.bash
cmux sidebar validate <name> # parse/interpret check with real data shapes cmux sidebar select <name> # switch the sidebar to it - Iterate. Saving the file hot-reloads the sidebar in place (forces it). Look at the result, fix what looks off, and verify rows show real data and taps do the right thing before declaring it done.
cmux sidebar reload
- 启用测试版功能(仅需一次)。自定义侧边栏位于「设置 → Beta功能 → Custom sidebars」(配置项)。如果编写的侧边栏未出现在选择器中,首先检查此标记是否开启。
customSidebars.beta.enabled - 编写命名文件。文件名会成为菜单标签;建议使用简短的短横线命名法:
文件内容为单个SwiftUI风格的视图表达式(无需
~/.config/cmux/sidebars/<name>.swift、struct或导入语句)。还有var body变体用于静态布局;对于任何动态场景,优先选择.json格式。.swift - 验证并选择侧边栏:
用户也可以手动选择:右键点击侧边栏切换按钮。bash
cmux sidebar validate <name> # 使用真实数据结构进行解析/解释检查 cmux sidebar select <name> # 切换到该侧边栏 - 迭代优化。保存文件后侧边栏会原地hot reload(可强制重载)。查看结果,修复不合理的地方,在完成前验证行是否显示真实数据,以及点击是否执行正确操作。
cmux sidebar reload
Authoring rules
创作规则
- Default to live data. Bind to the context instead of hard-coding text so the sidebar stays correct on its own.
workspaces - Make it interactive by default. Rows that represent something openable should run the matching action on tap. A list that just displays text is rarely what they wanted.
cmux(...) - Prefer for workspace-like lists. It gives persisted drag-and-drop reordering for free.
Reorderable - Keep it native and uncluttered: a title, a divider, then the content.
- Cap long lists (, filter/sort before rendering). The sidebar re-evaluates about once a second; do not render hundreds of rows.
.prefix(20) - Stay inside the supported subset. Unsupported syntax is skipped gracefully (never crashes), but choose the closest supported approach rather than shipping a half-blank sidebar.
- 默认使用实时数据。绑定到上下文而非硬编码文本,这样侧边栏可自动保持内容准确。
workspaces - 默认设置为交互式。代表可打开内容的行,点击时应执行对应的操作。仅显示文本的列表几乎不是用户想要的。
cmux(...) - 对于类工作区列表,优先使用。它可免费提供持久化的拖放重排序功能。
Reorderable - 保持原生风格且简洁:一个标题、一条分隔线,然后是内容。
- 限制长列表(使用,渲染前先过滤/排序)。侧边栏大约每秒重新评估一次;不要渲染数百行内容。
.prefix(20) - 使用支持的子集。不支持的语法会被优雅跳过(绝不会崩溃),但请选择最接近的支持方案,而非交付半空白的侧边栏。
Quick start
快速开始
bash
cat > ~/.config/cmux/sidebars/mine.swift <<'SWIFT'
VStack(alignment: .leading, spacing: 8) {
Text("My sidebar").font(.title3).bold()
Text(clock.time).font(.caption).foregroundColor(.secondary)
Divider()
Reorderable(workspaces, move: "workspace.reorder") { w in
Button(action: { cmux("workspace.select", workspace_id: w.id) }) {
HStack {
Text(w.selected ? "●" : "○").foregroundColor(w.selected ? "#FF8800" : .secondary)
Text(w.title)
Spacer()
}.padding(4)
}
}
}
SWIFT
cmux sidebar validate mine && cmux sidebar select minebash
cat > ~/.config/cmux/sidebars/mine.swift <<'SWIFT'
VStack(alignment: .leading, spacing: 8) {
Text("My sidebar").font(.title3).bold()
Text(clock.time).font(.caption).foregroundColor(.secondary)
Divider()
Reorderable(workspaces, move: "workspace.reorder") { w in
Button(action: { cmux("workspace.select", workspace_id: w.id) }) {
HStack {
Text(w.selected ? "●" : "○").foregroundColor(w.selected ? "#FF8800" : .secondary)
Text(w.title)
Spacer()
}.padding(4)
}
}
}
SWIFT
cmux sidebar validate mine && cmux sidebar select mineLive data context (read-only, refreshes ~1s)
实时数据上下文(只读,约每秒刷新一次)
- : array with
workspaces,id,title,selected,pinned,index,directory+ports,portCount,unread+tabs; plus, when present:tabCount,description,color+branch,dirty/pr(prs),{number, label, url, status, stale, branch}(progress),{value, label},latestMessage,latestPrompt,latestAt(remote).{target, state, connected} - :
workspaces[i].tabs,id,title,focused; pluspinned,directory+branch,dirtywhen available.ports - :
clock.{time, hour, minute, second, weekday, epoch} - Scalars: ,
workspaceCount,selectedTitle,selectedId.unreadTotal
Optional fields are omitted when absent; guard with or .
if let b = w.branch { ... }w.pr != nil ? ... : ...- :数组,包含
workspaces、id、title、selected、pinned、index、directory+ports、portCount、unread+tabs;若存在则还包含:tabCount、description、color+branch、dirty/pr(prs)、{number, label, url, status, stale, branch}(progress)、{value, label}、latestMessage、latestPrompt、latestAt(remote)。{target, state, connected} - :包含
workspaces[i].tabs、id、title、focused;若可用则还包含pinned、directory+branch、dirty。ports - :
clock。{time, hour, minute, second, weekday, epoch} - 标量值:、
workspaceCount、selectedTitle、selectedId。unreadTotal
可选字段不存在时会被省略;可使用或进行判断。
if let b = w.branch { ... }w.pr != nil ? ... : ...Actions
操作
A button or body calls , dispatched through the same surface as the CLI. Common methods: (), (), ( + ). opens links. Discover the full command surface with .
.onTapGesturecmux("<method>", param: value)cmuxworkspace.selectworkspace_idsurface.focussurface_idworkspace.reorderworkspace_idindexopenURL("https://...")cmux docs api按钮或的主体可调用,通过与 CLI相同的界面分发。常用方法:(参数)、(参数)、(参数 + )。可打开链接。使用可发现完整的命令集。
.onTapGesturecmux("<method>", param: value)cmuxworkspace.selectworkspace_idsurface.focussurface_idworkspace.reorderworkspace_idindexopenURL("https://...")cmux docs apiSupported subset at a glance
支持子集概览
Containers: stacks (incl. lazy), , , , grids, , , (two resizable columns). Content: , , , (title and label form), , , , , , shapes, gradients via . Modifiers: full typography set, colors as hex strings or tokens, //layout, /// with arbitrary nested views, shadows/borders/opacity/effects, , , . Language: , user helpers, /, , ternary, string interpolation, arithmetic, array methods (////...), string and number formatting.
GroupListSectionViewThatFitsScrollViewHSplitViewTextLabelImage(systemName:)ButtonMenuProgressViewGaugeSpacerDivider.background.padding.frame.background.overlay.mask.contextMenu.onTapGesture.help.disabledletfuncforForEachif/elsefiltermapsortedprefixNot yet supported (write the natural Swift anyway; it degrades gracefully): and input controls (, , , ), custom / definitions, navigation (/), . Two-way editing does not work yet; taps that run do.
@StateTextFieldToggleSliderPickerstructViewsheetpopoverAsyncImagecmux(...)容器:栈(包括懒加载栈)、、、、网格、、、(两个可调整大小的列)。内容:、、、(标题和标签形式)、、、、、、形状、通过设置渐变。修饰器:完整的排版集、十六进制字符串或标记表示的颜色、//布局、///(支持任意嵌套视图)、阴影/边框/透明度/效果、、、。语言特性:、用户自定义辅助函数、/、、三元运算符、字符串插值、算术运算、数组方法(////...)、字符串和数字格式化。
GroupListSectionViewThatFitsScrollViewHSplitViewTextLabelImage(systemName:)ButtonMenuProgressViewGaugeSpacerDivider.background.padding.frame.background.overlay.mask.contextMenu.onTapGesture.help.disabledletfuncforForEachif/elsefiltermapsortedprefix暂不支持(仍可编写标准Swift代码;会优雅降级):和输入控件(、、、)、自定义/定义、导航(/)、。双向编辑目前不可用;但点击执行是可行的。
@StateTextFieldToggleSliderPickerstructViewsheetpopoverAsyncImagecmux(...)Troubleshooting
故障排除
- Sidebar missing from the right-click picker: the beta flag is off, or the file is not directly under .
~/.config/cmux/sidebars/ - Blank or partial render: run ; errors show inline in the sidebar with the failing location. A broken save keeps the last working render on screen, so re-save after fixing.
cmux sidebar validate <name> - Rows not tappable: wrap the row in or add
Button(action: { cmux(...) }) { ... }..onTapGesture { cmux(...) } - Reorder not persisting: use , not
Reorderable(data, move: "workspace.reorder")/List/.onMove..draggable
- 右键选择器中找不到侧边栏:测试版功能标记未开启,或文件未直接放在目录下。
~/.config/cmux/sidebars/ - 侧边栏空白或部分渲染:运行;错误会连同出错位置一起显示在侧边栏内。保存错误文件后,屏幕上会保留最后可正常渲染的版本,修复后重新保存即可。
cmux sidebar validate <name> - 行无法点击:将行包裹在中,或添加
Button(action: { cmux(...) }) { ... }。.onTapGesture { cmux(...) } - 重排序不持久:使用,而非
Reorderable(data, move: "workspace.reorder")/List/.onMove。.draggable