anywidget-generator

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese
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.
<example title="Example of simple anywidget implementation"> 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
在编写anywidget时,请在
_esm
中使用原生JavaScript(vanilla javascript),不要忘记
_css
。CSS需要在浅色模式和深色模式下都有独特的外观,除非明确要求进一步扩展,否则请保持CSS代码精简。在展示组件时,必须通过
widget = mo.ui.anywidget(OriginalAnywidget())
进行包装。如果需要,你也可以使用pathlib将
_esm
_css
指向外部文件,当组件需要大量复杂的JavaScript或CSS代码时,这种方式会很实用。
<example title="简单anywidget实现示例"> import anywidget import traitlets
class CounterWidget(anywidget.AnyWidget): _esm = """ // 定义主渲染函数 function render({ model, el }) { let count = () => model.get("number"); let btn = document.createElement("button"); 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); } // 重要!必须在底部导出! 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.

从其他单元格获取组件,
.value
是一个字典。

print(widget.value["number"]) </example>
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.
print(widget.value["number"]) </example>
以上是一个简单计数器组件的最小示例。通常情况下,由于需要大量JavaScript和CSS代码,组件可能会变得更复杂。除非组件极其简单,否则你应该考虑使用pathlib将
_esm
_css
指向外部文件。
在分享anywidget时,请保持示例精简。除非明确要求,否则不要将其与marimo UI元素结合使用。

Best Practices

最佳实践

Unless specifically told otherwise, assume the following:
  1. 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.
  2. 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) { ... }
  3. Wrap the widget for display:
    • Always wrap with marimo:
      widget = mo.ui.anywidget(OriginalAnywidget())
    • Access values via
      widget.value
      which returns a dictionary
  4. 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
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.
除非另有明确说明,请遵循以下规则:
  1. _esm
    中使用原生JavaScript
    • 定义一个接收
      { model, el }
      作为参数的
      render
      函数
    • 使用
      model.get()
      读取trait值
    • 使用
      model.set()
      model.save_changes()
      更新trait
    • 使用
      model.on("change:traitname", callback)
      监听变化
    • 在底部通过
      export default { render };
      导出默认对象
    • 所有组件都继承自
      anywidget.AnyWidget
      ,因此
      widget.observe(handler)
      仍然是响应状态变化的标准方式。
    • Python构造函数通常会验证边界、长度或选项数量;请直接使用抛出的
      ValueError/TraitError
      作为指导,不要重复实现逻辑。
  2. 包含
    _css
    样式
    • 除非明确要求扩展,否则保持CSS精简
    • 确保在浅色和深色模式下都有独特的外观
    • 使用CSS媒体查询实现深色模式:
      @media (prefers-color-scheme: dark) { ... }
  3. 包装组件以进行展示
    • 始终使用marimo进行包装:
      widget = mo.ui.anywidget(OriginalAnywidget())
    • 通过
      widget.value
      访问值,它会返回一个字典
  4. 保持示例精简
    • 添加一个marimo笔记本以突出核心功能
    • 仅展示基础用法
    • 除非明确要求,否则不要与其他marimo UI元素结合使用
越简单越好。优先选择直观、直接的代码,而非巧妙的抽象——项目的新手应该能够从上到下阅读代码并理解其含义,而无需查阅框架的魔法特性或追踪间接调用。