plotui 0.5.1__cp39-abi3-win_amd64.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.
- plotui/__init__.py +47 -0
- plotui/_bin/plotui.exe +0 -0
- plotui/_cli.py +33 -0
- plotui/_plotui.pyd +0 -0
- plotui/textual.py +772 -0
- plotui-0.5.1.dist-info/METADATA +288 -0
- plotui-0.5.1.dist-info/RECORD +11 -0
- plotui-0.5.1.dist-info/WHEEL +4 -0
- plotui-0.5.1.dist-info/entry_points.txt +2 -0
- plotui-0.5.1.dist-info/licenses/LICENSE +21 -0
- plotui-0.5.1.dist-info/sboms/plotui-py.cyclonedx.json +1390 -0
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: plotui
|
|
3
|
+
Version: 0.5.1
|
|
4
|
+
Requires-Dist: textual>=0.60 ; extra == 'textual'
|
|
5
|
+
Provides-Extra: textual
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Summary: Interactive 2D/3D plots in the terminal — Plotly-style, for Textual, powered by a Rust core and the Kitty graphics protocol.
|
|
8
|
+
Keywords: terminal,plot,3d,textual,kitty,tui,visualization
|
|
9
|
+
License: MIT
|
|
10
|
+
Requires-Python: >=3.9
|
|
11
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
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/step/bar,
|
|
25
|
+
> histogram, box, heatmap, and band charts with axes, ticks, titles,
|
|
26
|
+
> explicit ranges, log scales, categorical labels, a colorbar and a legend;
|
|
27
|
+
> DAG/pipeline graphs with a layered layout and a DOT subset reader; a 3D
|
|
28
|
+
> scatter/graph/surface/mesh engine; a Kitty-image raw demo; and a Textual
|
|
29
|
+
> widget.
|
|
30
|
+
|
|
31
|
+
## Architecture
|
|
32
|
+
|
|
33
|
+
The one rule that shapes everything: **the Rust core owns pixels, not the
|
|
34
|
+
terminal.** It has no event loop and no input handling — the TUI framework
|
|
35
|
+
(Textual, Ratatui, or Bubble Tea) owns the loop, forwards input to the
|
|
36
|
+
camera, and asks for a frame.
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
f64 data ─▶ camera + rasterizer ─▶ RGBA buffer ─▶ Kitty escape bytes ─▶ your terminal's cell grid
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
crates/
|
|
44
|
+
plotui-core/ pure engine: data model, 3D camera, rasterizer → RGBA
|
|
45
|
+
plotui-protocol/ RGBA → terminal bytes (Kitty graphics protocol)
|
|
46
|
+
plotui-term/ shared frontend glue: render-path detection, cell-pixel
|
|
47
|
+
probing, tmux passthrough, the per-frame render policy
|
|
48
|
+
plotui-bind/ shared binding semantics: parsing, validation, defaults,
|
|
49
|
+
and their exact error messages (Python and Go agree)
|
|
50
|
+
plotui-py/ PyO3 bindings → the `plotui._plotui` native module
|
|
51
|
+
plotui-ratatui/ Ratatui widget (native Rust frontend)
|
|
52
|
+
plotui-ffi/ C ABI (cdylib + staticlib) behind the Go bindings
|
|
53
|
+
python/plotui/ the Python package + Textual `PlotWidget`
|
|
54
|
+
go/ Go bindings + `teaplot`, the Bubble Tea v2 component
|
|
55
|
+
examples/ raw_demo.py (Kitty images), textual_demo.py
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`core` and `protocol` are pure and I/O-free, so the same engine can back every
|
|
59
|
+
frontend and be unit-tested by hashing pixel buffers. The protocol layer emits
|
|
60
|
+
Kitty graphics escapes — chunked, placement-aware, and wrapped for tmux
|
|
61
|
+
passthrough — as pure functions of the RGBA frame.
|
|
62
|
+
|
|
63
|
+
## Integrations
|
|
64
|
+
|
|
65
|
+
Each TUI framework gets a first-class widget, not a port. All frontends sit on
|
|
66
|
+
the same policy crates (`plotui-term` for detection/tmux/render policy,
|
|
67
|
+
`plotui-bind` for argument validation and its exact error strings), so a plot
|
|
68
|
+
looks and behaves identically whichever framework hosts it — down to the error
|
|
69
|
+
messages.
|
|
70
|
+
|
|
71
|
+
| Frontend | How it works | Where in the codebase | Try it |
|
|
72
|
+
| --- | --- | --- | --- |
|
|
73
|
+
| **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` |
|
|
74
|
+
| **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` |
|
|
75
|
+
| **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) |
|
|
76
|
+
| **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) |
|
|
77
|
+
|
|
78
|
+
The three TUI widgets have feature parity: render-path detection, tmux
|
|
79
|
+
passthrough, drag/zoom/pan/keys, picking + hover, the 2D crosshair, text
|
|
80
|
+
overlays, half-resolution interaction frames, and streaming extend.
|
|
81
|
+
|
|
82
|
+
## Install the CLI
|
|
83
|
+
|
|
84
|
+
`plotui` is also a command-line tool: pipe columns of numbers in, get a
|
|
85
|
+
real-pixel chart out — interactive on a TTY (pan, zoom, crosshair), a single
|
|
86
|
+
printed frame when piped or with `--static`.
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
curl -fsSL https://plotui.xyz/install.sh | sh # prebuilt binary
|
|
90
|
+
brew install sebaheg/tap/plotui # Homebrew (macOS / Linux)
|
|
91
|
+
pip install plotui # prebuilt wheel: the library + the CLI
|
|
92
|
+
cargo install plotui # build from source
|
|
93
|
+
cargo binstall plotui # prebuilt, via cargo-binstall
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
seq 1 100 | LC_ALL=C awk '{print $1, sin($1/10)}' | plotui line
|
|
98
|
+
plotui scatter -H -d, data.csv # header row + comma-delimited
|
|
99
|
+
plotui bar counts.tsv # --horizontal, --stack, --group
|
|
100
|
+
plotui step states.txt # holds its value between samples
|
|
101
|
+
plotui hist samples.txt # binned automatically
|
|
102
|
+
plotui box -H measurements.tsv # one box per column
|
|
103
|
+
plotui dag pipeline.dot # a DAG from a DOT file; hover a task
|
|
104
|
+
# to light everything it waits on
|
|
105
|
+
plotui line --log-y --title "queue depth" \
|
|
106
|
+
--x-title minute --y-title items # titles and log scales
|
|
107
|
+
plotui line --x-range 0:100 --y-range 0:1 # pin an extent, LO:HI
|
|
108
|
+
tail -f app.log | LC_ALL=C awk '{print $2}' \
|
|
109
|
+
| plotui line -f --window 200 # live, on the last 200 samples
|
|
110
|
+
plotui example scatter # built-in demo scenes, no data needed
|
|
111
|
+
plotui example deps # plotui's own dependency graph, laid
|
|
112
|
+
# out live by a force simulation
|
|
113
|
+
plotui example pipeline # a nightly forecast DAG, running
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
`--follow` (`-f`) keeps the reader open instead of plotting once at EOF: rows
|
|
117
|
+
are parsed as they arrive and appended in place, so the chart grows without a
|
|
118
|
+
redraw from scratch and pan/zoom survive. It needs piped input and a terminal
|
|
119
|
+
to draw into — a malformed line is skipped rather than fatal, and the count is
|
|
120
|
+
reported when you quit. The shape of the input (delimiter, column count,
|
|
121
|
+
whether x is a calendar) is settled by the first row and held for the rest of
|
|
122
|
+
the stream.
|
|
123
|
+
|
|
124
|
+
On a long feed the whole run compresses into a sliver, so `--window <N>` keeps
|
|
125
|
+
the view on the last N samples and `--last <span>` on the last `30s` / `5m` /
|
|
126
|
+
`2h` of x. Neither drops data: the window is a *view*, drawn on the range
|
|
127
|
+
slider (which a window switches on) against the entire run, so you can drag
|
|
128
|
+
back through everything that has arrived. Doing so hands the view over — a
|
|
129
|
+
reader who has scrolled back to an incident does not want the next row to
|
|
130
|
+
yank them forward — and **f** goes live again, jumping to the head.
|
|
131
|
+
|
|
132
|
+
Like every plotui frontend, the CLI needs a terminal with Kitty graphics
|
|
133
|
+
(supported terminals below); elsewhere it prints a notice and exits.
|
|
134
|
+
|
|
135
|
+
## Develop
|
|
136
|
+
|
|
137
|
+
Requires Rust and Python 3.9+. Build the native module into a virtualenv with
|
|
138
|
+
[maturin](https://www.maturin.rs/):
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
python -m venv .venv && source .venv/bin/activate
|
|
142
|
+
pip install maturin textual
|
|
143
|
+
maturin develop --release
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Then, in a terminal with Kitty graphics support — **Kitty**, **Ghostty**,
|
|
147
|
+
**iTerm2 ≥ 3.5**, **WezTerm**, or **Konsole** — for the full-resolution pixel demos:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
python examples/raw_demo.py # 3D scatter via Kitty images
|
|
151
|
+
python examples/textual_demo.py # embedded in Textual
|
|
152
|
+
python examples/textual_graph.py # interactive graph: hover + click-to-inspect
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
The Textual widget picks its render path per terminal: Unicode-placeholder
|
|
156
|
+
Kitty graphics in Kitty/Ghostty, direct Kitty placement in iTerm2/WezTerm/
|
|
157
|
+
Konsole — plus Warp, Rio, and VS Code, whose younger Kitty decoders are
|
|
158
|
+
supported but still maturing (VS Code needs its
|
|
159
|
+
`terminal.integrated.enableImages` setting). plotui only draws
|
|
160
|
+
real pixels — terminals without Kitty graphics get a notice naming supported
|
|
161
|
+
terminals, never a degraded plot. Override with
|
|
162
|
+
`PLOTUI_RENDER=placeholder|direct` or `PlotWidget(..., render_mode=...)`.
|
|
163
|
+
|
|
164
|
+
## Python API
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
from plotui import Plot
|
|
168
|
+
|
|
169
|
+
# 2D: axes, ticks, and a legend appear automatically. Traces added without a
|
|
170
|
+
# color take colorway slots in fixed order; `name=` puts a series in the
|
|
171
|
+
# legend. Colors accept (r, g, b) tuples or shorthands: "#e63c78", "red".
|
|
172
|
+
plot = Plot()
|
|
173
|
+
plot.add_line(xs, ys, name="forecast")
|
|
174
|
+
plot.add_scatter(xs2, ys2, name="observed")
|
|
175
|
+
plot.add_bar(xs3, heights)
|
|
176
|
+
|
|
177
|
+
# Secondary axes: axis="y2"/"y3" bind a series to an independent right-hand
|
|
178
|
+
# axis — its own autoscale and tick column, labels tinted to the series color
|
|
179
|
+
# (y2 innermost, y3 outermost). The grid stays with the left axis.
|
|
180
|
+
plot.add_line(xs, tokens, name="tokens", axis="y2")
|
|
181
|
+
plot.add_line(xs, cpu_minutes, name="cpu min", axis="y3")
|
|
182
|
+
|
|
183
|
+
# Titles: each buys its own margin, so the plot area shrinks rather than
|
|
184
|
+
# drawing over the data. The y title is drawn rotated in the left margin.
|
|
185
|
+
plot.set_title("p99 latency")
|
|
186
|
+
plot.set_x_title("requests")
|
|
187
|
+
plot.set_y_title("ms")
|
|
188
|
+
|
|
189
|
+
# Ranges and scales: an explicit range pins the *extent* only — no autoscale
|
|
190
|
+
# padding, and zoom/pan still compose on top of it (unlike set_x_window,
|
|
191
|
+
# which is the live window and supersedes the camera). A log axis ticks in
|
|
192
|
+
# powers of ten; values at or below zero have no log coordinate and neither
|
|
193
|
+
# set the range nor draw.
|
|
194
|
+
plot.set_x_range((0, 100)) # None restores autoscale
|
|
195
|
+
plot.set_y_range((0.1, 1e4))
|
|
196
|
+
plot.set_y_log(True) # set_x_log for x
|
|
197
|
+
|
|
198
|
+
# DAGs and pipelines: labelled boxes wired by arrows, laid out by rank. Node
|
|
199
|
+
# centres are data coordinates; each box is sized in pixels from its label,
|
|
200
|
+
# so zooming spreads the graph apart while the text stays readable.
|
|
201
|
+
from plotui import LayeredLayout, from_dot, reachable
|
|
202
|
+
|
|
203
|
+
layout = LayeredLayout(len(tasks), edges) # rankdir="TB" or "LR"
|
|
204
|
+
plot = Plot()
|
|
205
|
+
h = plot.add_graph2d(*layout.positions(), edges,
|
|
206
|
+
labels=tasks, routes=layout.routes())
|
|
207
|
+
plot.set_graph_colors(h, states) # repaint as the run advances
|
|
208
|
+
lit = reachable(len(tasks), edges, hovered) # everything it waits on
|
|
209
|
+
plot = from_dot(open("pipeline.dot").read()) # or straight from DOT
|
|
210
|
+
|
|
211
|
+
# 3D: any 3D trace switches the plot to the orbit camera.
|
|
212
|
+
plot = Plot()
|
|
213
|
+
plot.add_scatter3d(xs, ys, zs, name="Cluster A") # colors from the colorway
|
|
214
|
+
|
|
215
|
+
# Colorways: the default sequence is pink/cyan/orange-first; swap it with a
|
|
216
|
+
# built-in name or your own list before adding traces.
|
|
217
|
+
plot.set_colorway("vivid") # "plotui", "muted", "vivid"
|
|
218
|
+
plot.set_colorway(["#e63c78", "cyan", (240, 161, 60)])
|
|
219
|
+
|
|
220
|
+
# Streaming: every add_* returns a trace handle. Append through it instead
|
|
221
|
+
# of rebuilding — O(new points), autoscale follows; numpy arrays are read
|
|
222
|
+
# in one bulk copy. set_visible toggles a series without losing its handle,
|
|
223
|
+
# palette slot, or node indices.
|
|
224
|
+
h = plot.add_line([], [], name="loss")
|
|
225
|
+
plot.extend(h, xs, ys) # 3D scatter/line: extend(h, xs, ys, zs)
|
|
226
|
+
plot.set_visible(h, False)
|
|
227
|
+
|
|
228
|
+
# Interaction (forward your framework's events to these):
|
|
229
|
+
plot.rotate(d_yaw, d_pitch)
|
|
230
|
+
plot.zoom_by(factor)
|
|
231
|
+
plot.pan(dx, dy)
|
|
232
|
+
plot.reset()
|
|
233
|
+
|
|
234
|
+
# Render (the frontend places the bytes):
|
|
235
|
+
escape = plot.render_kitty(cols, rows, cell_w, cell_h) # Kitty pixel image
|
|
236
|
+
pixels = plot.render_rgba(px_w, px_h) # raw RGBA8 bytes
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Graphs take per-element styling, and the camera/projection state is fully
|
|
240
|
+
scriptable — the hooks a host needs for label overlays, camera targeting, and
|
|
241
|
+
rebuilding a plot without losing the view:
|
|
242
|
+
|
|
243
|
+
```python
|
|
244
|
+
plot.add_graph3d(xs, ys, zs, edges=[(0, 1), (1, 2)],
|
|
245
|
+
node_colors=[...], # one (r, g, b) per node
|
|
246
|
+
node_sizes=[...], # per-node radius (else `size`)
|
|
247
|
+
edge_colors=[...], # per-edge (r, g, b) (else derived)
|
|
248
|
+
node_shapes=[...]) # per-node "disc" | "ring" | "square" |
|
|
249
|
+
# "triangle" | "diamond" | "diamond-open" | "dot"
|
|
250
|
+
plot.set_show_box(False) # hide the 3D orientation cube
|
|
251
|
+
plot.set_bounds((x0, y0, z0), (x1, y1, z1)) # pin the data frame (else the nodes'
|
|
252
|
+
# bounding box); None, None restores
|
|
253
|
+
plot.set_chrome(grid=(26, 32, 36), # recolour the non-data chrome to sit on
|
|
254
|
+
frame=(43, 50, 55), # your own background: bg (legend box),
|
|
255
|
+
ink=(103, 111, 118)) # frame, grid, ink, ink_bright
|
|
256
|
+
|
|
257
|
+
state = plot.camera_state() # (yaw, pitch, zoom, pan_x, pan_y)
|
|
258
|
+
plot.set_camera_state(*state) # restore (e.g. onto a new Plot)
|
|
259
|
+
plot.project_nodes(px_w, px_h) # [(x_px, y_px, depth)] per node —
|
|
260
|
+
# exact render/pick geometry
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
In Textual, use `plotui.textual.PlotWidget(plot)` and it handles the event
|
|
264
|
+
plumbing for you. Pass `pickable=True` to make 3D graph nodes and edges
|
|
265
|
+
interactive: hovering lights the element under the cursor up white, and
|
|
266
|
+
clicking posts an `ElementPicked` message with `("node", i)` or `("edge", i)`
|
|
267
|
+
(see `examples/textual_graph.py`, which opens a slide-in inspector from it).
|
|
268
|
+
|
|
269
|
+
The widget also supports **text overlays** — `widget.set_overlay([(row, col,
|
|
270
|
+
text, style), ...])` splices terminal-crisp text (labels, badges) over the
|
|
271
|
+
image in every render mode without re-rasterizing — and exposes a
|
|
272
|
+
`widget.dragging` property for hosts that defer work mid-gesture. To customize
|
|
273
|
+
interaction in a subclass, override the `apply_rotate` / `apply_pan` /
|
|
274
|
+
`apply_zoom` / `apply_reset` / `on_click_at` primitives that every input path
|
|
275
|
+
routes through — do **not** override the Textual `on_*` handlers (Textual
|
|
276
|
+
dispatches those to every class in the MRO, so both would run).
|
|
277
|
+
|
|
278
|
+
## License
|
|
279
|
+
|
|
280
|
+
MIT, except for one embedded asset: chart text is set in
|
|
281
|
+
[Martian Mono](https://github.com/evilmartians/mono) (Copyright 2020 The
|
|
282
|
+
Martian Mono Project Authors), used under the SIL Open Font License 1.1. The
|
|
283
|
+
glyph outlines are compiled into `plotui-core` as
|
|
284
|
+
`crates/plotui-core/src/glyphs.rs`; the license travels with them in
|
|
285
|
+
`crates/plotui-core/fonts/MartianMono-OFL.txt`. The font carries no Reserved
|
|
286
|
+
Font Name, and nothing about the OFL reaches your code — it covers the font
|
|
287
|
+
data, not the crate.
|
|
288
|
+
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
plotui/__init__.py,sha256=Tp-dUbLiuRI39fm3BbENqjE1fokSouH53js2IvLfX3g,2088
|
|
2
|
+
plotui/_bin/plotui.exe,sha256=aR6FZbp0uQv7bhp_gWABSELbUa-7ZyuTbt2YK0arBvo,1940480
|
|
3
|
+
plotui/_cli.py,sha256=IQyM6Pm-xOa_irMWxD9Xx8mAzl0RBuGsOtzQsdd3cRU,1180
|
|
4
|
+
plotui/_plotui.pyd,sha256=gZmNABWJ8Jb6SwQUmoNNnuIjavpeoawhVGs1_t85R5Q,1317376
|
|
5
|
+
plotui/textual.py,sha256=ePhPYt6u17Q7YiYS0nK11UW4yuAFUcjA4GEGjOKqO20,34704
|
|
6
|
+
plotui-0.5.1.dist-info/METADATA,sha256=p6S6-buhB-4bTeSR64FxuEES83fZ__I7nr5yCW293Fs,15826
|
|
7
|
+
plotui-0.5.1.dist-info/WHEEL,sha256=xe4_tbg8wYdeh4O7nLbnWckzIQtCR4nODDgwke_TKy8,95
|
|
8
|
+
plotui-0.5.1.dist-info/entry_points.txt,sha256=omVtgXTZI-S8tlNy-u_BGA5z2fIKSy0HZINbkqxFgH4,42
|
|
9
|
+
plotui-0.5.1.dist-info/licenses/LICENSE,sha256=KAEbjdC1FAlj4nWDuDaxLrwtMCFHcxUlTkHalxb6nMI,1095
|
|
10
|
+
plotui-0.5.1.dist-info/sboms/plotui-py.cyclonedx.json,sha256=-y2s6Z079o-CNrxPVyWnc_CoOA3tOFKrGxVPjdgJKpA,43598
|
|
11
|
+
plotui-0.5.1.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sebastian Haglund
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|