Skip to content

NicePoolWebView

NicePoolWebView is the Python/NiceGUI adapter for the shared @mapmanager/nicepool browser component. Python callers work with pandas, typed dictionaries, callbacks, and row IDs; they do not convert data to JSON or call JavaScript.

Small example

import pandas as pd
from nicegui import ui

from nicewidgets.nicepool_web_view import NicePoolSelectionDict, NicePoolWebView

frame = pd.DataFrame({
    'row_id': ['row-1', 'row-2', 'row-3'],
    'condition': ['control', 'treated', 'treated'],
    'velocity': [-1.2, 0.8, 2.4],
})

def selection_changed(selection: NicePoolSelectionDict) -> None:
    print(selection['selectedRowIds'])

view = NicePoolWebView(
    frame,
    row_id_column='row_id',
    on_selection=selection_changed,
)

ui.run()

The runnable version is examples/nicepool_web_view/demo_app.py.

DataFrame requirements

  • Column names must be unique, nonempty strings.
  • Cells may contain strings, finite numbers, booleans, or missing values.
  • Missing pandas values are preserved as missing values in the browser table.
  • The named row-ID column must exist.
  • Every row ID must be nonmissing and unique after conversion to a string.
  • IDs received by selection callbacks are strings from that row-ID column.
  • Calling set_data() completely resets selection, filters, and plot state.

Arrays, nested objects, timestamps, arbitrary Python objects, infinities, and duplicate column names are rejected before reaching the browser.

Controlling the view

Browser communication is asynchronous:

await view.set_data(new_frame)

state = await view.get_state()
state['layout'] = '1x2'
await view.set_state(state)

plot_state = await view.get_plot_state()
plot_state['pointSize'] = 10
await view.set_plot_state(plot_state)

await view.set_selection(['row-1', 'row-3'], primary_row_id='row-1')
await view.clear_selection()

get_state() returns a NicePoolStateDict: schema version, layout, active plot index, and exactly four PlotStateDict entries. Inactive plot slots remain in the state. get_plot_state() and set_plot_state() operate on the active plot unless a zero-based plot_index is supplied.

Selection callbacks

on_selection receives a NicePoolSelectionDict:

{
    'primaryRowId': 'row-1',
    'selectedRowIds': ['row-1', 'row-3'],
}

primaryRowId may be None. selectedRowIds contains unique stable IDs from the current DataFrame.

Integration notes

  • Give the view a stable height. The constructor defaults to 70vh; use its height argument when the host layout requires another size.
  • Do not serve or register NicePool JavaScript yourself. The adapter serves its bundled, matching assets.
  • Rebuild and synchronize the bundled assets whenever the NicePool package is updated; stale browser files can make the Python and browser APIs disagree.
  • The adapter cache-busts its entry module so a restarted development app picks up newly bundled files.
  • Theme ownership belongs to the host application. Call set_theme() when the application's light/dark theme changes.

NiceWidgets maintainers update the bundled browser files from a sibling mapmanager-web-components checkout with:

uv run python scripts/nicepool_web_view/sync_web_assets.py

API reference

Embed NicePool in NiceGUI while keeping browser details private.

The view owns one authoritative pandas table. Constructing it schedules the initial table replacement after the page is connected. Public control methods are asynchronous because they cross the Python/browser boundary.

Parameters:

Name Type Description Default
dataframe DataFrame

Initial authoritative table. Cells may contain strings, finite numbers, booleans, or missing values.

required
row_id_column str

Column containing stable, nonmissing, unique row IDs. IDs are exposed to callbacks as strings.

required
on_selection SelectionCallback | None

Optional callback receiving user-driven row selection.

None
on_state StateCallback | None

Optional callback receiving user-driven workspace state.

None
on_theme ThemeCallback | None

Optional callback receiving 'dark' or 'light'.

None
height str

CSS height for the embedded widget. The default gives Plotly a stable initial drawing area and may be overridden by the host.

'70vh'

Raises:

Type Description
TypeError

If the DataFrame contains unsupported columns or values.

ValueError

If the row-ID contract is invalid.

RuntimeError

If bundled NicePool browser assets are unavailable.

