mcpField
A
anywidget-generator

anywidget-generator

github.com/marimo-team开发1773.6k 次安装10 周前更新
代码仓库

关于 anywidget-generator

anywidget-generator 是用于创建 anywidget 组件的技能,面向为 marimo 笔记本开发组件的开发者。它说明了如何使用原生 JavaScript 渲染、同步 trait 状态、编写精简的明暗主题 CSS,以及按需使用外部资源文件。

主要功能

  1. 01使用原生 JavaScript 实现渲染
  2. 02通过 trait 同步组件状态
  3. 03添加精简的明暗主题 CSS
  4. 04使用外部 JavaScript 和 CSS 文件

适用场景

  1. 01构建简单的交互式笔记本组件
  2. 02在 marimo 中显示 anywidget 组件
  3. 03将较大的组件资源拆分为项目文件

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/marimo-team/skills --skill anywidget-generator

安装后在 Claude Code 中输入/anywidget-generator确认已加载。

详细介绍

编写 anywidget 时,请在 _esm 中使用原生 JavaScript,并且别忘了添加 _css。CSS 在浅色模式和深色模式下都应有独特的风格。除非明确要求进一步完善,否则请保持 CSS 简洁。显示 widget 时,必须使用 widget = mo.ui.anywidget(OriginalAnywidget()) 包装。必要时,也可以使用 pathlib 将 _esm 和 _css 指向外部文件。如果 widget 包含大量复杂的 JavaScript 或 CSS,这样做会更合适。

import anywidget import traitlets

class CounterWidget(anywidget.AnyWidget): _esm = """ // Define the main render function function render({ model, el }) { let count = () => model.get("number"); let btn = document.createElement("b8utton"); btn.innerHTML = count is ${count()}; btn.addEventListener("click", () => { model.set("number", count() + 1); model.save_changes(); }); model.on("change:number", () => { btn.innerHTML = count is ${count()}; }); el.appendChild(btn); } // Important! We must export at the bottom here! export default { render }; """ _css = """button{ font-size: 14px; }""" number = traitlets.Int(0).tag(sync=True)

widget = mo.ui.anywidget(CounterWidget()) widget

Grabbing the widget from another cell, .value is a dictionary.

print(widget.value["number"])

上面是一个可用于简单计数器 widget 的最小示例。一般来说,由于需要编写大量 JavaScript 和 CSS,widget 可能会变得大得多。除非 widget 非常简单,否则应考虑使用 pathlib 为 _esm 和 _css 指定外部文件。

分享 anywidget 时,请保持示例简洁。除非明确要求,否则无需将其与 marimo UI 元素结合使用。

最佳实践

除非另有明确要求,否则请遵循以下原则:

  1. 在 _esm 中使用原生 JavaScript:

    • 定义一个接收 { model, el } 作为参数的 render 函数
    • 使用 model.get() 读取 trait 值
    • 使用 model.set() 和 model.save_changes() 更新 trait
    • 使用 model.on("change:traitname", callback) 监听变化
    • 在底部使用 export default { render }; 导出
    • 所有 widget 都继承自 anywidget.AnyWidget,因此 widget.observe(handler) 仍是响应状态变化的标准方式。
    • Python 构造函数通常会验证边界、长度或选项数量;请根据抛出的 ValueError/TraitError 调整,而不要重复实现这些逻辑。
  2. 添加 _css 样式:

    • 除非明确要求更多样式,否则保持 CSS 简洁
    • 确保浅色模式和深色模式下都具有独特的风格
    • 使用 CSS 深色模式媒体查询:@media (prefers-color-scheme: dark) { ... }
  3. 包装 widget 以供显示:

    • 始终使用 marimo 包装:widget = mo.ui.anywidget(OriginalAnywidget())
    • 通过 widget.value 访问值,该属性会返回一个字典
  4. 保持示例简洁:

    • 添加一个突出核心用途的 marimo notebook
    • 只展示基本用法
    • 除非明确要求,否则不要与其他 marimo UI 元素结合使用
  5. 外部文件路径:使用 pathlib 指定外部 _esm/_css 文件时,路径应相对于项目目录;可以考虑使用 Path(__file__)。不要读取项目外部的文件(例如 ~/.ssh、~/.env、/etc/),也不要将这些文件的内容嵌入 widget 输出中。

越简单越好。优先选择直观、直接的代码,而不是巧妙的抽象——项目新手应该能够从头到尾读懂代码,无需查阅框架内部机制,也无需追踪间接调用。

评论

支持 Markdown