Metadata-Version: 2.5
Name: spaday-codemirror
Version: 0.1.1
Summary: CodeMirror 6 code editor for spaday
Project-URL: Repository, https://github.com/1kbgz/spaday-codemirror
Project-URL: Homepage, https://github.com/1kbgz/spaday-codemirror
Author-email: 1kbgz <dev@1kbgz.com>
License: Apache-2.0
License-File: LICENSE
Classifier: Development Status :: 3 - Alpha
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Requires-Python: >=3.11
Requires-Dist: spaday<1.0,>=0.8.2
Provides-Extra: develop
Requires-Dist: build; extra == 'develop'
Requires-Dist: bump-my-version; extra == 'develop'
Requires-Dist: check-dist; extra == 'develop'
Requires-Dist: codespell; extra == 'develop'
Requires-Dist: hatch-js; extra == 'develop'
Requires-Dist: hatchling; extra == 'develop'
Requires-Dist: httpx; extra == 'develop'
Requires-Dist: mdformat; extra == 'develop'
Requires-Dist: mdformat-tables>=1; extra == 'develop'
Requires-Dist: pytest; extra == 'develop'
Requires-Dist: pytest-cov; extra == 'develop'
Requires-Dist: ruff; extra == 'develop'
Requires-Dist: starlette; extra == 'develop'
Requires-Dist: transports<0.9,>=0.8; extra == 'develop'
Requires-Dist: twine; extra == 'develop'
Requires-Dist: ty; extra == 'develop'
Requires-Dist: uv; extra == 'develop'
Requires-Dist: uvicorn; extra == 'develop'
Requires-Dist: websockets; extra == 'develop'
Requires-Dist: wheel; extra == 'develop'
Provides-Extra: examples
Requires-Dist: starlette; extra == 'examples'
Requires-Dist: transports<0.9,>=0.8; extra == 'examples'
Requires-Dist: uvicorn; extra == 'examples'
Requires-Dist: websockets; extra == 'examples'
Description-Content-Type: text/markdown

<a href="https://github.com/1kbgz/spaday-codemirror">
  <picture>
    <source media="(prefers-color-scheme: dark)" srcset="https://github.com/1kbgz/spaday-codemirror/raw/main/docs/img/logo-dark.webp?raw=true">
    <img alt="spaday-codemirror logo, a code editor inside a browser window" src="https://github.com/1kbgz/spaday-codemirror/raw/main/docs/img/logo-light.webp?raw=true" width="1200">
  </picture>
</a>

