Lazydocker 西班牙语快捷键手册解读:按键绑定定义、视图分区与 cheatsheet 自动生成机制
docs/keybindings/Keybindings_es.md 是 Lazydocker 官方维护的西班牙语快捷键速查表(cheatsheet),覆盖了项目管理、容器、服务、镜像、卷、网络六大面板以及主面板与全局按键的完整映射。读完本文,你不仅能把这张快捷键表当作日常操作的速查手册使用,还能理解这些按键是如何在 pkg/gui/keybindings.go 中逐一定义与注册的,以及这份文档是如何由 pkg/cheatsheet/generate.go 自动从源码"翻译"生成、并在 CI 中做一致性校验的。
文档定位:一份自动生成的速查表
该文件首行就声明了自身的来历:
This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run
go run scripts/cheatsheet/main.go generatefrom the project root.
也就是说,这份西班牙语手册(以及 docs/keybindings/ 目录下德语、英语、法语、荷兰语、波兰语、葡萄牙语、土耳其语、中文等 9 种语言的姊妹文件)并不是人手撰写的,而是由快捷键注册代码加上 pkg/i18n 国际化词条共同生成的产物。要修改按键说明文字,正确姿势是修改 pkg/i18n 目录下的翻译文件(例如西语词条位于 pkg/i18n/spanish.go),然后在项目根目录运行:
go run scripts/cheatsheet/main.go generate
命令行入口 scripts/cheatsheet/main.go 支持两个子命令:
generate:调用 pkg/cheatsheet 的Generate(),为所有语言重新生成Keybindings_{lang}.md;check:调用Check(),把仓库中已提交的 cheatsheet 与刚刚重新生成的内容做 diff,不一致则以退出码 1 报错(CI 中即依赖这一点,见.github/workflows/ci.yml中执行的go run scripts/cheatsheet/main.go check)。
完整快捷键总表(继承自原文档)
下表完整保留了 Keybindings_es.md 中各章节的全部按键条目:按键 列与原文档一致,西班牙语说明 为原文档的 kbd 描述,中文释义 为便于中文读者理解的对应功能(与英文手册 docs/keybindings/Keybindings_en.md 及源码 Description 字段的英文文案一致)。
各面板通用按键(所有侧边面板均支持)
西班牙语手册中 Proyecto、Contenedores、Servicios、Imágenes、Volúmenes、Redes 六个章节的末尾都重复出现同样的 4 条按键,对应源码中按面板循环注册的绑定(见下文"公共绑定的循环注册"一节):
| 按键 | 西班牙语说明 | 中文释义 |
|---|---|---|
enter |
enfocar panel principal | 聚焦主面板(右侧详情/日志区域) |
[ |
anterior pestaña | 切换到上一个上下文标签页 |
] |
siguiente pestaña | 切换到下一个上下文标签页 |
/ |
filtrar lista | 对当前列表启用过滤 |
Proyecto(项目/Compose 项目面板)
| 按键 | 西班牙语说明 | 中文释义 |
|---|---|---|
e |
editar configuración de lazydocker | 编辑 Lazydocker 配置文件 |
o |
abrir configuración de lazydocker | 打开 Lazydocker 配置文件 |
m |
ver logs | 查看(项目全部服务的)日志 |
对应源码中的注册(pkg/gui/keybindings.go#L131-L151):
{
ViewName: "project",
Key: 'e',
Modifier: gocui.ModNone,
Handler: gui.handleEditConfig,
Description: gui.Tr.EditConfig,
},
{
ViewName: "project",
Key: 'o',
...
Handler: gui.handleOpenConfig,
},
{
ViewName: "project",
Key: 'm',
...
Handler: gui.handleViewAllLogs,
},
Contenedores(容器面板)
| 按键 | 西班牙语说明 | 中文释义 |
|---|---|---|
d |
borrar | 删除容器(弹出确认菜单) |
e |
esconder/mostrar contenedores parados | 隐藏/显示已停止的容器 |
p |
pausa | 暂停容器 |
s |
parar | 停止容器 |
r |
reiniciar | 重启容器 |
a |
attach | 附加到容器(交互接管) |
m |
ver logs | 查看容器日志 |
E |
ejecutar shell | 在容器内执行 shell |
c |
ejecutar comando personalizado | 执行预定义自定义命令 |
b |
ver comandos masivos | 查看批量操作命令 |
w |
abrir en navegador (first port is http) | 在浏览器中打开(假定第一个映射端口为 http) |
这 11 个按键在 pkg/gui/keybindings.go#L188-L264 中逐一注册到 ViewName: "containers" 视图上,Handler 分别指向 handleContainersRemoveMenu、handleHideStoppedContainers、handleContainerPause、handleContainerStop、handleContainerRestart、handleContainerAttach、handleContainerViewLogs、handleContainersExecShell、handleContainersCustomCommand、handleContainersBulkCommand、handleContainersOpenInBrowserCommand。
一个值得注意的细节:w 键的西语描述 abrir en navegador (first port is http) 中括号内保留了英文原文。这是因为 pkg/i18n/spanish.go#L75 中 OpenInBrowser 词条本身即为该混合文案,cheatsheet 只是原样输出翻译词条。
Servicios(Compose 服务面板)
| 按键 | 西班牙语说明 | 中文释义 |
|---|---|---|
u |
levantar servicio | 启动(up)单个服务 |
d |
borrar contenedores | 删除服务对应的容器 |
s |
parar | 停止服务 |
p |
pausa | 暂停服务 |
r |
reiniciar | 重启服务 |
S |
iniciar | 启动(start)已创建的服务 |
a |
attach | 附加到服务容器 |
m |
ver logs | 查看服务日志(渲染到主面板) |
U |
levantar proyecto | 启动整个项目(up project) |
D |
dar de baja el proyecto | 拆除整个项目(down project) |
R |
ver opciones de reinicio | 查看重启选项菜单 |
c |
ejecutar comando personalizado | 执行预定义自定义命令 |
b |
ver comandos masivos | 查看批量操作命令 |
E |
ejecutar shell | 在服务容器内执行 shell |
w |
abrir en navegador (first port is http) | 在浏览器中打开 |
注册代码位于 pkg/gui/keybindings.go#L265-L369。注意服务面板区分了 s(stop)与 S(start)、u(up 服务)与 U(up 项目)、d(删容器)与 D(down 项目):小写面向单个服务,大写面向整个 Compose 项目,这是西语手册中最容易混淆的一组按键。
Imágenes / Volúmenes / Redes(镜像、卷、网络面板)
这三个资源面板的专属按键完全同构,每个面板只有 3 个业务按键,其余均为通用按键:
| 按键 | 西班牙语说明 | 中文释义 |
|---|---|---|
c |
ejecutar comando personalizado | 执行预定义自定义命令 |
d |
limpiar imagen / volúmen / red | 删除镜像 / 清理卷 / 删除网络 |
b |
ver comandos masivos | 查看批量操作命令 |
对应 pkg/gui/keybindings.go#L370-L431 中 images、volumes、networks 三个 ViewName 下的注册,处理器分别为 handleImagesCustomCommand/handleImagesRemoveMenu/handleImagesBulkCommand、handleVolumes*、handleNetworks* 系列。
Inicio(主面板)与 Global(全局)
| 按键 | 西班牙语说明 | 中文释义 |
|---|---|---|
esc |
regresar | 返回(退出主面板的日志/详情视图,回到列表) |
+ |
next screen mode (normal/half/fullscreen) | 切换下一个屏幕模式(normal/half/fullscreen) |
_ |
prev screen mode | 切换上一个屏幕模式 |
1 |
focus projects panel | 聚焦 Projects 面板 |
2 |
focus services panel | 聚焦 Services 面板 |
3 |
focus containers panel | 聚焦 Containers 面板 |
4 |
focus images panel | 聚焦 Images 面板 |
5 |
focus volumes panel | 聚焦 Volumes 面板 |
6 |
focus networks panel | 聚焦 Networks 面板 |
注意 "Inicio" 一节只列出了 esc 一个按键:主面板同样绑定了 j/k 上下滚动、h/l 与方向键左右滚动,但这些绑定没有 Description 字段,因此不会出现在 cheatsheet 里(原因见下文"只收录有描述词条的绑定")。
按键在源码中的定义:Binding 结构与注册流程
西语手册的每一行都对应 pkg/gui/keybindings.go 中一条 Binding 记录。核心结构体定义在 pkg/gui/keybindings.go#L12-L18:
// Binding - a keybinding mapping a key and modifier to a handler. The keypress
// is only handled if the given view has focus, or handled globally if the view
// is ""
type Binding struct {
ViewName string
Handler func(*gocui.Gui, *gocui.View) error
Key interface{} // FIXME: find out how to get `gocui.Key | rune`
Modifier gocui.Modifier
Description string
}
三个关键字段决定了快捷键手册的形态:
ViewName:绑定生效的视图。为空字符串时表示"全局"(任意视图下都生效,如esc、q、Ctrl+C、PgUp/PgDn等);非空时(如"containers")只有该视图获得焦点时按键才被处理。这正是手册按面板分章节的根本原因;Key:按键本身,既可以是rune(如'd'),也可以是 gocui 的专用键(如gocui.KeyEsc、gocui.KeyEnter);Description:取自gui.Tr国际化词条的说明文本,是 cheatsheet 中"按键: 说明"里冒号右边的文字。
GetKey() 方法(pkg/gui/keybindings.go#L21-L54)负责把按键值转成手册里显示的文本:27 显示为 esc、13 显示为 enter、方向键显示为 ▲▼◄► 等,其余按键直接按字符输出。
应用启动时,keybindings() 函数(pkg/gui/keybindings.go#L593-L607)遍历 GetInitialKeybindings() 返回的全部绑定并调用 gocui 的 SetKeybinding 逐一注册——也就是说手册中列出的按键与运行时行为来自同一份数据源,天然一致。
公共绑定的循环注册:为什么六个章节的结尾一模一样
手册里每个侧边面板章节末尾都有 enter / [ / ] / / 四条相同按键。这不是文档模板复制,而是 pkg/gui/keybindings.go#L552-L588 中的循环逻辑:
for _, panel := range gui.allSidePanels() {
bindings = append(bindings,
&Binding{
ViewName: panel.GetView().Name(),
Key: gocui.KeyEnter,
...
Handler: gui.handleEnterMain,
Description: gui.Tr.FocusMain,
},
&Binding{
ViewName: panel.GetView().Name(),
Key: '[',
...
Handler: wrappedHandler(panel.HandlePrevMainTab),
},
&Binding{... Key: ']', ... Handler: wrappedHandler(panel.HandleNextMainTab) ...},
)
}
for _, panel := range gui.allListPanels() {
if !panel.IsFilterDisabled() {
bindings = append(bindings, &Binding{
ViewName: panel.GetView().Name(),
Key: '/',
Handler: wrappedHandler(gui.handleOpenFilter),
Description: gui.Tr.LcFilter,
})
}
}
allSidePanels()包含 Projects、Services、Containers、Images、Volumes、Networks 六个面板,因此每个面板都得到enter(聚焦主面板)、[(上一个上下文标签)、](下一个上下文标签)三条绑定;/过滤绑定只追加给"未禁用过滤"的列表面板(!panel.IsFilterDisabled()),过滤的交互实现(打开过滤提示、Enter提交、Esc取消)位于 pkg/gui/filtering.go;- 数字键
1–6快速跳转面板的绑定同样因为ViewName为空而归入手册的 "Global" 章节,注册代码见 pkg/gui/keybindings.go#L537-L544。
此外,每个侧边面板还循环绑定了 Tab/Backtab/h/l 与左右方向键用于在面板间切换焦点(pkg/gui/keybindings.go#L514-L523),j/k、上下方向键、鼠标滚轮与左键点击用于列表行移动(pkg/gui/keybindings.go#L525-L534)——这些绑定因缺少 Description 而未出现在手册中。
文档是如何生成的:headless GUI + 国际化词条
Keybindings_es.md 的生成链路是理解"为什么它是文档、却不会过期"的关键,核心在 pkg/cheatsheet/generate.go#L35-L61:
func generateAtDir(dir string) {
mConfig, err := config.NewAppConfig("lazydocker", "", "", "", "", true, nil, "", "")
...
for lang := range i18n.GetTranslationSets() {
os.Setenv("LC_ALL", lang)
mApp, _ := app.NewApp(mConfig)
mApp.Gui.SetupFakeGui()
file, err := os.Create(dir + "/Keybindings_" + lang + ".md")
...
bindingSections := getBindingSections(mApp)
content := formatSections(mApp, bindingSections)
...
}
}
分步骤解读:
- 按语言循环:
i18n.GetTranslationSets()返回项目支持的全部语言,每个语言先os.Setenv("LC_ALL", lang)切换 locale,使得mApp.Tr.*词条取到对应语言(西语即es,对应 pkg/i18n/spanish.go 中的词条); - 搭建无头 GUI:
SetupFakeGui()(pkg/gui/gui.go#L498-L514)以Headless: true启动一个 gocui 实例并createAllViews()+setPanels()。源码注释写得很直白:需要真实的视图和面板存在,才能知道到底有哪些按键。这正是上文那些"按面板循环"绑定的注册前提; - 只收录有描述词条的绑定:
getBindingSections()(pkg/cheatsheet/generate.go#L78-L106)遍历GetInitialKeybindings(),binding.Description == ""的直接跳过——这解释了手册中看不到q(退出)、x/?(打开选项菜单)、J/K/H/L滚动、Tab等已注册按键的原因; - 视图名到章节标题的本地化映射:
viewName为空时归入global,其余经titleMap映射到本地化标题(如mApp.Tr.ContainersTitle在西语下即Contenedores、main即Inicio),章节标题与手册正文因此全部是西语; - 格式化输出:
formatSections()(pkg/cheatsheet/generate.go#L128-L141)用formatBinding()逐行输出<kbd>按键</kbd>: 说明,包裹在<pre>块中,并在文首加上那段 auto-generated 提示。
一致性校验:check 命令与 CI 集成
防止"改了按键忘了重新生成文档"的机制在 pkg/cheatsheet/validate.go#L15-L60:
func Check() {
dir := GetKeybindingsDir() // <repo>/docs/keybindings
tmpDir := filepath.Join(os.TempDir(), "lazydocker_cheatsheet")
...
generateAtDir(tmpDir) // 在临时目录重新生成一套
actualContent := obtainContent(dir) // 读仓库里的 Keybindings_*.md
expectedContent := obtainContent(tmpDir)
if actualContent != expectedContent {
difflib.WriteUnifiedDiff(...) // 打印统一 diff
fmt.Printf("\nCheatsheets are out of date. Please run `%s` ...", generateCheatsheetCmd)
os.Exit(1)
}
fmt.Println("\nCheatsheets are up to date")
}
即:在临时目录用当前源码重新生成全部语言的手册,与 docs/keybindings/ 中已提交的内容逐字符比较,有差异就输出 unified diff 并退出码 1。该检查已被接入 CI(.github/workflows/ci.yml 中执行 go run scripts/cheatsheet/main.go check),保证仓库里每一份 Keybindings_*.md 始终与 pkg/gui/keybindings.go 中的真实绑定同步。
日常使用与更新要点
- 使用:把
Keybindings_es.md当作西语界面下的速查表;同一目录下的Keybindings_en.md、Keybindings_zh.md等 8 份文档结构与条目一一对应,差异仅在文案语言。按键与面板的对应关系由ViewName决定,只有当该面板视图持有焦点时按键才生效。 - 更新:若要新增/修改按键的说明文案,应修改 pkg/i18n 对应语言的词条(而非手改 Markdown);若要新增按键本身,需在
GetInitialKeybindings()中注册Binding并设置Description,随后运行go run scripts/cheatsheet/main.go generate重新生成九份文档,提交前可用... check验证。 - 适用前提:上述按键集合与章节划分以当前仓库源码为准,随
GetInitialKeybindings()的演进而变化;w键"以第一个映射端口为 http"的行为、过滤/上下文标签页等能力均以实际注册的处理器实现(如 pkg/commands/container.go、pkg/gui/panels/ 中的面板逻辑)为准。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00