plotui-cli 0.4.1__py3-none-macosx_11_0_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Binary file
@@ -0,0 +1,233 @@
1
+ Metadata-Version: 2.4
2
+ Name: plotui-cli
3
+ Version: 0.4.1
4
+ Summary: The plotui command-line tool: plot data from stdin or files as real terminal pixels (line, scatter, bar).
5
+ Keywords: terminal,plot,cli,kitty,tui,visualization
6
+ Home-Page: https://plotui.xyz
7
+ License: MIT
8
+ Requires-Python: >=3.9
9
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
10
+ Project-URL: Homepage, https://plotui.xyz
11
+ Project-URL: Repository, https://github.com/sebaheg/plotui
12
+
13
+ # plotui
14
+
15
+ **Interactive 2D/3D plots in the terminal — Plotly-style — for Textual, Ratatui, and Bubble Tea, powered by a Rust core and the Kitty graphics protocol.**
16
+
17
+ `plotui` renders scatter plots (and, soon, lines / surfaces / bars) as real
18
+ pixel graphics inside a terminal, and lets you rotate, pan, and zoom them. It
19
+ drops into a [Textual](https://textual.textualize.io/),
20
+ [Ratatui](https://ratatui.rs/), or
21
+ [Bubble Tea](https://github.com/charmbracelet/bubbletea) app as a first-class
22
+ widget, with the rendering engine written in Rust so it stays fast in 2D and 3D.
23
+
24
+ > Status: **early scaffold.** Working today: 2D scatter/line/bar charts with
25
+ > axes, ticks, and a legend; a 3D scatter/graph engine; a Kitty-image raw demo;
26
+ > and a Textual widget. See the roadmap below.
27
+
28
+ ## Architecture
29
+
30
+ The one rule that shapes everything: **the Rust core owns pixels, not the
31
+ terminal.** It has no event loop and no input handling — the TUI framework
32
+ (Textual, Ratatui, or Bubble Tea) owns the loop, forwards input to the
33
+ camera, and asks for a frame.
34
+
35
+ ```
36
+ crates/
37
+ plotui-core/ pure engine: data model, 3D camera, rasterizer → RGBA
38
+ plotui-protocol/ RGBA → terminal bytes (Kitty graphics protocol)
39
+ plotui-term/ shared frontend glue: render-path detection, cell-pixel
40
+ probing, tmux passthrough, the per-frame render policy
41
+ plotui-bind/ shared binding semantics: parsing, validation, defaults,
42
+ and their exact error messages (Python and Go agree)
43
+ plotui-py/ PyO3 bindings → the `plotui._plotui` native module
44
+ plotui-ratatui/ Ratatui widget (native Rust frontend)
45
+ plotui-ffi/ C ABI (cdylib + staticlib) behind the Go bindings
46
+ python/plotui/ the Python package + Textual `PlotWidget`
47
+ go/ Go bindings + `teaplot`, the Bubble Tea v2 component
48
+ examples/ raw_demo.py (Kitty images), textual_demo.py
49
+ ```
50
+
51
+ `core` and `protocol` are pure and I/O-free, so the same engine can back every
52
+ frontend and be unit-tested by hashing pixel buffers.
53
+
54
+ ## Integrations
55
+
56
+ Each TUI framework gets a first-class widget, not a port. All frontends sit on
57
+ the same policy crates (`plotui-term` for detection/tmux/render policy,
58
+ `plotui-bind` for argument validation and its exact error strings), so a plot
59
+ looks and behaves identically whichever framework hosts it — down to the error
60
+ messages.
61
+
62
+ | Frontend | How it works | Where in the codebase | Try it |
63
+ | --- | --- | --- | --- |
64
+ | **Textual** (Python) | `PlotWidget` wraps the `plotui._plotui` native module (PyO3). Mouse events route to the camera, hover/click picking arrives as Textual messages, `extend` streams points in-place, and text overlays splice into the image without re-rasterizing. | `python/plotui/textual.py`; native module in `crates/plotui-py` | `python examples/textual_graph.py` |
65
+ | **Ratatui** (Rust) | A native `StatefulWidget` plus an app-owned `PlotState`: hand it crossterm events, draw it like any other widget — frames and Kitty placement ride ratatui's own buffer diff, flicker-free. | `crates/plotui-ratatui` | `cargo run -p plotui-ratatui --example demo` |
66
+ | **Bubble Tea** (Go) | `teaplot.New(plot)` returns an Elm-style model: `Update` consumes tea mouse/key events, `View` lays out the cell grid, and image escapes leave as `tea.Raw` commands. Links to the Rust engine statically over the `plotui-ffi` C ABI (cgo). | `go/` (bindings) + `go/teaplot` (component); ABI in `crates/plotui-ffi` | `go run ./examples/demo` from `go/` — see [go/README.md](go/README.md) |
67
+ | **Browser** (WASM) | The same engine compiled to WebAssembly drives the live demos on the website: pointer events feed the engine's own camera, and every frame is its RGBA bytes blitted onto a canvas. Not a plotting-in-the-browser product — it exists so the site can show the real renderer. | `crates/plotui-wasm`; consumed by `site/` | [plotui.xyz/examples.html](https://plotui.xyz/examples.html) |
68
+
69
+ The three TUI widgets have feature parity: render-path detection, tmux
70
+ passthrough, drag/zoom/pan/keys, picking + hover, the 2D crosshair, text
71
+ overlays, half-resolution interaction frames, and streaming extend.
72
+
73
+ ## Install the CLI
74
+
75
+ `plotui` is also a command-line tool: pipe columns of numbers in, get a
76
+ real-pixel chart out — interactive on a TTY (pan, zoom, crosshair), a single
77
+ printed frame when piped or with `--static`.
78
+
79
+ ```bash
80
+ curl -fsSL https://plotui.xyz/install.sh | sh # prebuilt binary
81
+ brew install sebaheg/tap/plotui # Homebrew (macOS / Linux)
82
+ cargo install plotui # build from source
83
+ cargo binstall plotui # prebuilt, via cargo-binstall
84
+ ```
85
+
86
+ ```bash
87
+ seq 1 100 | awk '{print $1, sin($1/10)}' | plotui line
88
+ plotui scatter -H -d, data.csv # header row + comma-delimited
89
+ plotui bar counts.tsv
90
+ ```
91
+
92
+ Like every plotui frontend, the CLI needs a terminal with Kitty graphics
93
+ (supported terminals below); elsewhere it prints a notice and exits.
94
+
95
+ ## Develop
96
+
97
+ Requires Rust and Python 3.9+. Build the native module into a virtualenv with
98
+ [maturin](https://www.maturin.rs/):
99
+
100
+ ```bash
101
+ python -m venv .venv && source .venv/bin/activate
102
+ pip install maturin textual
103
+ maturin develop --release
104
+ ```
105
+
106
+ Then, in a terminal with Kitty graphics support — **Kitty**, **Ghostty**,
107
+ **iTerm2 ≥ 3.5**, **WezTerm**, or **Konsole** — for the full-resolution pixel demos:
108
+
109
+ ```bash
110
+ python examples/raw_demo.py # 3D scatter via Kitty images
111
+ python examples/textual_demo.py # embedded in Textual
112
+ python examples/textual_graph.py # interactive graph: hover + click-to-inspect
113
+ ```
114
+
115
+ The Textual widget picks its render path per terminal: Unicode-placeholder
116
+ Kitty graphics in Kitty/Ghostty, direct Kitty placement in iTerm2/WezTerm/
117
+ Konsole — plus Warp, Rio, and VS Code, whose younger Kitty decoders are
118
+ supported but still maturing (VS Code needs its
119
+ `terminal.integrated.enableImages` setting). plotui only draws
120
+ real pixels — terminals without Kitty graphics get a notice naming supported
121
+ terminals, never a degraded plot. Override with
122
+ `PLOTUI_RENDER=placeholder|direct` or `PlotWidget(..., render_mode=...)`.
123
+
124
+ ## Python API
125
+
126
+ ```python
127
+ from plotui import Plot
128
+
129
+ # 2D: axes, ticks, and a legend appear automatically. Traces added without a
130
+ # color take palette slots in fixed order; `name=` puts a series in the legend.
131
+ plot = Plot()
132
+ plot.add_line(xs, ys, name="forecast")
133
+ plot.add_scatter(xs2, ys2, name="observed")
134
+ plot.add_bar(xs3, heights)
135
+
136
+ # Secondary axes: axis="y2"/"y3" bind a series to an independent right-hand
137
+ # axis — its own autoscale and tick column, labels tinted to the series color
138
+ # (y2 innermost, y3 outermost). The grid stays with the left axis.
139
+ plot.add_line(xs, tokens, name="tokens", axis="y2")
140
+ plot.add_line(xs, cpu_minutes, name="cpu min", axis="y3")
141
+
142
+ # 3D: any 3D trace switches the plot to the orbit camera.
143
+ plot = Plot()
144
+ plot.add_scatter3d(xs, ys, zs, color=(230, 60, 120), size=2.0)
145
+
146
+ # Streaming: every add_* returns a trace handle. Append through it instead
147
+ # of rebuilding — O(new points), autoscale follows; numpy arrays are read
148
+ # in one bulk copy. set_visible toggles a series without losing its handle,
149
+ # palette slot, or node indices.
150
+ h = plot.add_line([], [], name="loss")
151
+ plot.extend(h, xs, ys) # 3D scatter/line: extend(h, xs, ys, zs)
152
+ plot.set_visible(h, False)
153
+
154
+ # Interaction (forward your framework's events to these):
155
+ plot.rotate(d_yaw, d_pitch)
156
+ plot.zoom_by(factor)
157
+ plot.pan(dx, dy)
158
+ plot.reset()
159
+
160
+ # Render (the frontend places the bytes):
161
+ escape = plot.render_kitty(cols, rows, cell_w, cell_h) # Kitty pixel image
162
+ pixels = plot.render_rgba(px_w, px_h) # raw RGBA8 bytes
163
+ ```
164
+
165
+ Graphs take per-element styling, and the camera/projection state is fully
166
+ scriptable — the hooks a host needs for label overlays, camera targeting, and
167
+ rebuilding a plot without losing the view:
168
+
169
+ ```python
170
+ plot.add_graph3d(xs, ys, zs, edges=[(0, 1), (1, 2)],
171
+ node_colors=[...], # one (r, g, b) per node
172
+ node_sizes=[...], # per-node radius (else `size`)
173
+ edge_colors=[...], # per-edge (r, g, b) (else derived)
174
+ node_shapes=[...]) # per-node "disc" | "ring" | "square" |
175
+ # "triangle" | "diamond" | "diamond-open" | "dot"
176
+ plot.set_show_box(False) # hide the 3D orientation cube
177
+ plot.set_bounds((x0, y0, z0), (x1, y1, z1)) # pin the data frame (else the nodes'
178
+ # bounding box); None, None restores
179
+ plot.set_chrome(grid=(26, 32, 36), # recolour the non-data chrome to sit on
180
+ frame=(43, 50, 55), # your own background: bg (legend box),
181
+ ink=(103, 111, 118)) # frame, grid, ink, ink_bright
182
+
183
+ state = plot.camera_state() # (yaw, pitch, zoom, pan_x, pan_y)
184
+ plot.set_camera_state(*state) # restore (e.g. onto a new Plot)
185
+ plot.project_nodes(px_w, px_h) # [(x_px, y_px, depth)] per node —
186
+ # exact render/pick geometry
187
+ ```
188
+
189
+ In Textual, use `plotui.textual.PlotWidget(plot)` and it handles the event
190
+ plumbing for you. Pass `pickable=True` to make 3D graph nodes and edges
191
+ interactive: hovering lights the element under the cursor up white, and
192
+ clicking posts an `ElementPicked` message with `("node", i)` or `("edge", i)`
193
+ (see `examples/textual_graph.py`, which opens a slide-in inspector from it).
194
+
195
+ The widget also supports **text overlays** — `widget.set_overlay([(row, col,
196
+ text, style), ...])` splices terminal-crisp text (labels, badges) over the
197
+ image in every render mode without re-rasterizing — and exposes a
198
+ `widget.dragging` property for hosts that defer work mid-gesture. To customize
199
+ interaction in a subclass, override the `apply_rotate` / `apply_pan` /
200
+ `apply_zoom` / `apply_reset` / `on_click_at` primitives that every input path
201
+ routes through — do **not** override the Textual `on_*` handlers (Textual
202
+ dispatches those to every class in the MRO, so both would run).
203
+
204
+ ## Roadmap
205
+
206
+ - [x] Flicker-free Kitty placement via Unicode-placeholder virtual placement
207
+ (fixed image id, atomic replace) — wire the pixel path into the Textual widget
208
+ - [x] 2D traces: scatter, line, bar; axes, ticks, tick labels, legend
209
+ - [x] Independent right-hand y-axes (`axis="y2"`/`"y3"`) with tinted tick labels
210
+ - [ ] 2D step trace; axis titles; time-formatted x ticks
211
+ - [ ] 3D surface / mesh; axis cube with labels
212
+ - [x] Interactive hover / pick for 3D graph nodes *and* edges (opt-in via
213
+ `PlotWidget(..., pickable=True)`: hover lights the element up white,
214
+ click posts `ElementPicked`)
215
+ - [ ] Hover / pick for 2D traces; spatial index for large graphs
216
+ - [x] Streaming append: trace handles, `extend`, `set_visible`, incremental bounds
217
+ - [x] numpy fast-path input (one bulk copy, no per-element conversion)
218
+ - [ ] Rolling window (`max_points`) for endless streams
219
+ - [x] Graceful render-path auto-detection (placeholder / direct Kitty, with a
220
+ supported-terminals notice elsewhere and a `PLOTUI_RENDER` override)
221
+ - [ ] Sixel + iTerm2 OSC 1337 encoders for terminals without Kitty graphics
222
+ - [ ] Prebuilt wheels (maturin + cibuildwheel)
223
+ - [x] Ratatui frontend (native): `plotui-ratatui` — StatefulWidget + app-owned
224
+ PlotState, full parity with the Textual widget
225
+ (`cargo run -p plotui-ratatui --example demo`)
226
+ - [x] Bubble Tea frontend (cgo): `go/` bindings over the `plotui-ffi` C ABI +
227
+ the `teaplot` component for Bubble Tea v2 (see `go/README.md`)
228
+ - [ ] Prebuilt static libs for the Go bindings (today: local source build)
229
+
230
+ ## License
231
+
232
+ MIT
233
+
@@ -0,0 +1,5 @@
1
+ plotui_cli-0.4.1.data/scripts/plotui,sha256=HMqLFakkATvmSV_bdL_PIzGEVLauyURF_UnYRBK3xzI,1459504
2
+ plotui_cli-0.4.1.dist-info/METADATA,sha256=khF5WNVDjtj-BUoMMwJI-pdPpolDF8bbWq2mk2iHJe8,12203
3
+ plotui_cli-0.4.1.dist-info/WHEEL,sha256=U927pSLvWHh5eaqcN5v72CdMm-HUWF344fKLe8HhSOU,102
4
+ plotui_cli-0.4.1.dist-info/sboms/plotui.cyclonedx.json,sha256=VhxcpBbKGCv7ByCbdO0kQMLL8xyqdPnf5CpUHQ4MmaE,204186
5
+ plotui_cli-0.4.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: maturin (1.15.0)
3
+ Root-Is-Purelib: false
4
+ Tag: py3-none-macosx_11_0_arm64