关于 marko-ui
marko-ui 是用于从组件注册表使用 Marko 6 UI 组件的 CLI 技能,涵盖组件查找、文档查询、安装及变更检查。
主要功能
- 01搜索组件注册表
- 02查看组件文档
- 03添加并比较组件
- 04检查项目配置
- 05同步 Agent 组件列表
适用场景
- 01查找合适的 Marko 6 组件
- 02编写标记前查看属性
- 03安装或更新 UI 组件
- 04对比本地修改与注册表版本
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ svallory/ marko-ui --skill marko-ui安装后在 Claude Code 中输入/marko-ui确认已加载。
详细介绍
marko-ui
marko-ui 是适用于 Marko 6 的 shadcn/ui:无障碍组件的行为由 Zag.js 状态机提供,样式使用 Tailwind v4。以下所有操作都使用 marko-ui CLI——不要手动编写注册表中已有的组件,也不要猜测组件的 props。
使用项目的包运行器运行 CLI(bunx marko-ui、npx marko-ui、pnpm dlx marko-ui)。当 stdin 不是 TTY 时,所有命令均为非交互式:确认操作默认选择是,init 使用默认值,而不指定组件名称运行 add 时会以状态码 2 退出,不会打开选择器。
首先:项目使用哪种分发方式?
读取 components.json 中的 distribution(未设置时为 copy)。它会影响组件的获取和自定义方式。
copy(默认) | import | |
|---|---|---|
| 组件位于 | 项目中 ui 别名下(src/components/ui/<name>/) | node_modules/@marko-ui/shadcn |
| 获取组件 | marko-ui add <name> -y | 无需添加——导入 @marko-ui/shadcn/ui/<name>/<name>.marko |
| 自定义 | 编辑组件文件(它属于项目代码) | 在项目样式表中覆盖 mu-* hook 类 |
如果没有 components.json,说明尚未设置 marko-ui:运行 marko-ui init(添加 --agents 还会写入 Agent 文档)。
工作流
marko-ui search -q <query>— 查找组件(机器可读输出使用--json)。它会对每个条目的名称、标题和描述进行整词匹配;每增加一个词都会缩小排名范围,未匹配任何搜索词的条目会被排除。每个条目都会占用上下文,因此请先搜索,不要猜名称。已安装的组件会列在项目的AGENTS.md中。marko-ui docs <name>— 以 Markdown 格式显示组件文档:安装、用法、组成部分、属性、事件、键盘操作/无障碍,然后是必要示例。**在本次会话中使用某个组件之前,先阅读文档,再编写该组件的标记。**文档刻意保持精简——最多包含三个示例(第一个示例以及标记为必要的示例),其余示例列在“更多示例”下。--examples会打印所有示例;--example <id…>只打印指定的示例;--list会打印索引。docs <name> --json会在下方所示的信封中返回相同的 Markdown(以及生成该 Markdown 所依据的结构化模型)。这些 Markdown 是为你编写的:演示源码中的维护者注释已移除;属性列表每个属性占一行(name: type = default — description);= fixed X表示组件会设置该值,你设置的值会被忽略;| undefined会被省略,因为几乎所有属性都是可选的(必需属性会有标记);未记录默认值不代表“没有默认值”。代码片段中的导入路径遵循项目自身的components.json(distribution+aliases.ui),因此你读到的内容就是项目中实际使用的内容。对于项目已安装的组件,docs无需联网即可描述已安装版本:copy distribution 会读取add获取并缓存在node_modules/.cache/marko-ui/下的副本(即使add未能获取文档文件,仍会完成安装并发出DOCS_CACHE_FAILED警告,docs随后会读取注册表);import distribution 会读取已安装的@marko-ui/shadcn中的文档——并省略安装行;此时 stderr 会显示source: installed (…)(使用--json时见data.components[].source)。要查看已安装版本之外的内容,请使用--remote:它会读取注册表中的最新文档(即升级后会获取的内容),即使组件已安装也是如此。注册表中的文档是每个条目所引用的单独文件,因此show <name>会打印componentDocsRefURL,而不会打印文档本身;没有该引用的注册表条目没有文档,docs会说明这一点(NOT_FOUND,并建议使用show)。未安装的组件会从注册表获取,并显示精确的marko-ui add <name> -y命令。marko-ui show <name>— 以 JSON 格式显示注册表条目;--files会列出将写入的文件,--deps会列出 npm 依赖和注册表依赖。show在所有模式下都是 JSON 命令,因此接受--json但不会改变任何内容,而且即使在终端中,show失败时也会将marko-ui/error信封输出到 stdout——管道接收到的也是同一份文档,而不是说明文字。无论哪种情况,都应解析 stdout。marko-ui add <name> -y --json— 安装(copy distribution)。如果由你来执行,务必传入--json:它会返回哪些文件被created/updated/skipped、安装了哪些依赖,以及任何警告,因此你无需解析面向用户的输出即可了解结果。--dry-run会以相同结构预览结果,并使用dryRun: true和 PLANNED 状态(--dry-run --json);--overwrite会替换本地文件。marko-ui diff <name> --json— 对比本地修改与注册表版本:列出每个文件及其状态(unchanged/modified/missing);对于已修改的文件,还会以纯文本形式提供统一 diff,不含 ANSI 转义码。无论是否存在差异,ok都是true——diff只报告情况,不做判断。marko-ui doctor --json— 健康检查;退出代码 3 表示存在故障,每项失败的检查都包含一个fix字段,其中给出修复问题的命令(或单行操作)。请在init/add后、报告工作完成前运行此命令。marko-ui agents sync --json— 添加或移除组件后,刷新AGENTS.md中的组件列表。agentsChanged表示文件是否实际发生变化;skills会说明哪些 Agent 技能已安装,哪些已存在。 当需要获取完整的命令、标志、退出代码和错误代码列表时,marko-ui manifest会以 JSON 格式打印;marko-ui manifest <command>只会打印指定的一个命令(名称或别名)以及退出代码——检查单个标志时更省资源。名称未知会导致用法错误(退出代码为 2)。
读取 JSON
每个 --json 负载都是一个文档:{ "$type", "version", "ok", "data" }。读取 data 前,先根据 $type 分支判断——$type 的值为 marko-ui/search、marko-ui/status、marko-ui/show、marko-ui/docs、marko-ui/docs.list、marko-ui/doctor、marko-ui/registry.list 或 marko-ui/manifest。输出会压缩为单行(用于管道或程序),只有在 stdout 是终端时才会美化打印,因为此输出通常由程序读取,而缩进也会占用你付费购买的 Token。没有 --pretty 标志。
search 条目包含 name、简短的 type("ui"、"block")、description 和 registry——不包含任何可推导的信息。要从默认仓库以外的仓库安装搜索结果,请传入 <registry>/<name>;只有结果中包含非默认仓库时,data 中的 addArgument 才会说明这条规则。
status 会报告 config.distribution、config.visualStyle 和 config.iconLibrary;config.resolvedPaths 给出 cwd(绝对路径,只出现一次),以及所有相对于该路径的其他路径。
失败时会返回 读取错误 中描述的 marko-ui/error 封装——绝不会返回散文。只有 show 命令在所有模式下(包括终端)都遵循这一点:它在所有模式下都是 JSON 命令,因此失败时也会在 stdout 上输出文档。
读取命令所做的更改
add、init、eject、diff 和 agents sync 都会返回相同的三种
结构,因此一个解析器即可处理它们:
files:{ path, status },相对于cwd(只在顶部给出一次)。warnings:{ code, message, fix }。警告不是失败——命令已完成 工作,ok仍为true,警告会说明还剩下什么待处理。 代码列在marko-ui manifest的warningCodes中。人类可读输出会打印 相同的message和fix,因此你读取的内容与解析的内容 不会相互矛盾。next:建议接下来运行的命令。请据此操作,而不要自行推导。
status 的取值是固定词汇,而非散文:
| status | 含义 |
|---|---|
created | 本次运行写入了一个此前不存在的文件 |
updated | 本次运行替换了一个已有文件 |
unchanged | 该文件与仓库中的版本逐字节完全一致 |
skipped | 文件已存在,内容不同,且未对其做修改(使用 --overwrite 就会替换它) |
removed | 本次运行删除了一个过时的图标映射 |
missing | (仅 diff)仓库中有该文件,项目中没有 |
skipped 和 unchanged 表示不同的情况。 对未更改的组件重新运行 add,报告的是 unchanged,而不是 skipped;skipped 表示文件内容已发生偏移,且未被替换。在告知用户某项内容已安装前,请先查看状态——只有 created 表示“本次运行写入了该文件”。
对于 add,还要检查 items.resolved(实际安装的内容,其中包括除你所请求内容之外拉取的仓库依赖项——add button 也可能解析出 utils)、dependencies(installed / present /
failed),以及预览时的 dryRun: true;此时的状态是计划值,
而非实际观察到的状态。
读取错误
stdout 承载结果;stderr 承载所有问题。 如果通过管道运行 CLI 或捕获 stdout,你只会得到命令的有效负载,不会有其他内容——没有日志行、警告或失败文本。绝不要将 stderr 解析为数据。
使用 --json 时,失败会在 stdout 上输出一个 JSON 对象,不含其他内容,格式如下:
{
"$type": "marko-ui/error",
"version": 1,
"ok": false,
"error": {
"code": "NOT_FOUND",
"message": "Registry item \"buton\" was not found. Did you mean \"button\"?",
"suggestion": "Did you mean \"button\"?",
"details": { "itemName": "buton", "suggestions": ["button"] }
}
}
根据 error.code 分支判断,绝不要根据消息文本判断。所有代码都列在 marko-ui manifest 的 errorCodes 中——常见代码包括 NOT_FOUND(未知条目)、NOT_CONFIGURED(没有 components.json,或注册表尚未配置)、INVALID_CONFIG、NETWORK_ERROR(无法访问注册表/文档主机——退出码为 4)、FETCH_ERROR(主机返回 5xx 或 429——退出码为 4,请稍后重试)、MISSING_ENV_VARS、MONOREPO_ROOT(从工作区而非仓库根目录运行)、USAGE_ERROR(调用错误,包括未知命令或标志)、CHECK_FAILED(doctor/validate/agents --check 检查发现问题),以及 PROJECT_NOT_FOUND(该目录下没有 Marko 项目)。
连接失败(端口拒绝连接、DNS 失败、超时)在所有依赖注册表的命令中都会返回 NETWORK_ERROR,退出码为 4。主机返回 5xx 或 429 时,会返回 FETCH_ERROR,退出码同样为 4:注册表出现故障,同一命令稍后可能会成功。其他所有 HTTP 状态都会以各自的代码返回退出码 1:UNAUTHORIZED(401)、FORBIDDEN(403)、NOT_FOUND(404)、GONE(410),以及表示意外 4xx(例如 400)的 REQUEST_REJECTED——重试不会改变这些结果。
如果注册表索引可访问,details.suggestions 会提供“你是否想找”的候选项,因此你可以重试,而无需解析文本。docs、show、add 或 diff 中最常见的情况是条目名称未知。
details 绝不会包含堆栈跟踪、你主目录下的绝对路径或包含凭据的 URL——路径会缩短,注册表 URL 的查询字符串和凭据会被剥除。只有在设置了 MARKO_UI_DEBUG 时,堆栈才会出现在 stderr 上。
一个名称可能失败,而其他名称成功:marko-ui docs nope button 会在 stdout 上打印 button 的 Markdown,在 stderr 上报告 nope,并以退出码 1 退出。
退出码:0 成功 · 1 操作失败 · 2 用法错误(仅对应代码 USAGE_ERROR,且只对应此情况——参数或标志值错误或缺失;项目缺失、项目未初始化或分发版本错误都有各自的代码,退出码为 1) · 3 检查发现问题 · 4 无法访问注册表或注册表出现故障(连接失败、5xx、429)。marko-ui manifest 会列出这两个表(exitCodes、errorCodes)。
使用组件
import Dialog from "../components/ui/dialog/dialog.marko";
import Button from "../components/ui/button/button.marko";
<let/open=false/>
<Dialog open:=open>
<@trigger|props|>
<Button ...props>Edit profile</Button>
</@trigger>
<@title>Edit profile</@title>
<@description>Make changes to your profile here.</@description>
<p>Form fields go here.</p>
</Dialog>
- 部件是属性标签(
<@trigger>、<@title>),不是单独导入的子组件。marko-ui docs <name>会列出各组件的部件。 <@trigger|props|>会提供机器属性,供你展开并传递给所渲染的任意元素。这相当于 Radix 的asChild。- 状态通过
:=绑定(open:=open、value:=value、checked:=checked),或者传递值及其xChange处理函数(open=open openChange=(v) => { open = v })。 - 对于 copy 分发版本,
add还会生成一个 taglib(marko.json),除非项目已有自己的marko.json,因此安装的组件无需导入即可作为标签使用(<Button>、<CardHeader>)。显式导入也始终可用。 - 绝不要将组件导入为
Input。 它会遮蔽 Marko 保留的 props 类型。输入组件应导入为TextInput。
会导致组件静默出错的问题
以下每一项都曾导致真实故障。完成前,请对照此列表检查代码。
- 受控属性会锁定状态机。 使用
open=/value=/checked=,却没有对应的openChange/valueChange/checkedChange(或:=绑定),会使组件冻结在该状态。若只需设置初始值,请使用defaultOpen=/defaultValue=/defaultChecked=。 - 布尔值
aria-*/data-*属性的序列化结果不正确。 Marko 会将布尔值true渲染为不带值的属性:aria-selected=(bool)会输出aria-selected="",而data-active=(bool)永远无法匹配data-[active=true]:Tailwind 变体。请用String(...)包装。 - Zag 中的内容不能跨越标签边界,也不能存入状态。 状态机服务和
api对象包含函数,无法序列化以供恢复。请传递普通数据,或在模板中编写闭包(parentApi=() => api()),并在使用处调用api()。 - 不要在子组件的属性标签主体中读取父组件的
api()。 请改为通过闭包将其传入子组件的输入。症状是覆盖层打开后出现TypeError: ... is not a function。 - 不起作用的覆盖(导入发行版)。 样式层必须通过
layer(components)导入,且项目样式表中必须存在对应的@custom-variant style-<name>行。两者缺失都会静默失败。 - 无样式的组件(复制发行版)。 主题样式表未加载。请检查
components.json中记录为tailwind.css的文件是否由根布局导入,并确认 Vite 配置中包含@tailwindcss/vite。marko-ui init会配置好这两项;手动配置通常会漏掉其中一项。 - 出现
"Cannot find module @zag-js/..."或marko-zag。 缺少组件的 npm 依赖。marko-ui add会安装这些依赖;marko-ui show <name> --deps会列出依赖。
Marko 6 语言规则(<let>、<const>、<for>、事件处理器、模板中的 TypeScript)请使用 marko6 技能;@marko/run 路由请使用 marko-run。
编辑组件(复制发行版)
该文件属于项目代码。每个 .marko 文件都以一个 static const styles = { ... } 块开头,其中包含各部分的 Tailwind 类:在此修改类。交互组件通过一个标签连接其状态机:<zag/api=() => machine from=input/>,该标签会从 input 中提取状态机属性并返回 api getter;键盘处理、焦点管理和 ARIA 均由状态机负责,因此重新设置样式不会破坏无障碍访问。编辑后,运行 marko-ui diff <name> 可查看与注册表的差异。
更多信息
- 文档:https://marko-ui.saulo.tech/docs(组件页面为 Markdown 格式:
/docs/components/<name>.md) - 编写新组件:https://marko-ui.saulo.tech/docs/creating-components
- Agent 设置与故障排查:https://marko-ui.saulo.tech/docs/working-with-ai