[CodeMirror 6](https://codemirror.net/) code editor for [spaday](https://github.com/1kbgz/spaday).

[![Build Status](https://github.com/1kbgz/spaday-codemirror/actions/workflows/build.yaml/badge.svg?branch=main&event=push)](https://github.com/1kbgz/spaday-codemirror/actions/workflows/build.yaml)
[![codecov](https://codecov.io/gh/1kbgz/spaday-codemirror/branch/main/graph/badge.svg)](https://codecov.io/gh/1kbgz/spaday-codemirror)
[![License](https://img.shields.io/github/license/1kbgz/spaday-codemirror)](https://github.com/1kbgz/spaday-codemirror)
[![PyPI](https://img.shields.io/pypi/v/spaday-codemirror.svg)](https://pypi.python.org/pypi/spaday-codemirror)

## Usage

`spaday-codemirror` provides one custom element, `<spaday-codemirror>`, and its typed spaday binding, `CodeMirror`. The package registers itself as the `codemirror` component package, so pages load its assets with `packages=["codemirror"]`.

```python
from spaday.backends.starlette import serve
from spaday_codemirror import CodeMirror


def page():
    return CodeMirror(doc="print('hello')\n", language="python", theme="dark", tab_size=4)


app = serve(page, packages=["codemirror"])
```

To send edits to Python, bind `editor-change` to an action, e.g. `.on("editor-change", SendPatch("editor", "doc", event_value("doc")))`, and route the `spaday:patch` intent to your model. [`example.py`](spaday_codemirror/example.py) does this over a websocket.

Without Python, load `cdn/index.js` and `css/index.css` from `spaday_codemirror/extension/` (or `js/dist/`) and use the tag directly:

```html
<spaday-codemirror language="json" tab_size="2"></spaday-codemirror>
```

### Properties

Attributes share the property names.

| Property       | Type                                                                  | Default   | Description                                                                        |
| -------------- | --------------------------------------------------------------------- | --------- | ---------------------------------------------------------------------------------- |
| `doc`          | `string`                                                              | `""`      | Editor contents                                                                    |
| `language`     | `"python"` \| `"javascript"` \| `"json"` \| `"markdown"` \| `"plain"` | `"plain"` | Syntax mode                                                                        |
| `theme`        | `"light"` \| `"dark"`                                                 | `"light"` | Color theme (`dark` uses One Dark)                                                 |
| `read_only`    | `boolean`                                                             | `false`   | Disallow user edits                                                                |
| `line_numbers` | `boolean`                                                             | `true`    | Show the line-number gutter                                                        |
| `tab_size`     | `number`                                                              | `4`       | Tab width and indent unit, in spaces                                               |
| `selection`    | `{anchor: number, head?: number} \| null`                             | `null`    | Main selection; property only. `head` defaults to `anchor`; `null` leaves it as-is |

Setting a property updates the existing `EditorView` in place, so focus and scroll position are kept. Property changes never emit events.

### Theming

Editor chrome follows spaday's shell palette. Component tokens can be set on the editor or any
ancestor; `spaday_codemirror.TOKENS` exposes the same names for Python `css()` calls.

| Token                                 | Controls                      | Shell fallback    |
| ------------------------------------- | ----------------------------- | ----------------- |
| `--spa-codemirror-surface`            | Editor background             | `--spa-surface`   |
| `--spa-codemirror-text`               | Editor and panel text         | `--spa-muted`     |
| `--spa-codemirror-gutter-surface`     | Gutter and panel background   | `--spa-surface-2` |
| `--spa-codemirror-gutter-text`        | Line numbers                  | `--spa-muted`     |
| `--spa-codemirror-border`             | Editor, gutter, panel borders | `--spa-border`    |
| `--spa-codemirror-focus`              | Focused editor border         | `--spa-accent`    |
| `--spa-codemirror-cursor`             | Caret and drop cursor         | `--spa-accent`    |
| `--spa-codemirror-selection`          | Selection background          | Theme default     |
| `--spa-codemirror-active-line`        | Active-line background        | Theme default     |
| `--spa-codemirror-active-line-gutter` | Active line-number background | Theme default     |

Syntax colors remain owned by CodeMirror's light and One Dark themes. For example,
`CodeMirror(...).css(spa_codemirror_surface="#111", spa_codemirror_text="#ddd")` changes one
editor without changing the rest of the application.

### Events

Both events bubble and are composed.

| Event              | `detail`                                                          | Fired when                                   |
| ------------------ | ----------------------------------------------------------------- | -------------------------------------------- |
| `editor-change`    | `{doc, changes: [{from, to, insert}], selection: {anchor, head}}` | The user edits the document                  |
| `editor-selection` | `{selection: {anchor, head}}`                                     | The user moves the selection without editing |

## Browser examples

- [Hosted Pyodide example](https://1kbgz.github.io/spaday-codemirror/lite/): the example page running in a Python Web Worker.
- [`spaday_codemirror/example.py`](spaday_codemirror/example.py): two editors wired to a transports-hosted model, plus a gallery covering every language and representative settings. Python computes metrics and a normalized preview on each edit, and has Normalize and Reset actions.
- [`js/examples/`](js/examples/): the Pyodide page and worker.

## Run the examples locally

```bash
make develop
make build
python -m spaday_codemirror.example  # http://127.0.0.1:8031/
```

Build and test the Pyodide example:

```bash
make test-pyodide-example
cd dist/lite && python -m http.server
```

> [!NOTE]
> This library was generated using [copier](https://copier.readthedocs.io/en/stable/) from the [Base Python Project Template repository](https://github.com/python-project-templates/base).