Source code in src/nicewidgets/nicepool_web_view/view.py
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
class NicePoolWebView:
    """Embed NicePool in NiceGUI while keeping browser details private.

    The view owns one authoritative pandas table. Constructing it schedules the
    initial table replacement after the page is connected. Public control
    methods are asynchronous because they cross the Python/browser boundary.

    Args:
        dataframe: Initial authoritative table. Cells may contain strings,
            finite numbers, booleans, or missing values.
        row_id_column: Column containing stable, nonmissing, unique row IDs.
            IDs are exposed to callbacks as strings.
        on_selection: Optional callback receiving user-driven row selection.
        on_state: Optional callback receiving user-driven workspace state.
        on_theme: Optional callback receiving ``'dark'`` or ``'light'``.
        height: CSS height for the embedded widget. The default gives Plotly a
            stable initial drawing area and may be overridden by the host.

    Raises:
        TypeError: If the DataFrame contains unsupported columns or values.
        ValueError: If the row-ID contract is invalid.
        RuntimeError: If bundled NicePool browser assets are unavailable.
    """

    def __init__(
        self,
        dataframe: pd.DataFrame,
        *,
        row_id_column: str,
        on_selection: SelectionCallback | None = None,
        on_state: StateCallback | None = None,
        on_theme: ThemeCallback | None = None,
        height: str = '70vh',
    ) -> None:
        _register_assets()
        self._row_id_column = row_id_column
        self._dataset = dataframe_to_dataset(dataframe, row_id_column)
        self._known_row_ids = {str(row[row_id_column]) for row in self._dataset['rows']}
        asset_version = _ENTRY_MODULE.stat().st_mtime_ns
        ui.add_head_html(
            f'<script type="module" src="{_ASSET_URL}/nicepool-element.js?v={asset_version}"></script>',
            shared=True,
        )
        self.element = ui.element('nice-pool').style(f'width: 100%; height: {height};')
        if on_selection is not None:
            self.element.on(
                'nicepool-selection-change',
                lambda event: on_selection(cast(NicePoolSelectionDict, self._event_detail(event))),
                ['detail'],
            )
        if on_state is not None:
            self.element.on(
                'nicepool-state-change',
                lambda event: on_state(cast(NicePoolStateDict, self._event_detail(event))),
                ['detail'],
            )
        if on_theme is not None:
            self.element.on(
                'nicepool-theme-change',
                lambda event: on_theme(cast(NicePoolTheme, self._event_value(event))),
                ['detail'],
            )
        ui.timer(0, self._initialize, once=True)

    @staticmethod
    def _event_detail(event: events.GenericEventArguments) -> dict[str, Any]:
        value = NicePoolWebView._event_value(event)
        return value if isinstance(value, dict) else {}

    @staticmethod
    def _event_value(event: events.GenericEventArguments) -> Any:
        return event.args.get('detail') if isinstance(event.args, dict) else None

    async def _initialize(self) -> None:
        await self._call('setData', self._dataset)

    async def _call(self, method: str, argument: object | None = None, *, has_argument: bool = True) -> Any:
        argument_source = json.dumps(argument, allow_nan=False) if has_argument else ''
        return await self.element.client.run_javascript(
            f"""
            await customElements.whenDefined('nice-pool');
            const element = getHtmlElement({self.element.id});
            return element.{method}({argument_source});
            """,
            timeout=10.0,
        )

    async def set_data(self, dataframe: pd.DataFrame, *, row_id_column: str | None = None) -> None:
        """Replace the complete dataset and reset state and selection.

        Args:
            dataframe: New authoritative table following the constructor's
                scalar-value and column-name requirements.
            row_id_column: Stable unique ID column. When omitted, reuse the
                column supplied to the constructor.

        Raises:
            TypeError: If the DataFrame contains unsupported values.
            ValueError: If the row-ID contract is invalid.
        """
        resolved_row_id = self._row_id_column if row_id_column is None else row_id_column
        dataset = dataframe_to_dataset(dataframe, resolved_row_id)
        await self._call('setData', dataset)
        self._dataset = dataset
        self._row_id_column = resolved_row_id
        self._known_row_ids = {str(row[resolved_row_id]) for row in dataset['rows']}

    async def reload_data(self) -> None:
        """Reload the current table, resetting all plot state and selection."""
        await self._call('setData', self._dataset)

    async def get_state(self) -> NicePoolStateDict:
        """Return the complete layout, active plot, and four plot states.

        Returns:
            A typed workspace mapping with ``schemaVersion``, ``layout``,
            ``activePlotIndex``, and exactly four entries in ``plots``.
        """
        return cast(NicePoolStateDict, await self._call('getState', has_argument=False))

    async def set_state(self, state: NicePoolStateDict) -> None:
        """Atomically replace the complete four-plot workspace state.

        Args:
            state: Complete state previously returned by :meth:`get_state` or
                constructed according to :class:`NicePoolStateDict`.
        """
        await self._call('setState', state)

    async def get_plot_state(self, plot_index: int | None = None) -> PlotStateDict:
        """Return an independent copy of one plot configuration.

        Args:
            plot_index: Zero-based plot slot. When omitted, use the active plot.

        Returns:
            A complete plot configuration safe for caller mutation.

        Raises:
            IndexError: If ``plot_index`` is outside the four available slots.
        """
        state = await self.get_state()
        resolved_index = state['activePlotIndex'] if plot_index is None else plot_index
        if resolved_index not in range(4):
            raise IndexError('plot_index must be between 0 and 3')
        return deepcopy(state['plots'][resolved_index])

    async def set_plot_state(self, plot_state: PlotStateDict, plot_index: int | None = None) -> None:
        """Replace one plot configuration without changing other plot slots.

        Args:
            plot_state: Complete plot configuration matching the active dataset.
            plot_index: Zero-based plot slot. When omitted, use the active plot.

        Raises:
            IndexError: If ``plot_index`` is outside the four available slots.
        """
        state = await self.get_state()
        resolved_index = state['activePlotIndex'] if plot_index is None else plot_index
        if resolved_index not in range(4):
            raise IndexError('plot_index must be between 0 and 3')
        state['plots'][resolved_index] = deepcopy(plot_state)
        await self.set_state(state)

    async def get_selection(self) -> NicePoolSelectionDict:
        """Return the current primary and multi-row selection."""
        return cast(NicePoolSelectionDict, await self._call('getSelection', has_argument=False))

    async def set_selection(self, row_ids: Sequence[str], *, primary_row_id: str | None = None) -> None:
        """Replace the selected rows using stable IDs from the row-ID column.

        Args:
            row_ids: Stable row IDs to select. Each value must identify a row in
                the current DataFrame.
            primary_row_id: Optional primary row. NicePool adds it to the
                selected set when necessary. ``None`` leaves no primary row.

        Raises:
            TypeError: If an ID is not a string.
            ValueError: If an ID is absent from the current DataFrame.
        """
        if isinstance(row_ids, str):
            raise TypeError('row_ids must be a sequence of row-ID strings, not one string')
        selected = list(row_ids)
        if any(not isinstance(row_id, str) for row_id in selected):
            raise TypeError('every selected row ID must be a string')
        unknown = next((row_id for row_id in selected if row_id not in self._known_row_ids), None)
        if unknown is not None:
            raise ValueError(f'unknown row ID: {unknown!r}')
        if primary_row_id is not None:
            self._validate_row_id(primary_row_id)
        selection: NicePoolSelectionDict = {
            'primaryRowId': primary_row_id,
            'selectedRowIds': selected,
        }
        await self._call('setSelection', selection)

    async def set_primary_selection(self, row_id: str | None) -> None:
        """Select one primary row, or clear selection when ``row_id`` is ``None``.

        Args:
            row_id: Stable ID from the configured DataFrame row-ID column.

        Raises:
            TypeError: If ``row_id`` is not a string or ``None``.
            ValueError: If the ID is absent from the current DataFrame.
        """
        if row_id is not None:
            self._validate_row_id(row_id)
        await self._call('setPrimarySelection', row_id)

    async def clear_selection(self) -> None:
        """Clear selection through the same browser path as the GUI button."""
        await self._call('clearSelection', has_argument=False)

    async def set_theme(self, theme: NicePoolTheme) -> None:
        """Apply a light or dark theme to controls and Plotly.

        Args:
            theme: Either ``'light'`` or ``'dark'``.
        """
        await self._call('setTheme', theme)

    async def set_plot_presets(self, presets: Sequence[PlotPresetDict]) -> None:
        """Replace the plot presets shown by the NicePool controls.

        Args:
            presets: Named single-plot configurations matching the current table.
        """
        await self._call('setPlotPresets', list(presets))

    def _validate_row_id(self, row_id: str) -> None:
        """Reject malformed or unknown external row identity."""
        if not isinstance(row_id, str):
            raise TypeError('row ID must be a string')
        if row_id not in self._known_row_ids:
            raise ValueError(f'unknown row ID: {row_id!r}')

