The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.
ptr_server() into an
interactive, brushable plot. ptr_ggplotly() converts the
state’s current ggplot into a plotly htmlwidget — minting
the per-row selection keys and wiring the crosstalk source
internally, so the call is state-first
(ptr_ggplotly(state)). ptr_plotly_selection()
returns a reactive of the rows the user brushed or clicked on that plot
(mode = "rows" and other projections), reset on each draw.
Together they support a full brush-to-table loop. See
?ptr_ggplotly and ?ptr_plotly_selection.ptr_options(gate_draw = ) toggles the
“Update plot” button. When TRUE (the default) the plot
re-renders only on the button click, batching placeholder changes; when
FALSE the button is omitted and the plot re-renders
reactively on every placeholder change (live mode). It also governs the
grid host’s “Draw all” button. Read once when the app is built, so set
it before ptr_app() / ptr_ui().ptr_options(suppress_warnings = )
silences R warnings emitted while the plot is drawn
(e.g. loess “all data on boundary of neighborhood” /
“Failed to fit group N”). Default FALSE. Only the
plot-drawing step is wrapped; errors still propagate to the inline error
pane.ppExpr safety messages are now
truthful — when a runtime ppExpr input is
rejected, the abort reports the actual reason the expression was blocked
rather than a generic message.name →
topic. The shipped primer instructs models to call
ggpaintr_docs(topic), but the registered
ellmer tool schema (and ptr_llm_topic()’s
parameter) named the argument name, so a model following
the primer emitted a key the schema rejected. The schema argument and
ptr_llm_topic() now agree on topic.ggpaintr-tutorial vignette as its single built
vignette.ptr_define_placeholder_value() /
ptr_define_placeholder_consumer() /
ptr_define_placeholder_source():
positional_arg → parse_positional_arg,
named_args → parse_named_args,
validate_input → validate_session_input, and
runtime → embellish_eval. The rename makes the
slot family legible — two slots parse the formula’s default
args into the widget seed (parse_positional_arg,
parse_named_args), one validates the live session
value (validate_session_input), and one defines the
plain-R evaluation of the embellished expression
(embellish_eval). This is a hard break with no deprecation
shim: passing an old name now raises R’s “unused argument” error. The
internal registry-record keys are unchanged
(entry$default_arg / named_args /
validate_input / runtime); only the public
argument names changed.embellish_eval defaults to the new
embellish_identity() for value/consumer
placeholders (the source role keeps its abort-guard default). Two
built-in helpers ship for the plain-R evaluation slot:
embellish_identity() (the identity
function(x, ...) x) and
embellish_symbol_to_string() (NSE-captures its argument and
returns column names as a character vector, so a column-selecting
consumer works inside a tidyselect verb when the placeholder-embellished
formula is run as ordinary R with no app).ptr_arg_numeric_vector() was removed.
Use ptr_arg_numeric(vector = TRUE). The argument-validator
factories (ptr_arg_numeric / ptr_arg_string /
ptr_arg_symbol_or_string) gained a
vector = FALSE flag (with length = honored in
vector mode for ptr_arg_numeric), and a new
ptr_arg_symbol() factory parses a bareword column name
(vector-capable) for multi-column positional defaults.ptr_app_bslib() and ptr_app_grid()
are no longer exported (changed to
@keywords internal). They remain available internally and
their help pages are still generated so existing cross-reference links
resolve, but they are no longer presented as public API; the public
turn-key entry point is ptr_app(). Write a thin wrapper on
the public primitives for a custom page shell or theme.ptr_wrap_placeholder_addin() wraps a highlighted selection
in a placeholder from the RStudio command palette (classifies the
selection, offers a placeholder picker, rewrites in place;
dark/light-aware via rstudioapi::getThemeInfo()).@examples for the formula-capturing entry points
(ptr_app, ptr_ui_controls,
ptr_ui_page, ptr_server) lead with the
unquoted ggplot expression (the canonical input), keeping the string
form as the documented fallback; Shiny examples are gated with
if (interactive()).~ formula is treated as a first-class atomic
in the canonical-pipeline lift, so the resugar pass no longer
shreds a formula inside ppExpr
(e.g. lm(ppExpr(y ~ x), data = ppUpload(d))); placeholders
inside the formula are still detected and wired because
translate_call() recurses into every argument
independently.~ (gear ~ cyl) instead of falling
through to a prefix ~(gear, cyl), and an explicit
parenthesis node (x) now renders as a balanced
(x) instead of the unparseable ((x).ptr_shared() now accepts quoted ggplot
expressions as formulas elements, alongside
strings. Each element of formulas may be a formula
string (as before) or a quoted expression built with
rlang::expr() / quote(); quoted expressions
are deparsed to their source and the two forms are interchangeable,
including in a single mixed list. This brings the multi-formula
coordinator in line with the string-or-expression input already accepted
by ptr_app() / ptr_server(). A built ggplot
object (whose source text is unrecoverable) is rejected with a message
pointing at expr(). Additive and fully backward-compatible
— existing all-string call sites are unchanged. (As with the
single-formula entries, a native pipe |> inside a quoted
expression is desugared by R before capture and so does not survive into
the generated code panel.)ptr_define_placeholder_source() no
longer accepts a
companion_id_fn = function(id) -> string callback; pass
shortcut = TRUE (single logical, default
FALSE) to opt into the env-shortcut sibling input. The
framework stamps
node$shortcut_id <- paste0(node$id, "_shortcut") on
every translated source node (the old node$companion_id
slot and the "_name" suffix are gone). Built-in
ppUpload migrates verbatim. The reserved shared key
"shortcut" aborts translation with a named-conflict error.
Hard rename, no lifecycle::deprecate_warn() shim. The same
surface ships four further behaviours:
node$auto_name, stamped at translate time for non-shared
sources (node$default %||% node$id) and at runtime for
shared sources (paste0(obj$id, "_", key)). When the
shortcut textbox is empty, substitute_pass splices
as.name(node$auto_name) so the rendered call always
references a real binding.ppUpload, picking a
file synchronously clears the shortcut textbox (the auto-name takes over
as the binding name); typing into the textbox synchronously clears any
pending file. Typing always means “look up this object in the caller
env”, never “rename the uploaded payload”.<auto-name> <- read.csv("<filename>") (or
read.delim, readxl::read_excel,
jsonlite::fromJSON, … per extension) — so the rendered
snippet reads top-to-bottom as a self-contained script.state$bound_names[[key]],
state$eval_env[[key]], and
state$active_uploads[[key]]. ptr_spec() dumps
node$auto_name (not "") as the bound name
whenever the textbox is empty, so reloading a spec re-establishes the
same binding the running app saw.ptr_shared(..., id = ...) coordinators on
one page sharing a panel-owned source key no longer collide on bare DOM
ids. When a panel-shared
ppUpload(shared = '<key>') was rendered under a
coordinator with a non-NULL id, the inner
fileInput + shortcut textInput still bound at
the bare shared_<key> /
shared_<key>_shortcut ids — so a second coordinator
(id = "right") on the same page emitted duplicate ids and
silently failed to bind one of the two uploads. The rendered widget now
binds at the coordinator-namespaced ids
(left-shared_<key> /
right-shared_<key>), matching the sibling render
paths in invoke_build_ui() and
ptr_setup_source_uis(). Single-coordinator apps (no
id = supplied) are byte-for-byte unchanged.ptr_define_placeholder_source(shortcut = TRUE) reads the
shortcut textbox at every keystroke. Two implementation attempts at the
ADR’s proposed 400 ms debounce (shiny::debounce() on the
shortcut input read) both broke unrelated test contracts — the bind-path
delay broke test-rewrite-pipeline-data-source.R (6 FAIL)
and the mutex-file-reset race in
test-shared-source-panel-multi-instance.R (2 FAIL). The
debounce is therefore not shipped in this release. User-visible
impact: typing a partial, unresolvable name into the shortcut
textbox may briefly surface a transient
Object 'm' not found-style error in the inline error pane
during the typing burst; the bind settles once the user finishes typing.
A debounce-with-race-preservation variant is tracked as a follow-up; A1
(vacate-on-empty) is unaffected and ships in this release. -
validate_input hook signature unified across value
and consumer placeholders. Both
ptr_define_placeholder_value() and
ptr_define_placeholder_consumer() now accept
validate_input = function(value, ctx). The consumer’s
previous function(value, upstream_cols) signature is gone —
registrations that still use it fail loudly via
validate_hook() (“must accept argument(s): ctx”).
ctx is a plain list with named fields: node
(the placeholder AST node), keyword (alias for
node$keyword), upstream_cols (character vector
or NULL), and data (the upstream data frame or
NULL). For value placeholders,
ctx$upstream_cols and ctx$data are always
NULL (value placeholders have no upstream column scope by
definition); for consumer placeholders, both fields are
populated when the upstream resolves, letting data-aware validators
inspect column types / ranges / levels and not just column names.
Migration: rename the second formal from upstream_cols to
ctx and read ctx$upstream_cols in the body.
Value placeholders that previously had no validator gain the option to
declare one.
var /
num / text / expr /
upload to ppVar / ppNum /
ppText / ppExpr /
ppUpload. Every formula passed to
ptr_app() / ptr_server() /
ptr_translate() must use the new vocabulary. The
pp-prefix avoids shadowing base R / ggplot2 names
(stats::var(), graphics::text(),
base::expression, etc.) and lets the same symbols double as
identity / guard plain-R functions exported from the package — so
ggplot(mtcars, aes(x = ppVar(mpg))) works both inside
ptr_app() (as a placeholder) and as a standalone ggplot
call (ppVar is identity, so
aes(x = ppVar(mpg)) evaluates to
aes(x = mpg)). See ADR 0009 for the full rationale.
Migration: rename every placeholder call inside your formula strings or
unquoted-expression formulas.var, num, text,
expr, upload are no longer registered by
ggpaintr and no longer trigger the shadow-check guard, so users who
previously had custom placeholders blocked by built-ins of these names
can now register them without conflict.ggplot_1_1_var_NA is now
ggplot_1_1_ppVar_NA). Bookmarked URLs /
setBookmarkExclude allow-lists / setInputs()
test fixtures from previous versions will need to be updated.Default-value arguments and code-mode toggle (ADR
0009). Built-in placeholders now accept a positional argument
that seeds the widget’s initial value: ppVar(mpg) opens the
column picker pre-selected on mpg, ppNum(5)
seeds the numeric input at 5, ppText("hello") pre-fills the
text input, and ppExpr(factor(cyl)) pre-fills the
expression box. Defaults survive the rename round-trip:
ptr_render(root, preserve_placeholders = TRUE) re-emits the
original ppX(default) call rather than the substituted
value. The ptr_app() code panel gained a “Final code” /
“Show placeholders” toggle that switches between the two render
modes.
Custom placeholders can opt in to the default/named-args
schema. ptr_define_placeholder_value() /
ptr_define_placeholder_consumer() /
ptr_define_placeholder_source() now accept
default_arg = <validator> and
named_args = list(<name> = <validator>, ...).
Use the bundled validator factories —
ptr_default_symbol_or_string(),
ptr_default_numeric(),
ptr_default_numeric_vector(),
ptr_default_string(), ptr_default_expression()
— or pass a custom closure. See
?ptr_define_placeholder_value.
L3 — own every piece of the UI. Every piece of
ggpaintr’s public UI now has its own exported builder:
ptr_ui_header(), ptr_ui_controls(),
ptr_ui_plot(), ptr_ui_error(),
ptr_ui_code(), ptr_ui_code_toggle(), and
ptr_ui_assets(). Compose exactly the pieces you want, place
each anywhere in your own layout, and wire them with the existing server
API (ptr_server() / ptr_module_server()). The
bundled ptr_app() / ptr_module_ui() /
ptr_outputs_ui() / ptr_controls_ui() paths are
reimplemented as thin composites of these pieces — same
<body> DOM, no behavior or performance change. The
pieces are deliberately bare (no assets, no
.ptr-app wrapper); the new
ptr_ui_page() shell wraps them in a
Bootstrap page + the single .ptr-app theme scope + the
(deduped) asset bundle in one call, so that is all an L3 user has to
remember. Swap the page builder with page =
(fluidPage default, also
fixedPage/fillPage/bootstrapPage/basicPage);
for a navbarPage or bslib root use the documented
decomposition recipe. See vignette("ggpaintr-use-cases") §
“L3 — Own every piece of the UI”.
ptr_server() now accepts a
ptr_shared_server() bundle directly via
shared_state =, mirroring ptr_module_server().
Wiring a page-level ptr_shared_ui() panel to a single
embedded or custom-rendered plot no longer requires spreading the four
bundle slots through ....
The custom-renderer pattern (reading state$runtime()
for your own renderPlotly() / renderGirafe())
is now documented as an L2 capability — both ptr_server()
and ptr_module_server() return the ptr_state,
so it needs nothing beyond embedding.
ptr_register_plot() /
ptr_register_error() / ptr_register_code() are
no longer exported. Post-rewrite they only ever ran inside
ptr_server() (their sole caller, via the internal runtime
setup) and rendered nothing without it, so the “compose outputs
manually” use case they advertised was impossible. They remain as
internal helpers; no replacement is needed (use the L3 pieces +
ptr_server(), or read state$runtime() for a
custom renderer). No deprecation cycle — this dev cycle’s own surface,
single pre-1.0 user.htmltools::htmlDependency() bundles.
ggpaintr.css, the code-window JS, and the
ptr_set_class handler + stage CSS are emitted as two
dependencies (ggpaintr, ggpaintr-layer)
instead of inline
<style>/<script>/<link>
tags. htmltools collapses each to a single <head>
injection no matter how many shells/pieces a page nests, so the old
window.__ptr_*_registered JS guards are gone. Rendered
<body> is unchanged; only <head>
asset emission differs. htmltools moves from Suggests to
Imports.placeholders = ptr_merge_placeholders(...) argument has
been removed from every public entry point (ptr_app(),
ptr_app_bslib(), ptr_app_grid(),
ptr_module_ui(), ptr_module_server(),
ptr_server(), ptr_translate(),
ptr_render()). Custom placeholders are now defined once per
R session via
ptr_define_placeholder_value() / _consumer() / _source(),
which write directly into an internal global registry. All instances in
that session see the same registry.
build_ui(node, label = NULL, ...) for value/source,
build_ui(node, cols, label = NULL, selected = character(0), ...)
for consumer. The id is on node$id (and, for sources
registered with shortcut = TRUE, the sibling-input id on
node$shortcut_id — see ADR 0025); there is no separate
id argument. resolve_expr(value, node, ...)
returning NULL is the new “drop this argument” signal
(replacing ptr_missing_expr()).ptr_define_placeholder_*() (caller-driven), or
auto-register from .onLoad (drop-in). Either pattern works;
pick by whether you want the registration to be visible at the call
site.ptr_module_server()
instances in the same Shiny app cannot have different
implementations of the same keyword. The second registration overwrites
the first (with a cli::cli_warn notice). If you need
divergent widgets per instance, prefix custom keywords with a
package-specific tag (mypkg_pct rather than
pct). A scoped per-instance registry override is on the
table for a future release.ptr_registry_clear() +
ptr_register_builtins() (currently internal) or run in a
fresh R session.annotation_custom(), annotation_map(),
annotation_raster() (their required args make zero-arg
calls error in stock ggplot2 — dropping prevents the crash);
aes(), aes_(), aes_q(),
aes_string(), vars() (empty mapping helpers
are render-identical to “absent” via inherit.aes); and
element_text(), element_line(),
element_rect(), element_point(),
element_polygon(), element_geom() (empty form
inherits from the active theme element). This means
e.g. geom_point(aes(colour = var)) with var
missing now reduces to + geom_point() (clean) instead of
+ geom_point(aes()). element_blank() is
intentionally excluded — empty form is a meaningful “suppress”
directive, not a no-op.theme(),
labs(),
xlab/ylab/ggtitle,
facet_wrap/facet_grid/facet_null,
xlim/ylim/lims,
expand_limits, guides, annotate)
or in the new safe_to_remove argument on
ptr_app(), ptr_app_bslib(),
ptr_app_components(), ptr_server(),
ptr_module_server(), ptr_server_state(),
ptr_complete_expr(), and ptr_exec(). Empty
calls whose name is not in the set (e.g. third-party helpers like
pcp_theme(), pcp_arrange(), or user-authored
aes_pcp()) are preserved by default — being absent from the
set is the “removal safety unknown” signal. Pass
safe_to_remove = c("pcp_theme") to opt a specific name into
the cleanup pass.expr placeholder, when the user supplies an
expression, always wins over the cleanup pass: whatever the user typed
into an expr input is honoured verbatim, even if its
top-level name is in safe_to_remove. The intent (“I want
this here”) overrides the curated list.geom_*() and stat_*() layers are kept
empty regardless of safe_to_remove, since they inherit
aesthetics from ggplot(). So
geom_point(colour = var) with var missing
still renders as geom_point().+ labs(), + theme(), + guides()
calls are now dropped (no semantic change — they’re no-ops in stock
ggplot2). The previous build kept them via a “diff guard” that has been
removed in favour of the cleaner curated-list rule.ptr_resolve_layer_data() and ptr_ns_id()
are now exported with full docs. Custom data-aware placeholders should
call these helpers from a bind_ui() callback instead of
replicating the source-symbol walk or the namespace-function dance.?ptr_define_placeholder now documents the
bind_ui contract, the contents of the placeholder
context, and the meta$id namespacing rule. A
second runnable example demonstrates a data-aware “numeric-columns-only”
placeholder using the public helpers.ptr_gg_extra() — advanced helper for embedded apps that
own their own renderPlot({...}) block. Captures ggplot
components (themes, scales, coords, …) added on top of
ptr_extract_plot() and stores them on a new
ptr_state$extras reactiveVal, so the default code binder
(ptr_register_code()) appends them to the generated-code
pane. Extras are suppressed automatically when the underlying runtime
reports a failure, so stale extras from a prior successful draw never
surface during an error state. See
vignette("ggpaintr-extensibility"), Recipe 3.ptr_llm_primer(), ptr_llm_topic(),
ptr_llm_topics(), ptr_llm_register() —
ellmer-facing helpers that expose the bundled inst/llm/
primer and topic files as plain strings or as registered ellmer tools,
so coding assistants can fetch just-in-time ggpaintr guidance. See
vignette("ggpaintr-llm").upload placeholder now accepts .tsv,
.xlsx, .xls, and .json in
addition to .csv and .rds. JSON uploads must
be an array of records; nested objects are flattened, nested arrays
error out. Excel and JSON readers require the new suggested packages
readxl and jsonlite. The “show me the code”
pane reflects the format-appropriate reader (read.delim,
readxl::read_excel, jsonlite::fromJSON).ptr_generate_shiny(), ptr_register_export(),
the download button, and ids$export_button are gone. Use
the public Shiny integration surface (ptr_server_state(),
ptr_register_*(), ptr_input_ui(),
ptr_output_ui(), ptr_app_bslib()) to compose
and distribute custom apps.source_file,
source_package, source_function, and
on_missing are removed from
ptr_define_placeholder(). They only existed to support the
exporter. Hook functions should be defined inline.v0-pre-export-removal.ggpaintr
workflowptr_* prefix for conciseness (previously
ggpaintr_*)bind_* → register_*,
*_value → extract_*)copy_rules parameter/system to
ui_text throughoutbase::stop() to
rlang::abort()@examples to all exported functionsptr_generate_shiny() so the maintained public
call path is now
ptr_generate_shiny(ptr_obj, output_file, ...) Note:
ptr_generate_shiny() was removed in 0.9.0 — see breaking
changes above.archive/legacy-package/ggpaintr-first documentationR CMD check and
CRAN-oriented cleanupThese binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.