The dock "manager" object is gone. apply_board_update.dock_board() is
now a pure reducer over board_layouts(); all live view surgery
(instantiate / tear down / restore / rename / switch) runs in a single
closure-resident reconcile pass driven by the committed board, replacing
the duplicated delta-driven and UI-driven view CRUD. View init is just the
empty-registry case of that pass (create every view, show the active one),
so there is no separate init path. The per-session dock state is ordinary
closure-private state passed explicitly, not a handle threaded back from
the board callback through dot_args. Also makes
augment_board_update.dock_board() idempotent — a view id is minted once
rather than re-minted on every augment pass — fixing a view-add loop
(#164).
Views now carry a stable, immutable id decoupled from their
editable display name, mirroring the id / name split used for
blocks. dock_layouts (and the runtime dock_mgr$docks registry) are
keyed by id; the name is an attribute read / written via view_name()
/ view_name<-() (with view_names() for a whole collection), and
active_view() now returns the active view's id. Renaming a view is a
pure name-attribute write — the id, dock module and DOM element are
untouched, so no structure is ever re-keyed (and the live-sync rename
no longer leaks as remove-then-add). The dock module / DOM ids derive
deterministically from the view id (no random per-render minting), and
the views delta gains a rename slot. Naming constraints relax to
display concerns (non-empty, unique label). Serialization round-trips
ids. In new_dock_board(layouts = list(...)) the list name is the
view's id (the container's key, like a block id — minted when
absent); the display name is set on the view via dock_layout(name = )
and falls back to a label derived from the id when unset. The views
delta addresses existing views by id (the only stable handle) —
mod / rm / active carry ids; add supplies a display name and
mints the id. Producers that addressed views by name (e.g.
blockr.assistant) must switch to ids (#166).
dock_layout objects gain format() / print() methods that render
the arrangement as an indented tree: orientation, nested groups with
their sizes, tabbed leaves with the active tab, and the focused panel.
Panel IDs print without their block_panel- / ext_panel- prefixes
by default; pass bare = FALSE for the canonical IDs (#161).
The add / append / prepend block action handlers now mount the
blockr.ui block-browser module: a card-list block picker with
search, per-category icons, and a per-card expand for tweaking the
id / title / link / port before adding. Repeated single clicks on a
card produce distinct blocks (suggested ids are seeded against the
board so they never collide), replacing the old single-select
selectize form. The per-field Shiny inputs (<mode>_block_selection
/ <mode>_block_id / <mode>_block_name / <mode>_link_id /
<mode>_block_input / <mode>_block_confirm) are gone in favour of
a single committed-block reactive returned by
blockr.ui::block_browser_server(). block_sidebar_body() is
removed (nothing in-tree calls it; link / stack flows use
link_sidebar_body() / stack_sidebar_body() unchanged). Requires
blockr.ui with the block-browser module.
The views slot in the board_update payload is now a structured
delta (add / mod / rm / active) instead of a wholesale
dock_layouts replacement. Mentioned views are touched, omitted
views keep their current state, and the four sub-slots compose
atomically with blocks / links / stacks in the same lifecycle
tick. See ?dock_board_update_lifecycle for the contract (#150).
Removing a block no longer clears the active view's layout. Instead,
every view containing the removed block has the block's panel
dropped surgically from its layout, preserving the rest of the grid.
augment_board_update.dock_board() performs the cleanup for the
update() lifecycle path; rm_blocks.dock_board() does the same
surgically (rather than nuking the active layout) for direct
callers like clear_board() (#150).
board_layouts(rv$board) now stays in sync with UI-driven layout
changes (panel close/add, drag-resize/rearrange, view CRUD).
UI-driven mutations are routed through update(list(views = ...))
and applied via validate_board_update.dock_board() and
apply_board_update.dock_board(). Writes are debounced (250 ms) so
drag-resize doesn't thrash. Requires blockr.core (>= 0.1.3) for
the update lifecycle generics.
Added prepend block action.
Define multi-view boards by passing a named list to
new_dock_board(layouts = ...):
layouts = list(
Analysis = list("block_1", "block_2"),
Overview = list("dag_extension")
)
Mark an arrangement as initially active with dock_layout(..., active = TRUE):
layouts = list(
Analysis = list("block_1", "block_2"),
Overview = dock_layout("dag_extension", active = TRUE)
)
If none is marked, the first one is used. The board's initialise_layout
normalises each slot to a dock_layout (storing the arrangement only),
and stores the result as a dock_layouts collection.
Breaking changes to the layout API:
new_dock_board()'s layout parameter to layouts (and the corresponding board field), since boards now hold a dock_layouts collection.dock_layout(brd) (and setter dock_layout(brd) <-) to active_layout(brd) / active_layout(brd) <-.board_views(brd) to board_layouts(brd), and the corresponding setter dock_layouts(brd) <- value to board_layouts(brd) <- value.dock_layout is now the per-view arrangement type. dock_layout(...) constructs one from a nested list of block / extension IDs and accepts orientation, sizes, and active arguments. The previously-exported "fully-resolved" dock_layout (grid + panels wire shape) is gone — panel content is derived from the board's blocks and extensions on demand at the dockview boundary, so per-view storage shrinks to just the arrangement and panel definitions no longer duplicate across views.panels(..., active = NULL) for tabbed leaves with an explicit open tab, and group(..., sizes = NULL) for nested branches with explicit ratios. dock_layout() itself also accepts sizes = for root-level ratios and orientation = for the top-level split direction.dock_view(), dock_grid(), is_dock_grid(), and as_dock_grid(). Use dock_layout(...) (or the new panels() / group()) for the per-view spec.default_layout() → and kept the name default_layout(). It now returns a dock_layout (arrangement only) — the previously-exported default_grid() (panel-ID form) is gone.create_dock_layout() (renamed internally to resolve_dock_layout()).dock_layouts() constructor. The user-facing input shape for new_dock_board(layouts = ...) is a plain named list — the dock_layouts type is the resolved collection that the board holds internally. is_dock_layouts(), as_dock_layouts(), and validate_dock_layouts() remain exported.new_dock_layout(); use dock_layout() instead.view_ids() and view_can_crud(). Both were internal helpers exposed by accident; renamed to layout_ids() and views_can_crud() respectively to align with what they operate on.dock_layout decoupled from dockview's internal tree. The persisted shape is a flattened recursive spec: the top object carries orientation, children, optional sizes, and optional focus (the panel with current focus); a child is a bare string (single-panel leaf), a {panels, active?} object (tabbed leaf), or a {children, sizes?} object (nested branch). Sizes are ratios (auto-normalised from dockview's pixel sizes); even splits omit sizes; the default open tab omits active; focus on the first leaf omits focus. focus round-trips the focused group (dockview's activeGroup) by naming a stable panel rather than the regenerated group id. Legacy payloads (with the dockview-shape grid field) load via a shape-discriminated reader. Producer-version routing is tracked in #153 (depends on blockr.core forwarding ... in blockr_deser.list).as_dock_layout() coerces a dock_layout (identity), a board (its active layout), or a spec list to a dock_layout; as.list() of a dock_layout returns that spec list. The R object ↔ JSON string boundary uses explicit verbs: layout_to_json() / layout_from_json(). Both as_dock_layout(<list>) and layout_from_json() take optional blocks / extensions to resolve bare IDs and validate. layout_panel_ids() / panel_obj_ids() inspect the panel / object IDs a layout references. The dockview wire format and its converters are not part of the public API — only the dock_layout object, our JSON, and the spec list are; as_dock_layout() rejects a dockview grid-shaped list.