Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
4a45117
build(deps): bump actions/checkout from 6 to 7 in /.github/workflows …
dependabot[bot] Jun 21, 2026
438d5b6
build(deps): bump actions/checkout from 6 to 7 in /.github/workflows …
dependabot[bot] Jun 28, 2026
dd7d485
fix: Subclassed StreamWindow from Window class to fix missing attributes
gopalparashar421 Jun 29, 2026
69a557c
fix: Added internal data helper for volume profile
gopalparashar421 Jun 29, 2026
b85dfe3
chore: Added updated bundle.js
gopalparashar421 Jun 29, 2026
ef528fd
feat: Enhance StreamChart with server readiness features
gopalparashar421 Jul 2, 2026
0e37e54
build(deps): bump actions/checkout from 6 to 7 in /.github/workflows …
dependabot[bot] Jul 8, 2026
27d1c29
feat: Implement show and show_async to follow AbstractChart API for S…
gopalparashar421 Jul 14, 2026
d138084
test: Updated test for StreamChart to follow the new API
gopalparashar421 Jul 14, 2026
705a147
fix: Added protocol for Websocket Shim and solve CSP policy
gopalparashar421 Jul 15, 2026
5b4c70c
test: Added test for updated stream chart shim
gopalparashar421 Jul 15, 2026
60a404d
fix: Added fix for inline CSP policy
gopalparashar421 Jul 15, 2026
a101ec6
ci: Added self-hosted runners
gopalparashar421 Jul 18, 2026
3455efb
docs: Updated docs with updated responsiveness changes and Legend usage
gopalparashar421 Jul 18, 2026
4af60df
feat: Added responsiveness add built scripts
gopalparashar421 Jul 18, 2026
db9d6d4
chore: Updated example for styling
gopalparashar421 Jul 18, 2026
4555168
docs: Updated changelog
gopalparashar421 Jul 18, 2026
3c7e277
build(deps): bump actions/checkout from 6 to 7 in /.github/workflows …
dependabot[bot] Jul 18, 2026
0cc1988
fix(util): preserve transient tags in BulkRunScript
gopalparashar421 Jul 19, 2026
c1acd32
fix(chart): tag data emits and add reconnect snapshots
gopalparashar421 Jul 19, 2026
27457d1
fix(stream): snapshot-on-connect instead of replay flood
gopalparashar421 Jul 19, 2026
9d06a70
fix(stream): reload page on established WS close
gopalparashar421 Jul 19, 2026
72d42e8
test(stream): cover reconnect snapshot behavior
gopalparashar421 Jul 19, 2026
9275d38
chore: bump version to 1.4.0
gopalparashar421 Jul 19, 2026
f240b95
docs: promote 1.4.0 changelog and StreamChart docs
gopalparashar421 Jul 19, 2026
8979546
fix: handle empty data cases and improve data subscription management
gopalparashar421 Jul 19, 2026
bcc7879
chore: Updated bundle.js to apply fixes for Emtpy data points
gopalparashar421 Jul 22, 2026
19e5386
Revert "ci: Added self-hosted runners"
gopalparashar421 Aug 12, 2026
e47a0db
feat(stream): enhance logging and improve snapshot handling
gopalparashar421 Sep 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
name: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7

- name: Install uv
uses: astral-sh/setup-uv@v7
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7

- name: Install uv
uses: astral-sh/setup-uv@v7
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7

