Skip to content

Commit 0bfd101

Browse files
authored
Merge pull request #9 from deeleeramone/feature/tradingview
Feature/tradingview
2 parents 0ea0c6c + e2f11c2 commit 0bfd101

192 files changed

Lines changed: 65172 additions & 3031 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

pywry/AGENTS.md

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ Built on [PyTauri](https://pypi.org/project/pytauri/) (which uses Rust's [Tauri]
3535
- **Security**: Scoped token auth enabled by default, CSP headers, internal API protection, production presets available
3636
- **AgGrid Tables**: Best-in-class Pandas → AgGrid conversion with pre-wired events, context menus, and practical defaults
3737
- **Plotly Charts**: Plotly rendering with pre-wired plot events for Dash-like interactivity
38+
- **TradingView Charts**: TradingView Lightweight Charts integration with OHLCV normalization, multi-series, indicators, and toolbar-driven controls
3839
- **Toast Notifications**: Built-in alert system with positioning (info, success, warning, error, confirm)
3940
- **Marquee Ticker**: Scrolling text/content with dynamic updates
4041
- **Secrets Handling**: Secure password inputs — values stored server-side, never rendered in HTML
@@ -123,6 +124,8 @@ pywry/
123124
├── asset_loader.py # CSS/JS file loading with caching
124125
├── grid.py # AgGrid Pydantic models (ColDef, GridOptions, etc.)
125126
├── plotly_config.py # Plotly configuration models (PlotlyConfig, ModeBarButton, etc.)
127+
├── tvchart_config.py # TradingView chart config models (TVChartConfig, SeriesConfig, etc.)
128+
├── tvchart.py # OHLCV data normalization and toolbar factory
126129
├── toolbar.py # Toolbar component models (Button, Select, etc.)
127130
├── state_mixins.py # Widget state management mixins
128131
├── hot_reload.py # Hot reload manager
@@ -196,7 +199,7 @@ from pywry.grid import ColDef, ColGroupDef, DefaultColDef, RowSelection, GridOpt
196199
from pywry import GridStateMixin, PlotlyStateMixin, ToolbarStateMixin
197200

198201
# Inline functions (for notebooks)
199-
from pywry.inline import show_plotly, show_dataframe, block, stop_server
202+
from pywry.inline import show_plotly, show_dataframe, show_tvchart, block, stop_server
200203

201204
# Notebook detection
202205
from pywry import NotebookEnvironment, detect_notebook_environment, is_anywidget_available, should_use_inline_rendering
@@ -308,6 +311,22 @@ handle = app.show_dataframe(
308311
on_row_selected=None, # Row selection callback (notebook mode)
309312
server_side=False, # Use server-side mode for large datasets
310313
)
314+
315+
# Show TradingView Lightweight Chart
316+
handle = app.show_tvchart(
317+
data, # OHLCV DataFrame, list of dicts, or dict of lists
318+
title=None,
319+
width=None,
320+
height=None,
321+
callbacks=None,
322+
chart_options=None, # Chart-level options (layout, grid, etc.)
323+
series_options=None, # Series-specific options (colors, etc.)
324+
symbol_col=None, # Column name for multi-series grouping
325+
max_bars=10_000,
326+
toolbars=None,
327+
on_click=None,
328+
on_crosshair=None,
329+
)
311330
```
312331

313332
### HtmlContent Model

pywry/build_assets.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"https://cdn.jsdelivr.net/npm/ag-grid-community@35.0.0/dist/ag-grid-community.min.js"
1616
)
1717
AGGRID_CSS_BASE_URL = "https://cdn.jsdelivr.net/npm/ag-grid-community@35.0.0/styles"
18+
TVCHART_JS_URL = "https://cdn.jsdelivr.net/npm/lightweight-charts@5.1.0/dist/lightweight-charts.standalone.production.js"
1819

1920
# Asset directory
2021
ASSETS_DIR = Path(__file__).parent / "pywry" / "frontend" / "assets"
@@ -121,6 +122,16 @@ def download_aggrid_css() -> bool:
121122
return success
122123

123124

125+
def download_tvchart_js() -> bool:
126+
"""Download TradingView Lightweight Charts JS library."""
127+
dest = ASSETS_DIR / "lightweight-charts-5.1.0.standalone.production.js"
128+
gz_dest = Path(str(dest) + ".gz")
129+
if gz_dest.exists():
130+
print(f"Lightweight Charts JS already exists at {gz_dest}")
131+
return True
132+
return download_file(TVCHART_JS_URL, dest, "Lightweight Charts v5.1.0")
133+
134+
124135
def create_placeholder_files() -> None:
125136
"""Create placeholder files if downloads fail."""
126137
placeholder_files = [
@@ -130,6 +141,10 @@ def create_placeholder_files() -> None:
130141
b"// AG Grid placeholder - download failed\n",
131142
),
132143
("ag-grid-35.0.0.css.gz", b"/* AG Grid CSS placeholder - download failed */\n"),
144+
(
145+
"lightweight-charts-5.1.0.standalone.production.js.gz",
146+
b"// Lightweight Charts placeholder - download failed\n",
147+
),
133148
]
134149

135150
for filename, content in placeholder_files:
@@ -153,6 +168,7 @@ def download_all_assets() -> bool:
153168
download_plotly_js(),
154169
download_aggrid_js(),
155170
download_aggrid_css(),
171+
download_tvchart_js(),
156172
]
157173

158174
success = all(results)
@@ -176,6 +192,7 @@ def verify_assets() -> dict[str, bool]:
176192
"plotly-3.3.1.js.gz", # Full bundle with templates
177193
"ag-grid-community-35.0.0.min.js.gz",
178194
"ag-grid-35.0.0.css.gz",
195+
"lightweight-charts-5.1.0.standalone.production.js.gz",
179196
]
180197

181198
return {asset: (ASSETS_DIR / asset).exists() for asset in required_assets}

pywry/docs/docs/changelog.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## Unreleased
4+
5+
### New Features
6+
7+
- **Plotly dual-theme templates**`PlotlyConfig` now accepts `template_dark` and `template_light` dicts that are deep-merged on top of the built-in `plotly_dark` / `plotly_white` base templates. User overrides always win; un-set values inherit from the base. Both templates survive theme switches via `pywry:update-theme`.
8+
39
## Version 2.0.0
410

511
PyWry 2.0 is a major release with a completely redesigned architecture and API.
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ PyWry includes a first-class chat UI that can run in native windows, notebook wi
77

88
If you are building an interactive assistant, use `ChatManager` unless you explicitly need to assemble the raw chat HTML yourself.
99

10-
For the complete API surface, see the [Chat API](../reference/chat.md), [ChatManager API](../reference/chat-manager.md), and [Chat Providers API](../reference/chat-providers.md).
10+
For the complete API surface, see the [Chat API](../../reference/chat.md), [ChatManager API](../../reference/chat-manager.md), and [Chat Providers API](../../integrations/chat/chat-providers.md).
1111

1212
## Minimal ChatManager Setup
1313

@@ -276,7 +276,7 @@ This returns only the chat HTML structure. You are then responsible for wiring t
276276

277277
## Next Steps
278278

279-
- [Chat Artifacts And Providers](chat-artifacts.md)
280-
- [Chat API](../reference/chat.md)
281-
- [ChatManager API](../reference/chat-manager.md)
282-
- [Chat Providers API](../reference/chat-providers.md)
279+
- [Chat Artifacts And Providers](../../integrations/chat/index.md)
280+
- [Chat API](../../reference/chat.md)
281+
- [ChatManager API](../../reference/chat-manager.md)
282+
- [Chat Providers API](../../integrations/chat/chat-providers.md)

pywry/docs/docs/guides/content-assembly.md renamed to pywry/docs/docs/components/htmlcontent/content-assembly.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ This exact same assembly runs for all rendering paths — native, notebook, and
365365

366366
## Next Steps
367367

368-
- **[app.show()](app-show.md)** — The full `show()` method and its parameters
369-
- **[HtmlContent](html-content.md)** — Controlling CSS, JS, and data injection
370-
- **[Event System](events.md)** — How the JavaScript bridge communicates with Python
371-
- **[Configuration](configuration.md)** — Global settings, asset paths, and security
368+
- **[app.show()](../../guides/app-show.md)** — The full `show()` method and its parameters
369+
- **[HtmlContent](index.md)** — Controlling CSS, JS, and data injection
370+
- **[Event System](../../guides/events.md)** — How the JavaScript bridge communicates with Python
371+
- **[Configuration](../../guides/configuration.md)** — Global settings, asset paths, and security

pywry/docs/docs/guides/html-content.md renamed to pywry/docs/docs/components/htmlcontent/index.md

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,26 @@ app.show(content)
2626

2727
## Fields
2828

29-
| Field | Type | Default | Description |
30-
|---|---|---|---|
31-
| `html` | `str` | *(required)* | The HTML content to render — a fragment or a complete document |
32-
| `css_files` | `list[Path | str]` | `None` | Paths to CSS files to inject as `<style>` tags |
33-
| `script_files` | `list[Path | str]` | `None` | Paths to JavaScript files to inject as `<script>` tags |
34-
| `inline_css` | `str` | `None` | Raw CSS string injected as a `<style>` tag |
35-
| `json_data` | `dict` | `None` | Data injected as `window.json_data` in the page |
36-
| `init_script` | `str` | `None` | JavaScript code executed after all other scripts load |
37-
| `watch` | `bool` | `False` | Enable hot reload watching for `css_files` and `script_files` |
29+
`html`
30+
: **`str`** *(required)* — The HTML content to render — a fragment or a complete document.
31+
32+
`css_files`
33+
: **`list[Path | str]`** — Paths to CSS files to inject as `<style>` tags. Default: `None`.
34+
35+
`script_files`
36+
: **`list[Path | str]`** — Paths to JavaScript files to inject as `<script>` tags. Default: `None`.
37+
38+
`inline_css`
39+
: **`str`** — Raw CSS string injected as a `<style>` tag. Default: `None`.
40+
41+
`json_data`
42+
: **`dict`** — Data injected as `window.json_data` in the page. Default: `None`.
43+
44+
`init_script`
45+
: **`str`** — JavaScript code executed after all other scripts load. Default: `None`.
46+
47+
`watch`
48+
: **`bool`** — Enable hot reload watching for `css_files` and `script_files`. Default: `False`.
3849

3950
## HTML: Fragment vs. Complete Document
4051

@@ -203,7 +214,7 @@ You can also override `watch` at the `app.show()` level:
203214
app.show(content, watch=True)
204215
```
205216

206-
See the [Hot Reload guide](hot-reload.md) for the full implementation details.
217+
See the [Hot Reload guide](../../guides/hot-reload.md) for the full implementation details.
207218

208219
## Passing HtmlContent vs. Strings
209220

@@ -242,6 +253,6 @@ The toolbar HTML wraps `HtmlContent.html` at build time. See [Content Assembly](
242253
## Next Steps
243254

244255
- **[Content Assembly](content-assembly.md)** — How PyWry builds the final HTML document
245-
- **[app.show()](app-show.md)** — The full `show()` method reference
246-
- **[Hot Reload](hot-reload.md)** — Live CSS/JS updates during development
247-
- **[Theming & CSS](theming.md)** — Theme system and CSS variables
256+
- **[app.show()](../../guides/app-show.md)** — The full `show()` method reference
257+
- **[Hot Reload](../../guides/hot-reload.md)** — Live CSS/JS updates during development
258+
- **[Theming & CSS](../theming.md)** — Theme system and CSS variables

0 commit comments

Comments
 (0)