Changelog
blockr.dag 0.1.2.9000
Breaking changes
- The DAG extension no longer ingests or emits a full g6 graph object (#119).
new_dag_extension()drops thegraphargument in favour ofpositions, 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 inblockr.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 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 amberwaiting, yellowunsetor redfaileddot as the dock, with identical colour and geometry. Any error condition still shows red (a render-phase error leaves a blockreadybut errored). Areadynode carries no badge; adormantnode – 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
positionshandle is registered viaexternal_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 brokeg-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 forshinytest2end-to-end tests via the newblockr.dag.svg_rendereroption (see?new_dag_extension). Requiresg6R (>= 0.6.0.9001), which keeps the create-edge assist node from crashing the canvas renderer. - 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.
- Reworked block ports: ports are now always visible (rather than hover-only) with a fixed radius, and the output port uses the
label-bottomplacement. Pairs with g6R’s port-grab tolerance so a near-miss on a port still starts an edge. Requiresg6R (>= 0.6.0.9000). - Fix #86.
- Fix #110: copy/cut keyboard shortcuts no longer hijack plain text selections (column names, error messages, etc.).
- Fix #123: renaming a block now relabels its DAG node instead of erroring.
update_observer()handed blockr.core’s partial-argumentblocks$moddelta straight toupdate_nodes()(which needs fullblockobjects); 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 withtoJSON()’s defaultnull = "list", encodingNULLas{}and deserializing it back intolist(); pasted blocks then errored on first data (e.g.nzchar(list())-> “argument is of length zero” in the chart and table blocks). Now passesnull = "null"to match blockr.core’s save/restore serialization, round-trippingNULLfaithfully. - Fix blockr.dock#308: single-clicking a DAG node opens the block’s panel again.
blockr.dockretired the livedockhandle from the extension server surface, which the node-select observer passed toblockr.dock::show_panel(). The observer now emits aviewsupdate delta built withblockr.dock’s panel-op grammar (select, oradd+selectwhen 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_extensionsuffix, or an explicit list name), and extension results reach actions and block callbacks as oneextensionsbundle 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 withblockr.dock::extension_ids(board$board, "dag_extension")and index the bundle explicitly, instead of binding adag_extensionformal.