set_data async

set_data(
    dataframe: DataFrame,
    *,
    row_id_column: str | None = None,
) -> None

Replace the complete dataset and reset state and selection.

Parameters:

Name Type Description Default
dataframe DataFrame

New authoritative table following the constructor's scalar-value and column-name requirements.

required
row_id_column str | None

Stable unique ID column. When omitted, reuse the column supplied to the constructor.

None

Raises:

Type Description
TypeError

If the DataFrame contains unsupported values.

ValueError

If the row-ID contract is invalid.

Source code in src/nicewidgets/nicepool_web_view/view.py
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
async def set_data(self, dataframe: pd.DataFrame, *, row_id_column: str | None = None) -> None:
    """Replace the complete dataset and reset state and selection.

    Args:
        dataframe: New authoritative table following the constructor's
            scalar-value and column-name requirements.
        row_id_column: Stable unique ID column. When omitted, reuse the
            column supplied to the constructor.

    Raises:
        TypeError: If the DataFrame contains unsupported values.
        ValueError: If the row-ID contract is invalid.
    """
    resolved_row_id = self._row_id_column if row_id_column is None else row_id_column
    dataset = dataframe_to_dataset(dataframe, resolved_row_id)
    await self._call('setData', dataset)
    self._dataset = dataset
    self._row_id_column = resolved_row_id
    self._known_row_ids = {str(row[resolved_row_id]) for row in dataset['rows']}

