Claude code gen + Cursor to the rescue! Here’s some Claude generated code that I fixed with Cursor. This does more or less what I want it to do.
import numpy as np
from bokeh.plotting import figure, show
from bokeh.layouts import column
from bokeh.models import RangeTool, CustomJS
from bokeh.io import curdoc
# Generate sample data
N = 300
x = np.linspace(0, 4*np.pi, N)
y = np.sin(x) + 0.1*np.random.randn(N)
y2 = np.cos(x) + 0.1*np.random.randn(N)
# Create the main plot
main_plot = figure(
width=800,
height=400,
title="Main Plot - Zoomed View",
tools="pan,wheel_zoom,box_zoom,reset,save"
)
# Add line renderers to main plot
main_line1 = main_plot.line(x, y, line_width=2, color='blue', alpha=0.8, legend_label="sin(x)")
main_line2 = main_plot.line(x, y2, line_width=2, color='red', alpha=0.8, legend_label="cos(x)")
main_plot.legend.click_policy = "hide"
# Create the overview plot (smaller)
overview_plot = figure(
width=800,
height=200,
title="Overview Plot - Use RangeTool to select range",
tools="",
toolbar_location=None
)
# Add the same data to overview plot
overview_plot.line(x, y, line_width=1, color='blue', alpha=0.6)
overview_plot.line(x, y2, line_width=1, color='red', alpha=0.6)
# Define the constant range width
RANGE_WIDTH = 2.0 # This is the constant width we want to maintain
# Calculate initial range
initial_start = np.pi
initial_end = initial_start + RANGE_WIDTH
# Create RangeTool with initial range
range_tool = RangeTool(
x_range=main_plot.x_range
)
# Add the range tool to overview plot
overview_plot.add_tools(range_tool)
overview_plot.toolbar.active_multi = range_tool
# JavaScript callback to enforce constant width
callback_code = f"""
const range_width = {RANGE_WIDTH};
const x_range = cb_obj;
// Get current start and end
let start = x_range.start;
let end = x_range.end;
let current_width = end - start;
// Only adjust if the width has changed significantly
if (Math.abs(current_width - range_width) > 0.001) {{
// Determine which end moved more and adjust accordingly
let center = (start + end) / 2;
let new_start = center - range_width / 2;
let new_end = center + range_width / 2;
// Update the range while avoiding infinite recursion
x_range.setv({{
'start': new_start,
'end': new_end
}});
}}
"""
# Add the callback to the main plot's x_range
main_plot.x_range.js_on_change('start', CustomJS(code=callback_code))
main_plot.x_range.js_on_change('end', CustomJS(code=callback_code))
# Set initial range on main plot
main_plot.x_range.start = initial_start
main_plot.x_range.end = initial_end
# Create layout
layout = column(main_plot, overview_plot)
# Alternative method using Python callback (requires bokeh serve)
def python_callback(attr, old, new):
"""Python callback to enforce constant width - requires bokeh serve"""
current_start = main_plot.x_range.start
current_end = main_plot.x_range.end
current_width = current_end - current_start
if abs(current_width - RANGE_WIDTH) > 0.001:
center = (current_start + current_end) / 2
main_plot.x_range.start = center - RANGE_WIDTH / 2
main_plot.x_range.end = center + RANGE_WIDTH / 2
# Uncomment these lines if using with bokeh serve
# main_plot.x_range.on_change('start', python_callback)
# main_plot.x_range.on_change('end', python_callback)
# Show the plot
show(layout)
# If running with bokeh serve, use this instead:
# curdoc().add_root(layout)
print("Bokeh RangeTool Demo with Constant Width")
print(f"Range width is fixed at: {RANGE_WIDTH}")
print("\nTo run this demo:")
print("1. Save as 'rangetool_demo.py'")
print("2. Run: python rangetool_demo.py (opens in browser)")
print("3. Or run: bokeh serve rangetool_demo.py (for live server with Python callbacks)")
print("\nTry dragging the range selector in the overview plot!")
print("The selection will maintain a constant width as you move it around.")