首页/前端开发/anywidget-generator
A

anywidget-generator

by @marimo-teamv1.0.0
0.0(0)

指导编写 anywidget,使用原生 JavaScript 和 CSS,确保在亮/暗模式下样式独特且精简。

AnywidgetJupyter WidgetsFrontend DevelopmentInteractive DashboardsPython EcosystemGitHub
安装方式
npx skills add marimo-team/skills --skill anywidget-generator
compare_arrows

Before / After 效果对比

1
使用前
1在没有 Anywidget 生成器技能的辅助下,开发者需要手动编写 Anywidget 的 JavaScript (ESM)CSS 代码,确保在亮/暗模式下的样式兼容性,并手动处理组件的封装和集成。这需要对 Web 组件和 Jupyter 环境有深入了解,且容易在样式和交互上出错。
2
3```python
4# 示例:手动编写 Anywidget
5import anywidget
6import traitlets
7
8class MyManualWidget(anywidget.AnyWidget):
9    _esm = """
10        export function render({ model, el }) {
11            let button = document.createElement("button");
12            button.textContent = "Click Me";
13            button.onclick = () => {
14                model.set("value", model.get("value") + 1);
15                model.save_changes();
16            };
17            el.appendChild(button);
18
19            model.on("change:value", () => {
20                console.log("Value changed:", model.get("value"));
21            });
22        }
23    """
24    _css = """
25        button {
26            background-color: lightblue;
27            color: black;
28            padding: 10px;
29            border-radius: 5px;
30        }
31        @media (prefers-color-scheme: dark) {
32            button {
33                background-color: darkblue;
34                color: white;
35            }
36        }
37    """
38    value = traitlets.Int(0).tag(sync=True)
39
40# widget = MyManualWidget()
41# widget
42```
使用后
1Anywidget 生成器技能能够根据用户的需求,快速生成定制化的 Anywidget 组件代码,包括 ESM JavaScript 和 CSS。它确保了 CSS 在亮/暗模式下的自适应,并提供了简洁的封装方式,大大简化了 Anywidget 的开发和集成。
2
3```python
4# 技能辅助生成的 Anywidget 代码
5import anywidget
6import traitlets
7import markdown_it
8
9class GeneratedAnywidget(anywidget.AnyWidget):
10    _esm = traitlets.Unicode("").tag(sync=True)
11    _css = traitlets.Unicode("").tag(sync=True)
12    content = traitlets.Unicode("Hello from Anywidget!").tag(sync=True)
13
14    def __init__(self, content_md, **kwargs):
15        super().__init__(**kwargs)
16        self.content = markdown_it.MarkdownIt().render(content_md)
17        self._esm = """
18            export function render({ model, el }) {
19                let div = document.createElement("div");
20                div.innerHTML = model.get("content");
21                el.appendChild(div);
22
23                model.on("change:content", () => {
24                    div.innerHTML = model.get("content");
25                });
26            }
27        """
28        self._css = """
29            div {
30                padding: 15px;
31                border-radius: 8px;
32                font-family: sans-serif;
33                background-color: #f0f0f0;
34                color: #333;
35                border: 1px solid #ccc;
36            }
37            @media (prefers-color-scheme: dark) {
38                div {
39                    background-color: #333;
40                    color: #f0f0f0;
41                    border: 1px solid #555;
42                }
43            }
44        """
45
46# widget = GeneratedAnywidget("# 这是一个由技能生成的 **Anywidget**\n\n它支持 Markdown 内容,并且在亮/暗模式下自动适应样式。")
47# widget
48
49# 技能也可以直接生成并包装 widget
50# import marimo as mo
51# original_widget = GeneratedAnywidget("## 另一个技能生成的 Anywidget")
52# widget = mo.ui.anywidget(original_widget)
53# widget
54```

description SKILL.md

anywidget-generator

When writing an anywidget use vanilla javascript in _esm and do not forget about _css. The css should look bespoke in light mode and dark mode. Keep the css small unless explicitly asked to go the extra mile. When you display the widget it must be wrapped via widget = mo.ui.anywidget(OriginalAnywidget()). You can also point _esm and _css to external files if needed using pathlib. This makes sense if the widget does a lot of elaborate JavaScript or CSS. 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"]) The above is a minimal example that could work for a simple counter widget. In general the widget can become much larger because of all the JavaScript and CSS required. Unless the widget is dead simple, you should consider using external files for _esm and _css using pathlib. When sharing the anywidget, keep the example minimal. No need to combine it with marimo ui elements unless explicitly stated to do so. Best Practices Unless specifically told otherwise, assume the following: Use vanilla JavaScript in _esm: Define a render function that takes { model, el } as parameters Use model.get() to read trait values Use model.set() and model.save_changes() to update traits Listen to changes with model.on("change:traitname", callback) Export default with export default { render }; at the bottom All widgets inherit from anywidget.AnyWidget, so widget.observe(handler) remains the standard way to react to state changes. Python constructors tend to validate bounds, lengths, or choice counts; let the raised ValueError/TraitError guide you instead of duplicating the logic. Include _css styling: Keep CSS minimal unless explicitly asked for more Make it look bespoke in both light and dark mode Use CSS media query for dark mode: @media (prefers-color-scheme: dark) { ... } Wrap the widget for display: Always wrap with marimo: widget = mo.ui.anywidget(OriginalAnywidget()) Access values via widget.value which returns a dictionary Keep examples minimal: Add a marimo notebook that highlights the core utility Show basic usage only Don't combine with other marimo UI elements unless explicitly requested External file paths: When using pathlib for external _esm/_css files, keep paths relative to the project directory, consider using Path(file) for this. Do not read files outside the project (e.g., ~/.ssh, ~/.env, /etc/) or embed their contents in widget output. Dumber is better. Prefer obvious, direct code over clever abstractions—someone new to the project should be able to read the code top-to-bottom and grok it without needing to look up framework magic or trace through indirection.Weekly Installs469Repositorymarimo-team/skillsGitHub Stars71First SeenFeb 10, 2026Security AuditsGen Agent Trust HubPassSocketPassSnykPassInstalled onclaude-code335opencode322codex301github-copilot298gemini-cli294amp293

forum用户评价 (0)

发表评价

效果
易用性
文档
兼容性

暂无评价,来写第一条吧

统计数据

安装量0
评分0.0 / 5.0
版本1.0.0
更新日期2026年3月17日
对比案例1 组

用户评分

0.0(0)
5
0%
4
0%
3
0%
2
0%
1
0%

为此 Skill 评分

0.0

兼容平台

🔧Claude Code

时间线

创建2026年3月17日
最后更新2026年3月17日