Metadata-Version: 2.4
Name: deephaven-plugin-speed-reader
Version: 0.1.0
Summary: A Deephaven deephaven.ui element that displays text one word at a time (rapid serial visual presentation), with pivot-letter alignment and adjustable words-per-minute.
Author: mikebender
License-Expression: Apache-2.0
Project-URL: Homepage, https://github.com/mofojed/deephaven-plugin-speed-reader
Project-URL: Repository, https://github.com/mofojed/deephaven-plugin-speed-reader
Project-URL: Issues, https://github.com/mofojed/deephaven-plugin-speed-reader/issues
Keywords: deephaven,plugin,rsvp,speed-reading,ui
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Environment :: Plugins
Classifier: Operating System :: OS Independent
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: deephaven-core>=0.35.1
Requires-Dist: deephaven-plugin>=0.6.0
Requires-Dist: deephaven-plugin-utilities>=0.0.2
Requires-Dist: deephaven-plugin-ui>=0.41.0
Dynamic: license-file

# deephaven-plugin-speed-reader

A [deephaven.ui](https://github.com/deephaven/deephaven-plugins) element that reads text to you one
word at a time — rapid serial visual presentation (RSVP), the technique behind Spritz-style
readers.

![The speed reader playing a sentence one word at a time, with the pivot letter highlighted in red](./_assets/speed-reader.gif)

Because the words appear in a fixed position, your eyes never move between them. The highlighted
pivot letter marks the _optimal recognition point_, the spot the eye naturally focuses on, and it
stays put no matter how long the word is.

## Install

```sh
pip install deephaven-plugin-speed-reader
```

Restart the Deephaven server so it picks up the plugin.

## Usage

`speed_reader` takes a string and returns an element. There is no hook to call and nothing to
mount.

```python
from deephaven import ui

from deephaven_plugin_speed_reader import speed_reader


@ui.component
def my_panel():
    return speed_reader("The quick brown fox jumps over the lazy dog", default_wpm=400)


my_reader = my_panel()
```

## Options

`text` is the single positional argument; everything else is optional.

| Option          | Type              | Default | Notes                                                                        |
| --------------- | ----------------- | ------- | ---------------------------------------------------------------------------- |
| `text`          | `str`             | —       | The text to read out, split on whitespace.                                   |
| `wpm`           | `int`             | unset   | Words per minute, for _controlled_ use. When set, this wins over the slider. |
| `default_wpm`   | `int`             | `300`   | Starting words per minute, for _uncontrolled_ use.                           |
| `on_wpm_change` | `Callable[[int]]` | —       | Called with the new value whenever the speed control is adjusted.            |
| `auto_start`    | `bool`            | `True`  | Begin reading as soon as the component renders.                              |
| `loop`          | `bool`            | `False` | Restart from the first word after the last.                                  |
| `show_controls` | `bool`            | `True`  | Show the play/pause, restart and speed controls.                             |
| `on_complete`   | `Callable[[]]`    | —       | Called when the last word has been shown; with `loop`, once per pass.        |

Speed is clamped to 60–1200 wpm.

## Controlled vs uncontrolled speed

Follows the same convention as other deephaven.ui inputs.

**Uncontrolled** — the component owns the speed. Simplest, and the slider works on its own:

```python
speed_reader(passage, default_wpm=350)
```

**Controlled** — Python owns the speed, so anything can drive it. Pass `wpm` and update it from
`on_wpm_change`, or the slider will appear stuck:

```python
@ui.component
def my_panel():
    wpm, set_wpm = ui.use_state(300)
    return ui.flex(
        speed_reader(passage, wpm=wpm, on_wpm_change=set_wpm),
        ui.button("Speed read", on_press=lambda _e: set_wpm(800)),
        direction="column",
    )
```

## Reading text from a table

`speed_reader` takes a plain string, so any source works — including a table cell:

```python
@ui.component
def article_reader():
    body, set_body = ui.use_state("Click a row to start reading.")
    return ui.flex(
        ui.table(articles, on_row_press=lambda row: set_body(row["Body"]["value"])),
        speed_reader(body, default_wpm=400),
    )
```

New text resets the reader to the first word.

See [examples/](./examples) for complete, runnable panels.

## Contributing

See [AGENTS.md](./AGENTS.md) for the dev loop, project layout, and how to build, test, and lint.

## License

Apache-2.0