reload_data async

reload_data() -> None

Reload the current table, resetting all plot state and selection.

Source code in src/nicewidgets/nicepool_web_view/view.py
153
154
155
async def reload_data(self) -> None:
    """Reload the current table, resetting all plot state and selection."""
    await self._call('setData', self._dataset)

get_state async

get_state() -> NicePoolStateDict

Return the complete layout, active plot, and four plot states.

Returns:

Type Description
NicePoolStateDict

A typed workspace mapping with schemaVersion, layout,

NicePoolStateDict

activePlotIndex, and exactly four entries in plots.

Source code in src/nicewidgets/nicepool_web_view/view.py
157
158
159
160
161
162
163
164
async def get_state(self) -> NicePoolStateDict:
    """Return the complete layout, active plot, and four plot states.

    Returns:
        A typed workspace mapping with ``schemaVersion``, ``layout``,
        ``activePlotIndex``, and exactly four entries in ``plots``.
    """
    return cast(NicePoolStateDict, await self._call('getState', has_argument=False))

set_state async

set_state(state: NicePoolStateDict) -> None

Atomically replace the complete four-plot workspace state.

Parameters:

Name Type Description Default
state NicePoolStateDict

Complete state previously returned by :meth:get_state or constructed according to :class:NicePoolStateDict.

required
Source code in src/nicewidgets/nicepool_web_view/view.py
166
167
168
169
170
171
172
173
async def set_state(self, state: NicePoolStateDict) -> None:
    """Atomically replace the complete four-plot workspace state.

    Args:
        state: Complete state previously returned by :meth:`get_state` or
            constructed according to :class:`NicePoolStateDict`.
    """
    await self._call('setState', state)

get_plot_state async

get_plot_state(
    plot_index: int | None = None,
) -> PlotStateDict

Return an independent copy of one plot configuration.

Parameters:

Name Type Description Default
plot_index int | None

Zero-based plot slot. When omitted, use the active plot.

None

Returns:

Type Description
PlotStateDict

A complete plot configuration safe for caller mutation.

Raises:

Type Description
IndexError

