Thank you for your interest in contributing to PFund-Plot! This guide will help you get started.
Development Setup¶
git clone https://github.com/PFund-Software-Ltd/pfund-plot.git
cd pfund-plot
uv sync --all-extras --all-groups
cd ui
pnpm install
How to develop a Svelte component¶
- create a .svelte file in ui/src/components/
- create a widget wrapper of it in ui/src/widgets/
- set PYTHON_ENV=development in .env
- run
pnpm dev
Fallback: if the above doesn’t work, you can watch and build the widget(s) automatically:
# {widget_name} is e.g. candlestick
WIDGET_TARGET={widget_name} pnpm build:widget:watch
# or build ALL widgets for convenience
pnpm build:widgets:watch
Adding a new plot¶
- if the plot is supported by hvplot, use hvplot to implement the plot
- if the plot is not supported by hvplot, use holoviews to implement the plot
- if holoviews doesn’t support the plot, use panel to implement the plot
Panel can be used as the standardized interface for different plotting libraries.
e.g. if you want to use plotly
import panel as pn
import plotly.graph_objects as go
from pfund_plot.renderer import render
# your plotly figure
fig = go.Figure(...)
# convert plotly figure to panel
panel_fig = pn.pane.Plotly(fig)
# pass the panel figure to the render function
render(panel_fig)
e.g. if you want to use altair
import panel as pn
import altair as alt
from pfund_plot.renderer import render
# your altair figure
fig = alt.Chart(...)
# convert altair figure to panel
panel_fig = pn.pane.Vega(fig)
# pass the panel figure to the render function
render(panel_fig)
The same logic can be applied to other plotting libraries as long as panel
supports them.
e.g. there are also pn.pane.Bokeh
, pn.pane.Matplotlib
, pn.pane.ECharts
, pn.pane.HoloViews
, etc.
Svelte + AnyWidget Integration¶
For advanced UI components not available in the Python ecosystem, we use Svelte components with AnyWidget integration:
Architecture Overview¶
pfund-plot/
├── ui/ # Frontend components
│ ├── src/
│ │ ├── components/ # Pure Svelte components
│ │ │ └── tradingview/
│ │ │ └── Candlestick.svelte
│ │ └── widgets/ # AnyWidget wrappers
│ │ └── tradingview/
│ │ └── candlestick/
│ │ ├── index.ts # AnyWidget export
│ │ └── CandlestickWrapper.svelte
├── vite.config.ts # SvelteKit web app & dashboards
└── vite.widget.config.ts # AnyWidget builds for notebooks
Component Development Workflow¶
Pure Svelte Components (
ui/src/components/
)- Build reusable UI components independent of AnyWidget
- Can be used in web apps via
pnpm dev
- Example:
Candlestick.svelte
takesdata
prop
AnyWidget Wrappers (
ui/src/widgets/
)- Thin wrappers that handle AnyWidget bindings
- Each widget folder contains:
index.ts
- exportsdefineWidget()
*Wrapper.svelte
- handlesbindings
prop from Python
Build Process
pnpm build:widget
- builds widgets for Jupyter/Marimo- Auto-discovers all
src/widgets/**/index.ts
files - Outputs standalone ES modules in
dist/
When to Use Svelte Components¶
- Complex interactive visualizations (TradingView charts, custom controls)
- JavaScript libraries not available in Python ecosystem
- Need for high-performance frontend interactions
- Custom UI components that benefit from reactive frameworks
Development Commands¶
cd ui/
pnpm dev # Develop components in web app
pnpm build:widget:watch # Build widgets with hot reload
pnpm build:widget # Production widget builds
Dataframe Manipulation¶
pfund-plot
supports pandas
, polars
and dask
dataframes. If you need to manipulate the input dataframe, please use the narwhals library.
Example¶
Your function will look something like this:
# GenericFrame is just a type alias for pandas, polars and dask dataframes
from pfeed.typing import GenericFrame
from typing import Literal
from pfund_plot.renderer import render
def your_plot(
data: GenericFrame,
streaming: bool = False,
display_mode: Literal['notebook', 'browser', 'desktop'] = "notebook",
raw_figure: bool = False, # add this if your function uses hvplot
):
# your plotting logic
...
return render(panel_fig)
For a full example, see the function candlestick_plot()
in pfund_plot/plots/candlestick.py
.
It is a standard way to make a plot using hvplot, and pn.state.add_periodic_callback() to make the plot streaming.