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. 02traitを使ったウィジェット状態の同期
  3. 03ライト・ダークモード用の簡潔なCSS
  4. 04外部JavaScript・CSSファイルの利用

活用シーン

  1. 01シンプルな対話型ノートブックウィジェットの作成
  2. 02marimoでの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 = mo.ui.anywidget(OriginalAnywidget()) でラップする必要があります。必要に応じて pathlib を使い、_esm と _css を外部ファイルに指定することもできます。ウィジェットで手の込んだ JavaScript や CSS を多用する場合に適しています。

import anywidget import traitlets

class CounterWidget(anywidget.AnyWidget): _esm = """ // メインの render 関数を定義します 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); } // 重要!ここで最後にエクスポートする必要があります! export default { render }; """ _css = """button{ font-size: 14px; }""" number = traitlets.Int(0).tag(sync=True)

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

別のセルからウィジェットを取得します。.value は辞書です。

print(widget.value["number"])

上記は、シンプルなカウンターウィジェットで動作する最小限の例です。一般に、必要な JavaScript と CSS が増えるため、ウィジェットは大幅に大きくなることがあります。非常に単純なウィジェットでない限り、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 }; を記述してエクスポートします
    • すべてのウィジェットは 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 要素と組み合わせないでください
  5. 外部ファイルのパス:外部の _esm/_css ファイルに pathlib を使う場合は、パスをプロジェクトディレクトリからの相対パスにしてください。この場合は Path(__file__) の使用を検討してください。プロジェクト外のファイル(例:~/.ssh、~/.env、/etc/)を読み込んだり、その内容をウィジェットの出力に埋め込んだりしないでください。

シンプルなほうがよいです。巧妙な抽象化よりも、明快で直接的なコードを優先してください。プロジェクトに初めて参加した人でも、フレームワークの仕組みを調べたり、間接的な処理をたどったりせずに、コードを上から順に読んで理解できるようにしましょう。

コメント