Boardflare Notebook Changelog

Release notes and API evolution.

This changelog records public API, protocol, and runtime contract changes across Boardflare releases.

October 2026 release (v1.4.0)

What this release ships

Changes from main

  • bf.selection Table Selections: Active table selections can be tracked using bf.selection("TableName"), returning a pandas DataFrame with the table’s headers and at most one row: the row at the user’s active cell (the cell the cursor is on), even when a larger range is highlighted. The result is empty when the active cell is outside the table’s body rows or columns, on another sheet, on the header or total row, or on a hidden or filtered row. Rows carry values rather than positions. Limits: the cursor must be on a table cell, because cells beside the table select no row; filtering that hides the active row does not refresh the value until the next selection event.
  • One bf.inputs per Notebook: Enforced at most one bf.inputs(...) widget per notebook. Declaring more than one raises RuntimeError("Only one bf.inputs(...) is allowed per notebook; use one bf.inputs(...) with all inputs").
  • Notebook Source Moves to Code Sheet: Notebook code is now stored in the hidden _BOARDFLARE worksheet (format version 1) rather than Custom XML parts, allowing chat assistants (Copilot, ChatGPT, Claude) and external tools to edit code and workbook data together. Template provenance (templateId and templateVersion) is stored directly in cells H10:H11. Excel workbooks with a Custom XML notebook migrate on opening: the source is written to the code sheet, read back, and only then is the Custom XML part removed. The built-in AI editor in the task pane has been removed in favor of direct code-sheet editing.
  • Zero-Network Execution Sandbox: Notebook code has no direct external network access. Dynamic package installation over the network is disabled; dependencies are served from Boardflare via pre-bundled companion packages in the Pyodide lockfile, and notebooks cannot make external HTTP requests.
  • Blank header cells stay None: When reading ranges with headers=True, blank header cells remain None rather than being coerced to the string "nan". This matches Python in Excel (xl()) behaviour and uses pd.Index(..., dtype=object).
  • Duplicate headers now work: Range inputs containing duplicate column header labels are now handled cleanly. On main, accessing columns by name caused an AttributeError when pandas returned a DataFrame for duplicates; the runtime now normalizes columns by position index.
  • Publication Value Property Raises RuntimeError: Accessing .value on a bf.publish(...) result raises RuntimeError pointing authors to publication.consumers instead of exposing internal traits.
  • Clearer timezone-aware datetime error: Publishing a timezone-aware datetime raises an actionable error message explaining that Excel stores wall-clock values and providing a concrete conversion snippet (value.astimezone(zone).replace(tzinfo=None)), rather than a generic rejection message.
  • Web host reads formatted and error cells as typed values: In the web (Univer) host, currency, percent, date, datetime and time cells now arrive as numbers, datetime/time values, and formula errors as ExcelErrorValue, as they already did in Excel. On main the web host passed their display strings (for example "$49.99", "2026-09-01", "#DIV/0!").
  • Web host keeps text that looks like a boolean: A published string "True" stays text in the web host; on main the web host turned it into a boolean.