Skip to content

Changes since version 1.17.0 (2.0.0-rc1)

New Features

  • Client → server log channel — the browser now reports warnings/errors to the backend over the existing (id, event) envelope via sendLog(level, message, { source, data }) (targeting the reserved client_log control with the log event). A backend-only ClientLog control normalizes each message and, by default, logs it under tanga.viz.client; viz.on_client_log(handler) replaces the sink. The frontend's _log(...) init/WS trace lines can additionally be forwarded at info level by opting in with setLogForwarding(true) / ?log=1.

  • SdfCompose combine descriptor — SdfCompose(element, mode, smoothness=…) names a Composed/SdfGroup member's combine mode and optional blend radius, replacing the opaque (element, mode[, smoothness]) tuple (which still works).

  • Unified transform arguments — set_transform() and apply_transform() (on VizSceneObject/VizObjectRef) accept the same inputs: a Transform, an MV, or a GA operator (Rotor/GeneralRotor/Motor/Translator/ Dilator).

  • Configurable versor factorization — blade_factorize_versor(eps=1e-6, max_iterations=64) exposes the degeneracy threshold and the iteration cap.

  • Native, dependency-free editable table — TableView no longer loads the Tabulator CDN; it is now a plain-DOM table styled only by --tanga-* tokens, so it themes like every other control.

  • Optional column titles and row numbers — TableView accepts show_column_titles and show_row_numbers to toggle the header row and a leading 1-based row-number column.

  • Active cell + cursor-key movement — a single highlighted cell, moved with the arrow keys, anchors cell editing and keyboard navigation.

  • Column deletion — a new column_delete event, TableColumnDelete payload, Table.delete_column mutation, and on_column_delete handler, covered by backend undo/redo.

  • Header-click sorting — click a column header to sort ascending/descending (a third click clears the sort); display-only, gated by sortable.

  • Programmatic table control — TableView.get_cell / set_cell / get_value / clear_history round out the backend control API (set_cell pushes the grid to the browser).

  • Table viewport, zoom + resize — the TableView grid has a natural default size (scrolling internally instead of growing its container), a standard row height, title-bar +/− controls to zoom column width and row height, and a bottom-right corner grip to resize the whole table.

  • Backend-driven table rows/columns — TableView.add_row / add_column / delete_row / delete_column mutate the model and push the grid, so apps add their own ButtonViews to the layout (the widget ships no add/delete buttons).

  • Table column types, editors & persistence — columns can be typed (number / string / bool / an enum of allowed values), deduced from the data or set explicitly via TableView(column_types=[...]). Numbers right-align and reject non-numeric input, booleans render an always-on checkbox, and enums edit through a dropdown. save(path) / load(path) round-trip data + types + relative column widths + row height + sort order as a versioned JSON file (id + version); to_csv(path) / from_csv(path) exchange plain data; and json_path=... auto-saves on every change.

  • Selection-relative table add/delete — the table now reports its active cell to the backend (TableCellSelect payload, on_cell_select handler, and Table.active_cell / TableView.active_cell), and TableView gains insert_row(index) / insert_column(index) alongside delete_row / delete_column, so apps can insert above/below or left/right of the selected cell and delete the selected row/column — falling back to the table edges when no cell is selected.

  • Editable table columns — TableView headers can be renamed by double-clicking (editable_titles, default on), a number column carries a Python str.format display template (set_column_format, persisted in the saved JSON), and a right-click header menu proposes a different column type, applied or rejected by a backend convert_column (string always; bool↔number 1/0; enum only below 20 distinct values) with an on_column_type_change handler for custom handling (e.g. a warning banner). The sort arrow is also larger and directly clickable.

  • Auto-height toolbars — ToolbarView now sizes its height to its tallest control (plus the margin/border chrome) instead of a fixed control floor, so taller controls such as dropdowns no longer overflow the row, and a toolbar of only icon/button controls collapses to fit them with a small margin.

  • Per-scene, switchable space dimension — viz.scene(name, space_dim=…) / viz.add_scene(name, space_dim=…) set the space dimension when a scene is created, and viz.set_space_dim(2 | 3, scene_name=…) (or handle.space_dim =) switches it live — the browser re-applies camera mode and controls without a reload. A new camera/switch_2d_3d.py example toggles one plot between a flat 2D view and a tilted 3D view with a checkbox.

  • 2D camera stretch modes — the 2D camera's single uniform boolean is replaced by a stretch parameter ("fit" letterbox, "fill" stretch both axes, "fill_x" x-fills/y-keeps-aspect, "fill_y" y-fills/x-keeps-aspect), exposed on View2DConfig, CameraConfig2d, fit_view2d, and CoordinateSystem, and mirrored in the live viewer and HTML export.

Breaking Changes

  • uniform removed from the 2D camera — View2DConfig, CameraConfig2d, and fit_view2d no longer accept uniform; use stretch="fit" (the old uniform=True) or stretch="fill" (the old uniform=False).

Bug Fixes

  • parent_id control groups not shown on the single-scene page — a GroupView(parent_id=…) in a SceneView(overlay=[...]) is now attached via CSS2D once its parent entity is registered (deferred attach), instead of being dropped when the layout is built before the scene entities arrive.

  • Versor factorization hangs on near-degenerate motors — FactorizeVersor no longer loops forever when a max-grade blade is numerical noise (a pruned high-grade component); it discards that grade and returns the stable factors found so far.

  • Last column/row cell editing — replacing Tabulator's event layer with a plain-DOM grid fixes the double-click edit failing on the last column/row inside auto-sized overlay panels.

  • Column zoom no longer inflates the page — zooming column width keeps the grid's viewport fixed and scrolls horizontally, instead of stretching the table wider than the window.

  • Broken/overlapping toolbar icons — the material: icon family now loads Google Material Symbols Outlined (replacing the classic Material Icons font), so ligatures such as add_row_below, add_column_right, and splitscreen_bottom render as glyphs instead of raw text that mispositioned the toolbar layout and hover backgrounds. The four split-screen enum values were also corrected from split_screen_* to the real splitscreen_* ligature names.

  • Table resize grip overlapping the scrollbar — the bottom-right resize handle now has an opaque themed background so it cleanly covers the scrollbar corner (and the adjacent ends of the horizontal/vertical scrollbars) instead of the scrollbar track showing through the handle.

  • 2D plots clipped in split panes — fit_view2d(...) now defaults to a border_px of 60 (matching CoordinateSystem), so a per-pane camera embedded via SceneView(..., camera=fit_view2d(...)) leaves a label margin instead of clipping the axis labels and axes at the pane edge.

  • multi_plot.py rendered empty panes — the example referenced scenes (left/middle/right) that it never created; it is rewritten as three named 2D plots (sine, log power law, parabola) in a horizontal SplitView.

  • 2D→3D switch stayed orthographic — switchToCamera now recreates a default perspective camera when the space dimension changes to 3D and the current camera is orthographic, so a runtime set_space_dim(3) (with no explicit camera) actually switches to a 3D view.