Metadata-Version: 2.4
Name: manywidgets
Version: 0.1.0
Summary: A composable set of anywidget-based widgets for data analysis and geospatial work in notebooks — works well with lonboard, and renders statically (no kernel) via the myst-anywidget-static-export plugin.
Project-URL: Homepage, https://github.com/developmentseed/manywidgets
Project-URL: Repository, https://github.com/developmentseed/manywidgets
Project-URL: Issues, https://github.com/developmentseed/manywidgets/issues
Author-email: Development Seed <sanjay@developmentseed.org>
License-Expression: MIT
License-File: LICENSE
Keywords: anywidget,geospatial,jupyter,lonboard,mystmd,widgets
Classifier: Framework :: Jupyter
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Scientific/Engineering :: Visualization
Requires-Python: >=3.9
Requires-Dist: anywidget>=0.9
Requires-Dist: ipywidgets>=8
Requires-Dist: traitlets>=5
Provides-Extra: dev
Requires-Dist: ipykernel; extra == 'dev'
Requires-Dist: nbconvert; extra == 'dev'
Requires-Dist: nbformat; extra == 'dev'
Requires-Dist: numpy; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Provides-Extra: lonboard
Requires-Dist: lonboard>=0.10; extra == 'lonboard'
Provides-Extra: numpy
Requires-Dist: numpy; extra == 'numpy'
Description-Content-Type: text/markdown

# manywidgets

A composable set of [anywidget](https://anywidget.dev)-based widgets for data
analysis and geospatial work in notebooks. They're self-contained, link and
compose, work well with [lonboard](https://developmentseed.org/lonboard/), and
render statically (no kernel) via the
[`myst-anywidget-static-export`](https://github.com/developmentseed/myst-anywidget-static-export)
MyST plugin.

> **Status:** early but feature-complete for v1 — `Chart`, input controls (`Slider`,
> `RangeSlider`, `Dropdown`, `Toggle`, `Button`, `NumberInput`), value displays
> (`Stat`, `NumberDisplay`, `Text`), layout containers (`Row`, `Column`, `Grid`),
> the `Binder` linking primitive, and optional lonboard interop (`LayerToggle`,
> `FilterBinder`, `LayerFilter`).

## Install

```bash
pip install manywidgets
# optional lonboard interop widgets:
pip install "manywidgets[lonboard]"
```

## Quick start

```python
from manywidgets import Chart, Slider, Binder
import numpy as np

chart = Chart(title="Demo", x_label="x", y_label="y", height=320)
chart.add_series(x=np.linspace(0, 10, 100), y=np.sin(np.linspace(0, 10, 100)), name="sin")
chart
```

## Widgets

```python
from manywidgets import (
    Chart,                                   # Chart.js charts
    Slider, RangeSlider, Dropdown, Toggle, Button, NumberInput,  # input controls
    Stat, NumberDisplay, Text, Legend,       # value displays
    Row, Column, Grid,                       # layout (arrange + keep children linked)
    Binder,                                  # linking with transforms / nested paths
)

# Optional lonboard interop (pip install "manywidgets[lonboard]")
from manywidgets.lonboard import LayerToggle, FilterBinder, LayerFilter
```

## Linking widgets

Two complementary tools — see [`docs/guides/linking.md`](docs/guides/linking.md):

```python
from ipywidgets import jsdlink

# 1. jslink / jsdlink — the canonical, kernel-free pass-through link.
slider = Slider(label="Amplitude", min=0, max=5, value=1)
jsdlink((slider, "value"), (chart, "title"))  # browser-side, works live + static

# 2. Binder — for transforms / nested paths jslink can't express.
Binder(source=slider, source_field="value",
       target=chart, target_field="height",
       multiplier=100, offset=200)
```

## Agent skill

manywidgets ships an [agent skill](src/manywidgets/skill/SKILL.md) so coding
agents can help you build widgets and dashboards. Install it into a location your
agent discovers:

```bash
manywidgets install-skill            # ./.claude/skills/manywidgets/ (this project)
manywidgets install-skill --user     # ~/.claude/skills/manywidgets/ (all projects)
manywidgets install-skill --path DIR # anywhere else (other agents)
```

The skill (an entrypoint plus `references/` on widget API, usage, and authoring)
travels inside the wheel, so it always matches the installed version. The
per-widget API reference is generated from widget traits — never hand-edited.

## How it works / design

Every widget extends a thin `BaseWidget` (auto-assigns a stable `widget_id`) and
follows the TypeScript "golden example" structure: `src/index.ts` bundled by
esbuild into `dist/widget.js`, styling via the `_css` trait. Cross-widget
resolution and the static-export safety rules live once in a shared
`@manywidgets/core` TypeScript module that esbuild inlines into each widget.

This package has **no code dependency** on the static-export plugin — it just
follows the rules the plugin needs (wrap `save_changes`, never inject `<link>`
into the mount element, vanilla DOM, buffer-free core widgets, link via
`jslink`/`Binder`). The docs site references the plugin by release URL only.

## Development

```bash
npm install
npm run build          # esbuild every widget src/index.ts -> dist/widget.js
npm run typecheck      # tsc --noEmit
npm test               # vitest (JS unit tests)
pip install -e ".[dev]"
pytest
```

### Docs

Each widget owns its docs in `src/manywidgets/<name>/doc.md` (prose + a
`{code-cell}` example + an `{api-table}` placeholder). `npm run docs:gen` builds
`docs/widgets/<name>.ipynb` from those — auto-generating the API table from trait
introspection — so the per-widget pages are **generated build artifacts**
(gitignored), not hand-maintained. The agent skill's API reference is generated
the same way (`npm run skill:gen` → `src/manywidgets/skill/references/widgets-api.md`,
which **is** committed and CI-checked for drift) — regenerate it after changing
any widget's traits. Build and view:

```bash
# lonboard + geopandas + pyarrow are only needed for the lonboard interop example
pip install -e ".[numpy,lonboard]" ipykernel nbconvert nbformat geopandas pyarrow
python -m ipykernel install --user --name manywidgets-venv
npm run docs:gen       # generate docs/widgets/*.ipynb from each widget's doc.md
jupyter nbconvert --to notebook --execute --inplace docs/examples/*.ipynb docs/widgets/*.ipynb
cd docs && npx myst build --html && cd ..
npm run serve          # serve over HTTP at http://localhost:8000 (NOT file://)
```

> Serve the built site over HTTP — opening the HTML via `file://` breaks asset and
> widget loading (absolute paths + dynamic `import()`).

Releases are cut by publishing a GitHub Release (CI builds the wheel — with JS
compiled by the build hook — and publishes to PyPI). The JS bundles are
gitignored and rebuilt by the build; they ship inside the wheel.

## License

MIT — see [LICENSE](LICENSE).
