mcpField
M
marko-ui

marko-ui

github.com/svallory开发5131 次安装4 周前更新
代码仓库

关于 marko-ui

marko-ui 是用于从组件注册表使用 Marko 6 UI 组件的 CLI 技能,涵盖组件查找、文档查询、安装及变更检查。

主要功能

  1. 01搜索组件注册表
  2. 02查看组件文档
  3. 03添加并比较组件
  4. 04检查项目配置
  5. 05同步 Agent 组件列表

适用场景

  1. 01查找合适的 Marko 6 组件
  2. 02编写标记前查看属性
  3. 03安装或更新 UI 组件
  4. 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 文档)。

工作流

  1. marko-ui search -q <query> — 查找组件(机器可读输出使用 --json)。它会对每个条目的名称、标题和描述进行整词匹配;每增加一个词都会缩小排名范围,未匹配任何搜索词的条目会被排除。每个条目都会占用上下文,因此请先搜索,不要猜名称。已安装的组件会列在项目的 AGENTS.md 中。
  2. 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> 会打印 componentDocsRef URL,而不会打印文档本身;没有该引用的注册表条目没有文档,docs 会说明这一点(NOT_FOUND,并建议使用 show)。未安装的组件会从注册表获取,并显示精确的 marko-ui add <name> -y 命令。
  3. marko-ui show <name> — 以 JSON 格式显示注册表条目;--files 会列出将写入的文件,--deps 会列出 npm 依赖和注册表依赖。show 在所有模式下都是 JSON 命令,因此接受 --json 但不会改变任何内容,而且即使在终端中,show 失败时也会将 marko-ui/error 信封输出到 stdout——管道接收到的也是同一份文档,而不是说明文字。无论哪种情况,都应解析 stdout。
  4. marko-ui add <name> -y --json — 安装(copy distribution)。如果由你来执行,务必传入 --json:它会返回哪些文件被 created / updated / skipped、安装了哪些依赖,以及任何警告,因此你无需解析面向用户的输出即可了解结果。--dry-run 会以相同结构预览结果,并使用 dryRun: true 和 PLANNED 状态(--dry-run --json);--overwrite 会替换本地文件。
  5. marko-ui diff <name> --json — 对比本地修改与注册表版本:列出每个文件及其状态(unchanged / modified / missing);对于已修改的文件,还会以纯文本形式提供统一 diff,不含 ANSI 转义码。无论是否存在差异,ok 都是 true——diff 只报告情况,不做判断。
  6. marko-ui doctor --json — 健康检查;退出代码 3 表示存在故障,每项失败的检查都包含一个 fix 字段,其中给出修复问题的命令(或单行操作)。请在 init / add 后、报告工作完成前运行此命令。
  7. 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。

会导致组件静默出错的问题

以下每一项都曾导致真实故障。完成前,请对照此列表检查代码。

  1. 受控属性会锁定状态机。 使用 open= / value= / checked=,却没有对应的 openChange / valueChange / checkedChange(或 := 绑定),会使组件冻结在该状态。若只需设置初始值,请使用 defaultOpen= / defaultValue= / defaultChecked=。
  2. 布尔值 aria-* / data-* 属性的序列化结果不正确。 Marko 会将布尔值 true 渲染为不带值的属性:aria-selected=(bool) 会输出 aria-selected="",而 data-active=(bool) 永远无法匹配 data-[active=true]: Tailwind 变体。请用 String(...) 包装。
  3. Zag 中的内容不能跨越标签边界,也不能存入状态。 状态机服务和 api 对象包含函数,无法序列化以供恢复。请传递普通数据,或在模板中编写闭包(parentApi=() => api()),并在使用处调用 api()。
  4. 不要在子组件的属性标签主体中读取父组件的 api()。 请改为通过闭包将其传入子组件的输入。症状是覆盖层打开后出现 TypeError: ... is not a function。
  5. 不起作用的覆盖(导入发行版)。 样式层必须通过 layer(components) 导入,且项目样式表中必须存在对应的 @custom-variant style-<name> 行。两者缺失都会静默失败。
  6. 无样式的组件(复制发行版)。 主题样式表未加载。请检查 components.json 中记录为 tailwind.css 的文件是否由根布局导入,并确认 Vite 配置中包含 @tailwindcss/vite。marko-ui init 会配置好这两项;手动配置通常会漏掉其中一项。
  7. 出现 "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> 可查看与注册表的差异。

更多信息

评论

支持 Markdown