Skip to contents

blockr.dag 0.1.5

CRAN release: 2026-07-30

Breaking changes

  • new_context_menu_entry() and new_toolbar_item() no longer take a sidebar argument. An entry that fills a sidebar panel no longer names it: blockr.dock stamps the writing action onto the panel as it writes (blockr.dock#391), and ownership is read back with blockr.dock::sidebar_owned_by() rather than tracked here. retarget = TRUE is now the whole declaration, and an entry that fills a panel without re-targeting declares nothing at all. Requires a blockr.dock carrying sidebar_owned_by().

  • The DAG extension no longer ingests or emits a full g6 graph object (#119). new_dag_extension() drops the graph argument in favour of positions, a named list keyed by block id (list(a = list(x = 100, y = 200))) carrying only the board-independent view attributes the extension owns. The board is now the single source of truth: nodes, edges, combos and all styling (icons, labels, colors, ports) are always regenerated from it, and supplied positions are overlaid onto the corresponding nodes’ coordinates. Serialization shrinks to positions only; no board-derived styling is persisted. The g6 graph wire format and its board/g6 converters (new_graph(), as_graph(), g6_from_graph(), …) are now internal and no longer exported. This mirrors the dockview wire-format decoupling in blockr.dock. Note: the auto-layout still computes final node placement at cold start, so supplied positions are not yet honored over it; making positions pin over the layout is a planned follow-up (see #141).

New features

  • The DAG carries a search box and a board outline, for boards too large to read at fit-to-view (#139). Type a block name or id in the box, top-right of the canvas, and the view moves to that block and selects it, which reveals its panel like any other node click. Under the box sits the outline: open it to list the whole board as a tree, each stack an accordion holding its blocks in flow order, with the number of blocks it holds on the right, and the board’s own totals on the panel header so the size of it reads without opening anything. Clicking a row goes to that block or stack. The two are one control rather than two panels, and both name things the way a board does – “block”, “stack”, “link”, not g6’s “node”, “combo”, “edge”. Searching a block inside a collapsed stack opens that stack on the way, which is what makes collapsing usable: a large board no longer has to be legible on the canvas for every block to stay reachable. Requires a g6R carrying g6_search() and g6_outline() (cynkra/g6R#65).

  • The DAG node context menu offers an “Edit inputs” entry (#158). Right-clicking a node and choosing “Edit inputs” opens blockr.dock’s per-block input editor, so a block’s incoming links can be renamed, reordered and removed together instead of one edge at a time – the per-node counterpart to “Edit link”. It fills the shared actions sidebar and re-targets like the other editors (#162): while pinned, clicking another node re-points it at that block’s inputs.

  • Selecting an element re-points a pinned sidebar editor at it (#162). When a sidebar is pinned open on an editor, clicking another element of the kind that editor operates on re-targets it there, with no second trip through the context menu – the stack editor (Edit stack) follows combo clicks, and the node-anchored editors (Create link, Append block) follow node clicks. Each editor decides via its own context-menu condition, so a pinned editor is never taken over by a selection of a kind it does not handle. Re-targeting also stops as soon as anything else fills the panel: opening Create stack or the toolbar’s Add stack in a pinned sidebar releases it, so a later selection leaves the newly opened form alone.

  • The DAG edge context menu offers an “Edit link” entry (#154). Right-clicking an edge and choosing “Edit link” opens blockr.dock’s link editor for that link, so renaming a positional input, switching its slot or redirecting an endpoint no longer means removing the edge and drawing it again. It fills the shared actions sidebar and re-targets like the other editors (#162): while pinned, clicking another edge re-points it at that link.

  • The DAG canvas now reflects in-place link edits (#156). A board update whose only link change is a modification (links$mod) – redirecting an endpoint, switching a finite input’s slot, or renaming a variadic input, as the “Edit link” action emits – redraws the affected edge from the updated board: it moves to its new source, target and port and relabels, rather than leaving the stale edge on screen until an unrelated redraw.

  • The node status badge reflects a block’s full eval status, not just error conditions (#145). The badge is derived by blockr.dock::block_status_badge() – the single derivation the dock card icon and the DAG node badge share – so a node shows the same amber waiting, yellow unset or red failed dot as the dock, with identical colour and geometry. Any error condition still shows red (a render-phase error leaves a block ready but errored). A ready node carries no badge; a dormant node – one whose status is not currently computed because nothing renders its output – keeps its last-known badge rather than dropping it, so every invalid block stays flagged even after it leaves the eval set.

  • Pasted blocks keep their original names; the " (copy)" suffix is gone from blocks. A pasted group is offset on the canvas and selected, so the label carried no extra information, and mass-renaming made copying larger app parts painful. Stacks keep the suffix (Cohort (copy)) as group-level provenance.

  • Node positions are externally controllable (#120). The positions handle is registered via external_ctrl, so positions can be set programmatically (e.g. by an AI assistant) through the board update lifecycle: update(list(extensions = list(mod = list(<ext_id> = list(positions = list(<block-id> = list(x = , y = ))))))) moves the corresponding nodes. The handle is bidirectional: it tracks live user drags (debounced) and pushes external writes to the client, with a whole-pixel-rounded equality guard that prevents the set/echo feedback loop.

Internal changes

  • The DAG now renders with G6’s default canvas renderer instead of the SVG renderer. The SVG element reports offsetWidth == 0, which broke g-lite’s client/canvas coordinate scaling under browser zoom other than 100% (drops and port grabs silently failed below 100%). The SVG renderer is still used for shinytest2 end-to-end tests via the new blockr.dag.svg_renderer option (see ?new_dag_extension). Requires g6R (>= 0.6.5), which keeps the create-edge assist node from crashing the canvas renderer and fixes a combo/layout crash when a stack is created at runtime.
  • Reworked block ports: ports are now always visible (rather than hover-only) with a fixed radius, and the output port uses the label-bottom placement. Pairs with g6R’s port-grab tolerance so a near-miss on a port still starts an edge.
  • Fix stack renames/recolors not reflecting in the DAG: update_observer() still expected stacks$mod to carry full stacks objects after blockr.core switched the delta to partial-argument lists; the deltas are now resolved against the board before updating the combos.
  • Fix links between stacked blocks being unselectable: edges render at zIndex = -1 (under nodes) while combos sat at G6’s default 0, so a stack’s rectangle swallowed every click aimed at the edges between its member nodes. Combos now render at zIndex = -2 (node > edge > combo), keeping the stack itself clickable in its padding area.
  • Fix #123: renaming a block now relabels its DAG node instead of erroring. update_observer() handed blockr.core’s partial-argument blocks$mod delta straight to update_nodes() (which needs full block objects); it now updates the node label directly from the delta.
  • Fix #144: copy/paste no longer corrupts NULL-valued block state. copy_selection_to_clipboard() serialized the clipboard with toJSON()’s default null = "list", encoding NULL as {} and deserializing it back into list(); pasted blocks then errored on first data (e.g. nzchar(list()) -> “argument is of length zero” in the chart and table blocks). Now passes null = "null" to match blockr.core’s save/restore serialization, round-tripping NULL faithfully.
  • Fix blockr.dock#308: single-clicking a DAG node opens the block’s panel again. blockr.dock retired the live dock handle from the extension server surface, which the node-select observer passed to blockr.dock::show_panel(). The observer now emits a views update delta built with blockr.dock’s panel-op grammar (select, or add + select when absent) to reveal the block’s panel in the current view, adding it there if the view does not already hold it — without switching to another view.
  • Track blockr.dock’s extension-identity redesign. An extension’s runtime id is now the container-assigned key (its class minus the _extension suffix, or an explicit list name), and extension results reach actions and block callbacks as one extensions bundle keyed by that id rather than a splatted argument named after the id. The DAG actions and the block-status callback now resolve their result with blockr.dock::extension_ids(board$board, "dag_extension") and index the bundle explicitly, instead of binding a dag_extension formal.

blockr.dag 0.1.2

CRAN release: 2026-04-29

Internal changes

  • Label observers for OTEL support.
  • Add support for collapsible nodes and combos, through g6R.
  • Reworked actions. Inherits from blockr.dock.
  • Added support for node ports from g6R.
  • Fix #86: links can now be drawn repeatedly; previously the create-link gesture only worked once.
  • Fix #110: copy/cut keyboard shortcuts no longer hijack plain text selections (column names, error messages, etc.).

blockr.dag 0.1.0

CRAN release: 2025-12-18

  • Initial CRAN submission.