Skip to content

Commit e043300

Browse files
author
root
committed
Add Python API examples to web demo
1 parent 96f03c7 commit e043300

2 files changed

Lines changed: 74 additions & 3 deletions

File tree

app/page.jsx

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,90 +70,103 @@ const examples = [
7070
section: "Performance",
7171
title: "Million Point Line + Custom X + LOD",
7272
text: "Explicit x/y buffers, WASM min/max LOD, and callback-driven hover/click/selection inspection.",
73+
pythonCode: 'p = ip.Plot(width=1000, height=420, title="Line + LOD")\nh = p.line("signal", y, x=x)\np.on_hover(lambda plot, e: print(e["index"], e["x"], e["y"]))\np.on_select(lambda plot, e: plot.indices_for_selection(e, h))\np',
7374
code: 'const h = plot.line("signal", y, { x });\nplot.onHover(console.log);\nplot.onSelection((e) => plot.indicesForSelection(e, h));',
7475
},
7576
{
7677
id: "streaming-plot",
7778
section: "Performance",
7879
title: "Realtime Streaming Ring Buffer",
7980
text: "Append explicit x/y chunks into a fixed-capacity line without recreating the plot object.",
81+
pythonCode: 'p = ip.Plot(width=1000, height=360, title="Realtime")\nh = p.stream_line("ticks", capacity=12000, initial=y0, initial_x=x0, auto_render=True)\nh.append(chunk, x=chunk_x)\nh.pause(); h.resume()\np',
8082
code: 'const h = plot.streamLine("ticks", { capacity: 12000, x: initialX });\nh.append(chunk, { x: chunkX });\nh.pause(); h.resume();',
8183
},
8284
{
8385
id: "scatter-plot",
8486
section: "Points",
8587
title: "Scatter + Bubble Encodings",
8688
text: "Point-cloud rendering with explicit x/y data and bubble-size encodings for dense browser workflows.",
89+
pythonCode: 'p = ip.Plot(width=1000, height=420, title="Scatter + Bubbles")\np.scatter("samples", y, x=x, size=2.5)\np.bubbles("volume", y, sizes, x=x)\np',
8790
code: 'plot.scatter("samples", y, { x });\nplot.bubbles("volume", y, sizes, { x });',
8891
},
8992
{
9093
id: "curve-plot",
9194
section: "Curves",
9295
title: "Stairs, Stems, Digital, Shaded, Error Bars",
9396
text: "Signal-analysis overlays in one canvas: stepped series, impulses, bands, digital states, and uncertainty.",
97+
pythonCode: 'p = ip.Plot(width=1100, height=420, title="Signal Overlays")\np.stairs("step", y, x=x)\np.stems("stem", impulses, x=x)\np.digital("state", states, x=x)\np.shaded("band", lower, upper, x=x)\np.error_bars("fit", fit, err=err, x=x)\np',
9498
code: 'plot.stairs("step", y, { x });\nplot.shaded("band", lower, upper, { x });\nplot.errorBars("fit", y, { x, err });',
9599
},
96100
{
97101
id: "bars-plot",
98102
section: "Categorical",
99103
title: "Bars, Grouped Bars, Horizontal Bars",
100104
text: "Vertical bars, grouped categories, and horizontal rankings across ImPlot subplots.",
105+
pythonCode: 'sp = ip.Subplots(1, 3, width=1100, height=360, title="Bars")\nsp.subplot(0, 0).bars("sales", values)\nsp.subplot(0, 1).bar_groups(["A", "B", "C"], matrix)\nsp.subplot(0, 2).bars_h("rank", values)\nsp',
101106
code: 'plot.setSubplots(1, 3);\nplot.bars("sales", values);\nplot.barGroups(labels, matrix);\nplot.barsH("rank", values);',
102107
},
103108
{
104109
id: "distribution-plot",
105110
section: "Statistics",
106111
title: "Histogram + 2D Histogram",
107112
text: "1D and 2D distributions with colorbar-backed density inspection.",
113+
pythonCode: 'p = ip.Plot(width=1100, height=420, title="Distributions")\np.histogram("returns", values, bins=80)\np.histogram2d("density", x, y, x_bins=80, y_bins=60, show_colorbar=True)\np',
108114
code: 'plot.histogram("returns", values, { bins: 80 });\nplot.histogram2d("density", x, y, { xBins: 80, yBins: 60 });',
109115
},
110116
{
111117
id: "heatmap-image-plot",
112118
section: "Matrices",
113119
title: "Heatmap + Image",
114120
text: "Matrix and image plotting with empty heatmap labels, colorbar formatting, and float RGB buffers.",
121+
pythonCode: 'p = ip.Plot(width=1100, height=420, title="Heatmap + Image")\np.set_colormap("Viridis")\np.heatmap("z", matrix, label_fmt="", show_colorbar=True, colorbar_format="%.2f")\np.image("rgb", image, bounds=((0, 0), (cols, rows)))\np',
115122
code: 'plot.setColormap("Viridis");\nplot.heatmap("z", matrix, { rows, cols, labelFmt: "" });\nplot.image("rgb", image, { rows, cols, channels: 3 });',
116123
},
117124
{
118125
id: "overlays-plot",
119126
section: "Overlays",
120127
title: "Annotations, Tags, Text, Infinite Lines, Pie",
121128
text: "Thresholds, callouts, labels, tags, and pie-chart composition using ImPlot primitives.",
129+
pythonCode: 'p = ip.Plot(width=1100, height=420, title="Overlays")\np.vlines("events", xs)\np.hlines("limits", ys)\np.tag_y(0.0, label_fmt="zero")\np.annotation("peak", x0, y0)\np.pie_chart("mix", values, labels=labels, x=8, y=0, radius=1)\np',
122130
code: 'plot.vlines("events", xs);\nplot.tagY(0, { labelFmt: "zero" });\nplot.annotation("peak", x, y);\nplot.pieChart("mix", values, { labels });',
123131
},
124132
{
125133
id: "axes-plot",
126134
section: "Axes",
127135
title: "Axis Labels, Formats, Ticks, Log Scale, Secondary Axis",
128136
text: "Secondary axes, custom ticks, numeric formats, linked axes, and log/time scale controls.",
137+
pythonCode: 'p = ip.Plot(width=1100, height=420, title="Axes")\np.set_secondary_axes(y2=True)\np.set_axis_scale(x="linear", y="log")\np.set_axis_label("x1", "time")\np.set_axis_format("y1", "%.2e")\np.set_axis_ticks("x1", ticks, labels=labels)\np.line("primary", y, x=x)\np.line("secondary", y2, x=x, y_axis="y2")\np',
129138
code: 'plot.setSecondaryAxes({ y2: true });\nplot.setAxisScale({ x: "linear", y: "log" });\nplot.setAxisTicks("x1", ticks, { labels });',
130139
},
131140
{
132141
id: "subplots-plot",
133142
section: "Layout",
134143
title: "Linked Subplots + Crosshair",
135144
text: "A 2x2 ImPlot subplot grid with linked x-axis behavior and crosshair synchronization.",
145+
pythonCode: 'sp = ip.Subplots(2, 2, link_all_x=True, width=1100, height=650, title="Linked")\nsp.set_linked_crosshair("desk", axis="x")\nsp.subplot(0, 0).line("sin", y0, x=x)\nsp.subplot(0, 1).line("cos", y1, x=x)\nsp.subplot(1, 0).scatter("noise", y2, x=x)\nsp',
136146
code: 'plot.setSubplots(2, 2, { linkAllX: true });\nplot.setLinkedCrosshair("desk", { axis: "x" });\nplot.line("a", y, { x, subplotIndex: 0 });',
137147
},
138148
{
139149
id: "drag-plot",
140150
section: "Interaction",
141151
title: "Drag Lines, Drag Point, Drag Rect, Drag/Drop Targets",
142152
text: "Interactive ImPlot primitives for draggable guides, anchors, rectangles, and drop targets.",
153+
pythonCode: 'p = ip.Plot(width=1100, height=420, title="Drag Tools")\np.drag_line_x("cursor", 40)\np.drag_line_y("threshold", 0.5)\np.drag_point("anchor", 25, 0.5)\np.drag_rect("roi", 10, -1, 20, 1)\np.on_tool_change(lambda plot, event: print(event))\np',
143154
code: 'plot.dragLineX("cursor", 40);\nplot.dragPoint("anchor", 25, 0.5);\nplot.onInteraction(events => ...);',
144155
},
145156
{
146157
id: "colormap-plot",
147158
section: "Colormaps",
148159
title: "Colormap Widgets + Runtime Switching",
149160
text: "Selector, slider, and color button widgets that update heatmaps and colorbar primitives at runtime.",
161+
pythonCode: 'p = ip.Plot(width=1000, height=420, title="Colormaps")\np.set_colormap("Plasma")\np.heatmap("z", matrix, label_fmt="", show_colorbar=True)\np.colormap_selector(label="Choose map")\np.colormap_slider(label="Sample")\np.colormap_button(label="Active")\np',
150162
code: 'plot.setColormap("Plasma");\nplot.colormapSelector({ label: "Choose map" });\nplot.colormapSlider({ label: "Sample" });',
151163
},
152164
{
153165
id: "advanced-api-plot",
154166
section: "Advanced API",
155167
title: "State, Selection, Export",
156168
text: "View callbacks, state snapshots, PNG export, selection CSV, highlighting, constraints, links, and direct primitive access.",
169+
pythonCode: 'p = ip.Plot(width=1100, height=420, title="Advanced")\np.set_theme("nbimplot")\nh = p.line("signal", y, x=x)\nstate = p.get_state(include_data=True)\np.highlight_selection(selection, h)\ncsv = p.export_csv_selection(selection, h)\np.export_png("advanced.png")\np',
157170
code: 'plot.setTheme("nbimplot");\nconst state = plot.getState({ includeData: true });\nplot.highlightSelection(selection, h);\nconst csv = plot.exportCSVSelection(selection, h);\nawait plot.downloadPNG("advanced.png");',
158171
},
159172
];
@@ -170,6 +183,7 @@ export default function Page() {
170183
</a>
171184
<div className="topbar-links">
172185
<a href="#why">Architecture</a>
186+
<a href="#developer-api">Python API</a>
173187
<a href="#command-center">Demo Controls</a>
174188
<a href="#examples">Examples</a>
175189
<a href="https://pypi.org/project/nbimplot/">PyPI</a>
@@ -374,6 +388,7 @@ export default function Page() {
374388
<p>
375389
Each canvas is an independent WASM session. Scroll to lazy-load examples and
376390
verify lifecycle cleanup, interactions, subplots, colormaps, and exports.
391+
Every card shows the equivalent Python notebook API and direct web API.
377392
</p>
378393
</div>
379394
<div className="example-tabs" aria-label="Example categories">
@@ -393,7 +408,22 @@ export default function Page() {
393408
</div>
394409
<h2>{example.title}</h2>
395410
<p>{example.text}</p>
396-
<pre><code>{example.code}</code></pre>
411+
<div className="code-duo" aria-label={`${example.title} Python and web examples`}>
412+
<section className="code-pane">
413+
<div className="code-pane-top">
414+
<span>Python notebook</span>
415+
<strong>nbimplot</strong>
416+
</div>
417+
<pre><code>{example.pythonCode}</code></pre>
418+
</section>
419+
<section className="code-pane code-pane-web">
420+
<div className="code-pane-top">
421+
<span>Web app</span>
422+
<strong>@nbimplot/web</strong>
423+
</div>
424+
<pre><code>{example.code}</code></pre>
425+
</section>
426+
</div>
397427
</div>
398428
<div className="plot-frame">
399429
<div className="plot-frame-top">

app/styles.css

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -764,13 +764,54 @@ h3 {
764764
font-size: clamp(26px, 3vw, 42px);
765765
}
766766

767-
.example-copy pre {
767+
.code-duo {
768+
display: grid;
769+
gap: 10px;
770+
}
771+
772+
.code-pane {
768773
background: var(--ink);
769774
border: 1px solid rgb(255 255 255 / 0.08);
770775
border-radius: 16px;
776+
box-shadow: 0 16px 50px rgb(17 20 16 / 0.10);
771777
color: #f7efe1;
772778
overflow: hidden;
773-
padding: 14px;
779+
}
780+
781+
.code-pane-web {
782+
background: #13201d;
783+
}
784+
785+
.code-pane-top {
786+
align-items: center;
787+
border-bottom: 1px solid rgb(255 255 255 / 0.10);
788+
display: flex;
789+
gap: 12px;
790+
justify-content: space-between;
791+
padding: 10px 12px;
792+
}
793+
794+
.code-pane-top span {
795+
color: #81d8ca;
796+
font-family: "IBM Plex Mono", monospace;
797+
font-size: 10px;
798+
font-weight: 700;
799+
letter-spacing: 0.11em;
800+
text-transform: uppercase;
801+
}
802+
803+
.code-pane-top strong {
804+
color: rgb(244 239 229 / 0.64);
805+
font-family: "IBM Plex Mono", monospace;
806+
font-size: 10px;
807+
}
808+
809+
.code-pane pre {
810+
background: transparent;
811+
border: 0;
812+
color: #f7efe1;
813+
overflow: hidden;
814+
padding: 12px;
774815
}
775816

776817
.example-copy code {

0 commit comments

Comments
 (0)