- name: Install uv
uses: astral-sh/setup-uv@v7
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
should_publish: ${{ steps.check-pypi.outputs.should_publish }}
repository: ${{ steps.set-repo.outputs.repository }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7

- name: Install uv
uses: astral-sh/setup-uv@v7
Expand Down Expand Up @@ -102,7 +102,7 @@ jobs:
if: needs.version-check.outputs.should_publish == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7

- name: Install uv
uses: astral-sh/setup-uv@v7
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
python-version: ["3.10", "3.11", "3.12"]

steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7

- name: Install uv
uses: astral-sh/setup-uv@v7
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ pyrightconfig.json
working/
.zed/
uv.lock
/.cursor/agents
/.cursor/reference
/.cursor/skills
/.github/agents
/.github/reference
/.github/skills
Expand Down
35 changes: 35 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,41 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
---

## [Unreleased]

---

## [1.4.0] — 2026-07-19

### Added

- **Responsive chart chrome** — legend, top bar, subchart tabs, and HTML tables scale typography (and table padding) with the embedded window size via shared CSS `clamp()` tokens (`--chrome-font-*`). Designed for roughly 480×320–2560×1440 windows at 1x/2x HiDPI. No new Python sizing knobs.
- **`StreamChart.show_async(...)`** — asyncio-friendly server start that mirrors `Chart.show_async` (non-blocking `show` under the hood, then runs until cancelled).
- **`Window.run_script(..., transient=False)`** — additive keyword used by `StreamChart` to classify per-bar data scripts; ignored by the pywebview `Chart` path.

### Changed

- **`legend(..., font_size=...)` removed** (**breaking**) — both `AbstractChart.legend` and `SeriesCommon.legend` no longer accept `font_size`. Legend type size is owned by CSS; pass content and optional `color` / `font_family` only. `layout(font_size=...)` and `watermark(font_size=...)` are unchanged (canvas/LWC, not chrome).
- **Top bar and subchart tab overflow** — when widgets/tabs no longer fit at the responsive type-scale floor, the top bar and subchart tab bar scroll horizontally instead of clipping or compressing labels.
- **`stream-shim.js` reconnect** — after an established session closes, the page `location.reload()`s for a fresh JS context (avoids duplicate `Lib.Handler` construction). Close code `4002` uses a short backoff retry instead of reload; reload attempts are bounded via `sessionStorage`.

### Fixed

- **StreamChart reconnect flood** — while disconnected, per-bar `series.update` / `setData` / marker scripts are no longer buffered. On (re)connect the server replays a bounded structural script log, then emits a data snapshot from authoritative Python state (`candle_data` / series `data` / markers / tracked whitespace), so reopen-after-hours no longer replays thousands of updates and crash the page.
- **Candlestick snapshot** — main-series reconnect data is taken from `candle_data` (full OHLC), not the empty inherited `data` frame.
- **Snapshot/live cutover** — `_ws` is set only after the snapshot is fully sent under the data guard, so bars streamed during handshake are not lost.
- **`bulk_run` tagging** — transient vs structural tags are preserved through batched flushes; purely transient batches are dropped while disconnected.
- **StreamChart CSP** — Content-Security-Policy now allows WebSocket connections (`connect-src 'self' ws: wss:`) and inline styles (`style-src 'self' 'unsafe-inline'`), so live streaming and chrome styling work under the default CSP header.
- **StreamChart WebSocket scheme** — the browser shim picks `wss://` on HTTPS pages and `ws://` otherwise, instead of always using `ws://`.
- **`StreamWindow` inheritance** — subclasses `Window` so shared helpers (`_id_gen`, tables, style) stay in sync with `AbstractChart`.
- **Volume profile data helper** — internal data path aligned between Python and the TypeScript plugin.

### Notes

- **Snapshot boundary** — series data, markers, and whitespace from `append_whitespace` are snapshot-backed on reconnect. Other per-bar mutations (table cells, price lines, legend, PositionTool P&L) still buffer as structural and can grow if updated every bar.

---

## [1.3.0] — 2026-06-19

### Added
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ if __name__ == '__main__':
chart.crosshair(mode='normal', vert_color='#FFFFFF', vert_style='dotted',
horz_color='#FFFFFF', horz_style='dotted')

chart.legend(visible=True, font_size=14)
chart.legend(visible=True)

chart.set(df)

Expand Down Expand Up @@ -247,7 +247,7 @@ ___

### 7. StreamChart — browser-based chart over HTTP/WebSocket:

`StreamChart` spins up a local FastAPI/Uvicorn server and streams data to any browser tab. No desktop window is required — ideal for headless servers, notebooks, or remote development.
`StreamChart` spins up a local FastAPI/Uvicorn server and streams data to any browser tab. No desktop window is required — ideal for headless servers, notebooks, or remote development. On (re)connect it rebuilds from a data snapshot (not a backlog of every `update()` while the tab was closed).

```python
import pandas as pd
Expand Down
3 changes: 2 additions & 1 deletion docs/source/examples/subchart.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

Tabbed subcharts let you switch between multiple independent charts inside a
single desktop window. Call `create_subchart()` on the main chart; a tab bar
appears automatically above the charts on the first call.
appears automatically above the charts on the first call. When there are more
tabs than fit the window width, the tab bar scrolls horizontally.

> **Note:** This is separate from the **Panes API** (`pane_index`, `add_pane()`),
> which stacks multiple series vertically in one chart view. See
Expand Down
6 changes: 4 additions & 2 deletions docs/source/reference/abstract_chart.md
Original file line number Diff line number Diff line change
Expand Up @@ -319,9 +319,11 @@ ___



```{py:method} legend(visible: bool, ohlc: bool, percent: bool, lines: bool, color: COLOR, font_size: int, font_family: str, text: str, color_based_on_candle: bool)
```{py:method} legend(visible: bool, ohlc: bool, percent: bool, lines: bool, color: COLOR, font_family: str, text: str, color_based_on_candle: bool)

Configures the legend of the chart.

Legend type size is CSS-responsive (`clamp()` via shared chrome tokens). There is no Python `font_size` parameter — pass content and color/family only.
```
___

Expand Down Expand Up @@ -446,7 +448,7 @@ ___

Creates a tabbed sub-chart inside the same webview window and returns a {py:class}`SubChart` instance.

On the **first call**, a tab bar is injected above the charts and the main chart is assigned a tab labelled `main_label`. On subsequent calls `main_label` is ignored.
On the **first call**, a tab bar is injected above the charts and the main chart is assigned a tab labelled `main_label`. On subsequent calls `main_label` is ignored. When tab labels exceed the available width, the tab bar scrolls horizontally.

See the [Subcharts example](../examples/subchart.md).
```
Expand Down
3 changes: 2 additions & 1 deletion docs/source/reference/series.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,10 +191,11 @@ Shows a previously hidden series.
```
___

```{py:method} legend(visible, lines, color, font_size, font_family, text, pane_index)
```{py:method} legend(visible, lines, color, font_family, text, pane_index)

Configures the legend for the pane this series lives on.

Legend type size is CSS-responsive. There is no Python `font_size` parameter.
```
___

Expand Down
19 changes: 18 additions & 1 deletion docs/source/reference/stream_chart.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,35 @@ Open the URL in any browser. The token is a one-time secret — keep it private.
> **Tip:** the printed URL is a one-time CSRF-like token; do not expose it publicly.
> To open the browser automatically, pass `open_browser=True` to `show()`.

### Reconnect model

While disconnected, per-bar data scripts are dropped (not buffered). On connect the
server replays structural setup scripts, then a data snapshot from Python state
(series OHLC/values, markers, tracked whitespace). Same-page reconnects reload the
tab for a clean JavaScript context. See the [StreamChart tutorial](../tutorials/stream_chart.md)
for the snapshot boundary.

___


```{py:method} show(port: int, host: str, open_browser: bool, block: bool, cors_origins: list[str])
```{py:method} show(port: int, host: str, open_browser: bool, block: bool, cors_origins: list[str], debug: bool)

Starts the FastAPI/Uvicorn server and optionally opens the system browser.
Returns the chart URL once the server is accepting connections.

* `port` *(int, default 8080)*: TCP port to listen on.
* `host` *(str, default "127.0.0.1")*: Bind address. Use `"0.0.0.0"` for LAN access (a security reminder is printed).
* `open_browser` *(bool, default False)*: Open the default browser to the chart URL.
* `block` *(bool, default True)*: Block the calling thread until Ctrl+C is received.
* `cors_origins` *(list[str], optional)*: Additional allowed CORS origins (e.g. `"http://192.168.1.10:8080"`). `http://127.0.0.1` and `http://localhost` are always permitted.
* `debug` *(bool, default False)*: Enable FastAPI debug mode.

```

```{py:method} show_async(port: int, host: str, open_browser: bool, cors_origins: list[str], debug: bool)

Asyncio variant of `show`. Starts the server in a worker thread, returns the URL once
ready, then awaits until the task is cancelled or interrupted.

```

Expand Down
2 changes: 2 additions & 0 deletions docs/source/reference/tables.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ Tables are panes that can be used to gain further functionality from charts. The

The `Table` and `Row` objects act as dictionaries, and can be manipulated as such.

Table font size and header/cell padding scale responsively with the window via CSS (no Python `font_size` parameter). Colors, borders, text alignment, column widths, and sticky header positioning remain instance-driven as before.

`width`/`height`
: Either given as a percentage (a `float` between 0 and 1) or as an integer representing pixel size.

Expand Down
2 changes: 2 additions & 0 deletions docs/source/reference/topbar.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ Topbar widgets share common parameters:
* `name`: The name of the widget which can be used to access it from the `topbar` dictionary.
* `align`: The alignment of the widget (either `'left'` or `'right'` which determines which side of the topbar the widget will be placed upon.

Chrome typography (button, switcher, textbox, menu labels) scales responsively with the embedded window size via CSS `clamp()` tokens. There is no Python font-size API for the top bar. When left and right widgets exceed the available width, the top bar scrolls horizontally (widgets do not shrink or wrap).

___


Expand Down
37 changes: 36 additions & 1 deletion docs/source/tutorials/stream_chart.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,39 @@ if __name__ == '__main__':
time.sleep(0.1)
```

## Reconnect behavior

While no browser is connected, per-bar data scripts (`setData` / `update` / markers)
are **not** buffered. On (re)connect the server:

1. Replays a bounded **structural** script log (series creation, styling, drawings, …).
2. Emits a **data snapshot** from Python state (OHLC/volume, line series, markers,
and whitespace from `append_whitespace`).

Reopening a tab after hours of disconnected streaming therefore loads from current
state instead of replaying every bar update. Same-page reconnects reload the page
so structural replay always targets a fresh JavaScript context.

**Snapshot boundary:** table cells, price lines, legend text, and PositionTool P&L
updated every bar still append to the structural log and can grow if you mutate
them per bar while disconnected.

## Asyncio

Use `show_async` when the chart shares an event loop with other coroutines:

```python
import asyncio
from lightweight_charts import StreamChart

async def main():
chart = StreamChart()
chart.set(df)
await chart.show_async(port=8080, open_browser=True)

asyncio.run(main())
```

## Using plugins with StreamChart

All plugins work identically with `StreamChart`:
Expand All @@ -117,6 +150,8 @@ chart.show(block=True)
| Window | pywebview desktop window | Any browser via HTTP |
| Display | Requires display server | Headless compatible |
| Access URL | n/a | Printed one-time token URL |
| `show()` signature | `show(block)` | `show(port, host, open_browser, block)` |
| `show()` signature | `show(block)` | `show(port, host, open_browser, block, …)` |
| `show_async()` | Yes | Yes |
| Reconnect | n/a | Structural replay + data snapshot |
| Screenshot | `chart.screenshot()` | Not supported |
| `JupyterChart` | Yes | No |
2 changes: 1 addition & 1 deletion examples/5_styling/styling.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
horz_style="dotted",
)

chart.legend(visible=True, font_size=14)
chart.legend(visible=True)

chart.set(df)

Expand Down
2 changes: 1 addition & 1 deletion lightweight_charts/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = "1.3.0"
__version__ = "1.4.0"

from .abstract import (
AbstractChart,
Expand Down
Loading
Loading