Metadata-Version: 2.5
Name: lumut
Version: 0.1.1
Summary: Marimo-focused AnyWidget experiments for notebook interfaces
Project-URL: Homepage, https://github.com/banditelol/lumut
Project-URL: Repository, https://github.com/banditelol/lumut
Project-URL: Issues, https://github.com/banditelol/lumut/issues
Author: lumut contributors
License: MIT License
        
        Copyright (c) 2026 lumut contributors
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Keywords: anywidget,data-editor,jupyter,marimo,notebook
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: User Interfaces
Requires-Python: >=3.11
Requires-Dist: anywidget>=0.11.0
Requires-Dist: traitlets>=5.14.0
Provides-Extra: docs
Requires-Dist: mkdocstrings[python]>=0.25.1; extra == 'docs'
Requires-Dist: zensical>=0.0.38; extra == 'docs'
Provides-Extra: test
Requires-Dist: pytest>=8.0; extra == 'test'
Description-Content-Type: text/markdown

# lumut

Lumut—*moss* in Indonesian—is an MIT-licensed collection of marimo-focused
AnyWidgets and an experimental ground for creating new notebook widgets. It is
heavily inspired by [wigglystuff](https://github.com/koaning/wigglystuff):
small Python APIs, self-contained frontend bundles, and practical notebook
demos. The first widgets explore data editing; future experiments do not need
to be tables.

## Gallery

<div align="center">
  <a href="https://molab.marimo.io/github/banditelol/lumut/blob/main/demos/exp_data_editor.py/wasm?utm_source=lumut">
    <img src="docs/assets/gallery/exp-data-editor.svg" alt="The exp_data_editor showing wrapped text rows" width="640">
  </a>
  <br>
  <strong>exp_data_editor</strong><br>
  <a href="https://molab.marimo.io/github/banditelol/lumut/blob/main/demos/exp_data_editor.py/wasm?utm_source=lumut">molab</a> ·
  <a href="https://adityarp.com/lumut/reference/exp-data-editor/">API</a> ·
  <a href="docs/reference/exp-data-editor.md">Markdown</a>
</div>

<div align="center">
  <a href="https://molab.marimo.io/github/banditelol/lumut/blob/main/demos/data_editor_enhance.py/wasm?utm_source=lumut">
    <img src="docs/assets/gallery/data-editor-enhance.svg" alt="The data_editor_enhance Glide editor showing wrapped text rows" width="640">
  </a>
  <br>
  <strong>data_editor_enhance</strong> (Glide rough resize experiment)<br>
  <a href="https://molab.marimo.io/github/banditelol/lumut/blob/main/demos/data_editor_enhance.py/wasm?utm_source=lumut">molab</a> ·
  <a href="https://adityarp.com/lumut/reference/data-editor-enhance/">API</a> ·
  <a href="docs/reference/data-editor-enhance.md">Markdown</a>
</div>

The preview is an illustration of the widget. The MoLab demo runs the actual
AnyWidget from this repository.

## Current experiments

The current collection contains two data-editor experiments. They are useful
testbeds for marimo integration, interaction design, and AnyWidget packaging;
they are not intended to replace marimo's production `ui.data_editor` for
large datasets.

Both editors accept the documented `marimo.ui.data_editor` inputs: eager
Pandas-, Polars-, or PyArrow-style dataframes, a list of scalars (shown in a
`value` column), records, and column-oriented mappings. They support `label`,
`on_change`, `editable_columns`, `column_sizing_mode`, `pagination`, and
`page_size`; their edited value is normalized to a list of row dictionaries for
the AnyWidget transport.

Rows are measured only after they are rendered. Wrapped content can grow a row,
but never beyond `max_row_height`. Resizing a column triggers a remeasurement
of rendered rows, rather than an O(N) pass over all data.

## Why this does not use Glide Data Grid

Glide Data Grid remains a capable canvas-based editor, but its public
`rowHeight` API is either one constant or a callback indexed by row. In the
current implementation, the callback path requires the grid to walk row
heights to derive total height and map a scroll offset to a row. A cached
callback therefore does not make exact variable wrapped-row height suitable for
very large datasets. The upstream "fit content" request was discussed as a
consumer-side canvas-measurement workaround rather than delivered as a native
auto-height API.

Lumut instead uses TanStack Virtual's dynamic measurement model: unseen rows
have an estimate, rendered rows are measured, and the virtualizer adjusts
geometry as measurements arrive. `max_row_height` makes that refinement
bounded and predictable after column resizing.

Maintenance is a second consideration. Glide's most recent stable GitHub
release is 6.0.3 (February 2024), while development since then has appeared as
intermittent 6.0.4 alpha commits. That does not mean the project is abandoned,
but it makes an upstream change to its scroll geometry a higher-risk dependency
for an experimental component. Lumut depends on the MIT-licensed TanStack
Table and TanStack Virtual projects instead, and owns only the narrow editor
surface it needs.

References:

- [Glide `rowHeight` API](https://github.com/glideapps/glide-data-grid/blob/main/packages/core/API.md)
- [Glide issue #581: Make Row Height Fit Content](https://github.com/glideapps/glide-data-grid/issues/581)
- [Glide releases](https://github.com/glideapps/glide-data-grid/releases)
- [TanStack Virtual dynamic measurement example](https://tanstack.com/virtual/latest/docs/framework/react/examples/dynamic)

## Install and use

Install the published package with:

```bash
uv pip install lumut
```

For local development, build the JavaScript bundle before using the editable
package:

```bash
uv pip install -e .
npm install
npm run build
```

```python
import marimo as mo
from lumut import exp_data_editor

editor = mo.ui.anywidget(exp_data_editor(
    [
        {"name": "Ada", "notes": "A long value that can wrap naturally."},
        {"name": "Grace", "notes": "Another editable row."},
    ],
    label="People",
    editable_columns=["notes"],
    max_row_height=160,
))

editor
```

`editor.value` is the edited list of row dictionaries.

`column_sizing_mode`, `pagination`, and `page_size` remain available for
compatibility with marimo's documented API, even though marimo marks them
deprecated.

## Glide rough resize experiment

`data_editor_enhance` is the Glide-based resize experiment.
It is a Glide Data Grid implementation of the same small input/value contract
as `exp_data_editor` and `mo.ui.data_editor`, with `wrapped_columns` added.
During a wrapped-column resize it estimates a single capped row height from the
visible row window plus a 20-row buffer. On pointer release it applies that
sampled height to all rows. This avoids an O(N) measurement pass while dragging
but is an approximation, not content-fit auto-height: off-screen rows can be
over- or under-sized.

It deliberately pins the browser bundle to React 18: Glide 6.0.3 declares
React 16–18 peer support, whereas the TanStack-only editor has no such Glide
constraint.

Use this only for experimentation with eager small-to-medium data. Glide's
variable `rowHeight` callback still makes its scroll geometry a poor fit for a
million-row exact-auto-height editor. For a measured, bounded-height design,
use `exp_data_editor` and continue the planned windowed-data work in
[issue #1](https://github.com/banditelol/lumut/issues/1).

```python
from lumut import data_editor_enhance

glide_editor = data_editor_enhance(
    [{"notes": "Resize the notes column to try rough wrapping."}],
    editable_columns=["notes"],
    wrapped_columns=["notes"],
    max_row_height=160,
)
```

## Scope

This first experiment includes typed cell editing, column resize, wrapped text,
capped dynamic heights, column-name copying, adding columns on either side,
and adding or deleting rows. It does not yet implement spreadsheet range
selection, fill handles, search, or server/windowed data.

## Project conventions

The project follows wigglystuff's useful conventions: a small Python AnyWidget
class per experiment, a bundled JavaScript entry point, static assets packaged
by Hatchling, a `Makefile`, and focused Python tests.

`AGENTS.md` is the Codex-native replacement for wigglystuff's Claude-facing
instructions. `.codex/workflows.md` records the equivalent development
commands. Conductor's declarative workspace schema (`.conductor/settings.toml`)
has no Codex equivalent, so it is intentionally not copied; the command mapping
is documented in `.codex/workflows.md` instead.

## Development

```bash
make install
make build
make test
make dev
```

To inspect the widget in marimo after installing the package locally, run
`uv run --with marimo marimo edit demos/exp_data_editor.py`.

## Releasing to PyPI

Releases use GitHub Actions and [PyPI Trusted Publishing](https://docs.pypi.org/trusted-publishers/), so a PyPI API token is not stored in this repository.

One-time setup:

1. If `lumut` is not yet on PyPI, add a **pending** Trusted Publisher in your PyPI account settings. If it already exists, add a Trusted Publisher in that project's Publishing settings. In both cases use project name `lumut`, owner `banditelol`, repository `lumut`, workflow file `publish.yml`, and environment `pypi`.
2. The first successful run of a pending publisher creates the PyPI project; its configured project name must exactly match `project.name`.
3. In GitHub, create the protected `pypi` environment if you want approvals before publication. The workflow works without protection too.

For each release, update `version` in `pyproject.toml`, add release notes, and create a GitHub release whose tag is `v<version>` (for example, `v0.1.0`). The publishing workflow rebuilds the frontend, builds and checks the wheel and source distribution, then uploads those exact artifacts to PyPI. To verify a build locally before creating the release, run `make package` and `uvx twine check dist/*`.