If plot_index is outside the four available slots.

Source code in src/nicewidgets/nicepool_web_view/view.py
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
async def get_plot_state(self, plot_index: int | None = None) -> PlotStateDict:
    """Return an independent copy of one plot configuration.

    Args:
        plot_index: Zero-based plot slot. When omitted, use the active plot.

    Returns:
        A complete plot configuration safe for caller mutation.

    Raises:
        IndexError: If ``plot_index`` is outside the four available slots.
    """
    state = await self.get_state()
    resolved_index = state['activePlotIndex'] if plot_index is None else plot_index
    if resolved_index not in range(4):
        raise IndexError('plot_index must be between 0 and 3')
    return deepcopy(state['plots'][resolved_index])

set_plot_state async

set_plot_state(
    plot_state: PlotStateDict, plot_index: int | None = None
) -> None

Replace one plot configuration without changing other plot slots.

Parameters:

Name Type Description Default
plot_state PlotStateDict

Complete plot configuration matching the active dataset.

required
plot_index int | None

Zero-based plot slot. When omitted, use the active plot.

None

Raises:

Type Description
IndexError

If plot_index is outside the four available slots.

Source code in src/nicewidgets/nicepool_web_view/view.py
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
async def set_plot_state(self, plot_state: PlotStateDict, plot_index: int | None = None) -> None:
    """Replace one plot configuration without changing other plot slots.

    Args:
        plot_state: Complete plot configuration matching the active dataset.
        plot_index: Zero-based plot slot. When omitted, use the active plot.

    Raises:
        IndexError: If ``plot_index`` is outside the four available slots.
    """
    state = await self.get_state()
    resolved_index = state['activePlotIndex'] if plot_index is None else plot_index
    if resolved_index not in range(4):
        raise IndexError('plot_index must be between 0 and 3')
    state['plots'][resolved_index] = deepcopy(plot_state)
    await self.set_state(state)

get_selection async

get_selection() -> NicePoolSelectionDict

Return the current primary and multi-row selection.

Source code in src/nicewidgets/nicepool_web_view/view.py
210
211
212
async def get_selection(self) -> NicePoolSelectionDict:
    """Return the current primary and multi-row selection."""
    return cast(NicePoolSelectionDict, await self._call('getSelection', has_argument=False))

set_selection async

set_selection(
    row_ids: Sequence[str],
    *,
    primary_row_id: str | None = None,
) -> None

Replace the selected rows using stable IDs from the row-ID column.

Parameters:

Name Type Description Default
row_ids Sequence[str]

Stable row IDs to select. Each value must identify a row in the current DataFrame.

required
primary_row_id str | None

Optional primary row. NicePool adds it to the selected set when necessary. None leaves no primary row.

None

Raises:

Type Description
TypeError

If an ID is not a string.

ValueError

If an ID is absent from the current DataFrame.

Source code in src/nicewidgets/nicepool_web_view/view.py
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
async def set_selection(self, row_ids: Sequence[str], *, primary_row_id: str | None = None) -> None:
    """Replace the selected rows using stable IDs from the row-ID column.

    Args:
        row_ids: Stable row IDs to select. Each value must identify a row in
            the current DataFrame.
        primary_row_id: Optional primary row. NicePool adds it to the
            selected set when necessary. ``None`` leaves no primary row.

    Raises:
        TypeError: If an ID is not a string.
        ValueError: If an ID is absent from the current DataFrame.
    """
    if isinstance(row_ids, str):
        raise TypeError('row_ids must be a sequence of row-ID strings, not one string')
    selected = list(row_ids)
    if any(not isinstance(row_id, str) for row_id in selected):
        raise TypeError('every selected row ID must be a string')
    unknown = next((row_id for row_id in selected if row_id not in self._known_row_ids), None)
    if unknown is not None:
        raise ValueError(f'unknown row ID: {unknown!r}')
    if primary_row_id is not None:
        self._validate_row_id(primary_row_id)
    selection: NicePoolSelectionDict = {
        'primaryRowId': primary_row_id,
        'selectedRowIds': selected,
    }
    await self._call('setSelection', selection)

set_primary_selection async

set_primary_selection(row_id: str | None) -> None

