Adaptive resize with a complicated layout

I have a complicated layout that I’m having a hard time figuring out the correct sizing modes to use. The layout I want is:

  • Larger plot on the left, as big as possible
  • Tabs layout on the right, takes up about 1/4 the horizontal space
    • There is one panel with a few plots, and another with some widgets

Here is the code:

# filename: main.py
import numpy as np

from bokeh.layouts import column, layout
from bokeh.models import Slider
from bokeh.plotting import figure
from bokeh.models.widgets import Panel, Tabs

from bokeh.io import curdoc

tools = 'pan'


def make_main_plot(sizing_mode, width, height):
    # Define Bollinger Bands.
    upperband = np.random.randint(100, 150 + 1, size=100)
    lowerband = upperband - 100
    x_data = np.arange(1, 101)

    # Bollinger shading glyph:
    band_x = np.append(x_data, x_data[::-1])
    band_y = np.append(lowerband, upperband[::-1])

    p = figure(x_axis_type='datetime', tools=tools)
    p.patch(band_x, band_y, color='#7570B3', fill_alpha=0.2)

    p.title.text = 'Bollinger Bands'
    p.title_location = 'left'
    p.title.align = 'left'
    p.grid.grid_line_alpha = 0.4

    p.sizing_mode = sizing_mode
    p.plot_width = width
    p.plot_height = height
    return p


def make_sliders(sizing_mode, width, height):

    kwargs = dict(
        start=0,
        end=10,
        value=3,
        step=1,
        sizing_mode=sizing_mode,
        width=width,
        height=height,
    )

    amp_slider = Slider(title="Amplitude", **kwargs)
    freq_slider = Slider(title="Frequency", **kwargs)
    phase_slider = Slider(title="Phase", **kwargs)
    offset_slider = Slider(title="Offset", **kwargs)

    widgets = column(
        amp_slider,
        freq_slider,
        phase_slider,
        offset_slider,
        sizing_mode=sizing_mode,
        width=width,
        height=height,
    )
    return widgets


def make_scatters(sizing_mode, width, height):
    N = 100
    x = np.linspace(0, 4 * np.pi, N)
    y1 = np.sin(x)
    y2 = np.cos(x)

    s1 = figure(tools=tools)
    s1.circle(x, y1, color="navy", size=8, alpha=0.5)
    s2 = figure(tools=tools, x_range=s1.x_range, y_range=s1.y_range)
    s2.circle(x, y2, color="firebrick", size=8, alpha=0.5)

    s1.plot_height = height // 2
    s2.plot_height = height // 2
    s1.plot_width = width
    s2.plot_width = width

    s = column(s1, s2, sizing_mode=sizing_mode, width=width, height=height)

    return s


# sizing values
overall_mode = 'stretch_both'

tabs_w = 400
tabs_h = 700
tabs_mode = 'fixed'

main_w = 700
main_h = 700
main_mode = 'stretch_both'

sliders_w = tabs_w - 10
sliders_h = tabs_h // 10
sliders_mode = 'scale_width'

scatters_w = tabs_w
scatters_h = tabs_h - 10
scatters_mode = 'scale_height'


# make plots
main_plot = make_main_plot(main_mode, main_w, main_h)
sliders = make_sliders(sliders_mode, sliders_w, sliders_h)
scatters = make_scatters(scatters_mode, scatters_w, scatters_h)

# create layout
scatter_tab = Panel(child=scatters, title='Scatter')
inputs_tab = Panel(child=sliders, title='Inputs')

tabs = Tabs(
    tabs=[scatter_tab, inputs_tab],
    sizing_mode=tabs_mode,
    width=tabs_w,
    height=tabs_h,
)

page_layout = layout(
    [[main_plot, tabs]],
    sizing_mode=overall_mode
)

curdoc().add_root(page_layout)
curdoc().title = "Layout Testing"

This is run using bokeh serve.

With the width/height/mode settings in the example I get adaptive resizing for the width, but not the height. Is this possible or do I need to turn to custom CSS to get the layout I want?