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).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.
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.label-bottom placement. Pairs with g6R’s port-grab
tolerance so a near-miss on a port still starts an edge.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.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.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.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.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.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.dock.