Select one primary row, or clear selection when row_id is None.

Parameters:

Name Type Description Default
row_id str | None

Stable ID from the configured DataFrame row-ID column.

required

Raises:

Type Description
TypeError

If row_id is not a string or None.

ValueError

If the ID is absent from the current DataFrame.

Source code in src/nicewidgets/nicepool_web_view/view.py
243
244
245
246
247
248
249
250
251
252
253
254
255
async def set_primary_selection(self, row_id: str | None) -> None:
    """Select one primary row, or clear selection when ``row_id`` is ``None``.

    Args:
        row_id: Stable ID from the configured DataFrame row-ID column.

    Raises:
        TypeError: If ``row_id`` is not a string or ``None``.
        ValueError: If the ID is absent from the current DataFrame.
    """
    if row_id is not None:
        self._validate_row_id(row_id)
    await self._call('setPrimarySelection', row_id)

clear_selection async

clear_selection() -> None

Clear selection through the same browser path as the GUI button.

Source code in src/nicewidgets/nicepool_web_view/view.py
257
258
259
async def clear_selection(self) -> None:
    """Clear selection through the same browser path as the GUI button."""
    await self._call('clearSelection', has_argument=False)

set_theme async

set_theme(theme: NicePoolTheme) -> None

Apply a light or dark theme to controls and Plotly.

Parameters:

Name Type Description Default
theme NicePoolTheme

Either 'light' or 'dark'.

required
Source code in src/nicewidgets/nicepool_web_view/view.py
261
262
263
264
265
266
267
async def set_theme(self, theme: NicePoolTheme) -> None:
    """Apply a light or dark theme to controls and Plotly.

    Args:
        theme: Either ``'light'`` or ``'dark'``.
    """
    await self._call('setTheme', theme)

set_plot_presets async

set_plot_presets(presets: Sequence[PlotPresetDict]) -> None

Replace the plot presets shown by the NicePool controls.

Parameters:

Name Type Description Default
presets Sequence[PlotPresetDict]

Named single-plot configurations matching the current table.

required
Source code in src/nicewidgets/nicepool_web_view/view.py
269
270
271
272
273
274
275
async def set_plot_presets(self, presets: Sequence[PlotPresetDict]) -> None:
    """Replace the plot presets shown by the NicePool controls.

    Args:
        presets: Named single-plot configurations matching the current table.
    """
    await self._call('setPlotPresets', list(presets))

Bases: TypedDict

Complete serializable configuration for one NicePool plot.

Source code in src/nicewidgets/nicepool_web_view/types.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class PlotStateDict(TypedDict):
    """Complete serializable configuration for one NicePool plot."""

    plotType: PlotType
    preFilters: dict[str, NicePoolFilterValue]
    xColumn: str
    yColumn: str
    groupColumn: str | None
    colorColumn: str | None
    useAbsoluteValue: bool
    removeValuesThreshold: float | None
    swarmJitterAmount: float
    swarmGroupOffset: float
    jitterSeed: int
    histogramBins: int
    showRaw: bool
    showMean: bool
    showErrorBars: bool
    errorBarType: Literal['std', 'sem']
    pointSize: int
    showLegend: bool
    legendPosition: Literal['bottom', 'right', 'top', 'left']
    showPlotlyToolbar: bool
    showHover: bool
    showAxes: bool
    showHorizontalGrid: bool
    showVerticalGrid: bool
    cvEpsilon: float

Bases: TypedDict

Complete serializable state for the four-slot NicePool workspace.

Source code in src/nicewidgets/nicepool_web_view/types.py
42
43
44
45
46
47
48
class NicePoolStateDict(TypedDict):
    """Complete serializable state for the four-slot NicePool workspace."""

    schemaVersion: Literal[1]
    layout: PlotLayout
    activePlotIndex: int
    plots: list[PlotStateDict]

Bases: TypedDict

Primary and multi-row selection returned by NicePool.

Source code in src/nicewidgets/nicepool_web_view/types.py
51
52
53
54
55
class NicePoolSelectionDict(TypedDict):
    """Primary and multi-row selection returned by NicePool."""

    primaryRowId: str | None
    selectedRowIds: list[str]