maidr (development version)
Bug Fixes
Shiny:
render_maidr()renders Base R plots again. It branched on the expression’s return value, which says nothing about whether anything was drawn –plot()returns NULL invisibly, so it was treated as an empty reactive and rendered a silent blank, whilebarplot()andhist()return a non-plot value and errored the output slot with “Input must be a ggplot object”. Only ggplot worked.render_maidr()now asks whether the expression actually recorded any drawing, so all three render. An expression that draws nothing and returns NULL still renders nothing.Base R: recorded plot calls now capture non-standard-evaluation arguments safely, so
curve(sin(x))no longer errors or records stale values; replay evaluates them in the original environment.Base R: a multi-panel grid is no longer destroyed by the idiomatic trailing
par(mfrow = c(1, 1))reset. A layout call issued after the last plot governs nothing that was drawn, so it no longer wins over the layout the panels were actually drawn under.Base R:
hist(x, plot = FALSE)/boxplot(x, plot = FALSE)are no longer recorded as plot calls (previously they injected phantom layers into the next render).Base R: extracted bar data now always matches the rendered SVG order.
barplot()no longer re-sorts labels alphabetically out of sync with the drawn bars, unnamed vectors keep call order, and the sorted arguments the wrapper draws are also what gets recorded and replayed.Base R: matrix
barplot()without an explicitbesideargument is now correctly detected as stacked (barplot’s default), not simple bars.Base R:
legend.textno longer contaminates stacked/dodged bar highlight selectors with the legend’s swatch rectangles.Base R:
barplot(horiz = TRUE)now emitsorientation = "horz"with value/label roles swapped so announcements and navigation are correct.Base R:
heatmap()now reproduces the dendrogram row/column reordering it draws with,image()no longer transposes rows and columns, both accept positional matrix arguments, and per-cell selectors fix the vertically mirrored cell highlighting.Base R:
hist()recomputation now honoursright,include.lowest, andnclass, and probability/density histograms announce densities instead of counts.Base R:
plot(v, type = "l"),lines(v), andplot(v)single-vector calls no longer crash or emit NA values; graphical parameters can no longer be mistaken for data arguments.Base R: box plot outlier highlights now follow the actual drawing order instead of assuming lower outliers are drawn first.
Base R: multiline selectors are ordered numerically, so plots with ten or more series map each series to the correct polyline.
Base R: multipanel handling fixes -
layout()-based grids are treated as multipanel, plots beyond the grid follow R’s new-page behaviour, plots drawn before the layout call are excluded, per-panelaxis()format configs stay per-panel, empty grid cells serialize as valid subplots, and processor fields (orientation, domMapping) are preserved.Base R: fallback replay to the native device now replays
par()/layout()calls in original order and strips maidr-internal arguments.Base R:
title/subtitleextraction no longer partial-matches unrelated arguments (e.g.subset) and tolerates non-character values.Base R: unsupported data-bearing overlays (
polygon(),rect(),segments(), …) now trigger the documented fallback instead of being silently dropped from the accessible output.Base R:
plot(y ~ x, data = d, subset = ...)andboxplot(y ~ g, data = d, subset = ...)work again. The formula methods resolvesubsetrelative toparent.frame(), which the recording wrapper displaced, so these calls failed with “object ‘g’ not found” and “..3 used in an incorrect context” even though they work in plain R.Base R:
matplot(m)on a matrix again emits one series per column. Routing a lone matrix throughxy.coords()read a two-column matrix as an x/y pair, so every series but one vanished from the accessible output and the first series’ values were announced as x coordinates.Base R:
barplot(x, plot = FALSE)is no longer recorded as a plot call. The rule was applied to the generic wrapper only, andbarplot()has its own code path.Base R:
cancel_auto_show()removes its task callback by name. It used the indexaddTaskCallback()returned, which is a position in R’s callback list, so an unrelated package’s callback could be removed instead.Base R:
chartSeries()calls are now recorded even when ‘quantmod’ is loaded after ‘maidr’.ggplot2: faceted box plots, violins, histograms, smooths, heatmaps, and stacked/dodged bars no longer crash with “unused arguments”; extracted data and selectors are now scoped to each facet panel.
ggplot2: dodged bars accept expression aesthetics such as
aes(fill = factor(cyl)). Aesthetics were resolved withrlang::as_label()and used as column names, sodata[["factor(cyl)"]]wasNULLand the plot died with “all arguments must have the same length”. Aesthetics are now evaluated against the data, which also makes the row ordering match the drawn bars for expression aesthetics.ggplot2: each faceted box plot panel is announced with its own category names. Names were read from an unfiltered vector of axis positions indexed by position within the panel, so a panel drawing its boxes at positions 3 and 4 was labelled with the categories at positions 1 and 2 — correct quartiles under the wrong names.
ggplot2: nested patchwork layouts (e.g.
(p1 | p2) / p3) no longer drop panels from the accessible output.ggplot2: histogram and smooth layers extract their own layer’s data instead of the first similarly-shaped layer in multi-layer plots.
ggplot2: scatter point colour/grouping values are emitted again, faceted plots included. The category lookup compared a panel-filtered row count against the whole data set; the two never matched under faceting, so every faceted scatter announced raw hex codes (
#F8766D) instead of category names.ggplot2: each faceted heat map panel reports its own cell values. Cells were looked up in the unfiltered source data by (x, y) label, which matches a row in every panel, and the first match won — so all panels reported panel 1’s values.
Base R:
layout()matrices no longer count their empty0cells as panels when tracking device state, matching the panel grouping code.ggplot2: heatmap axis mappings follow factor level order instead of data-appearance order.
ggplot2:
maidr_off()now really disables RMarkdown interception, PDF and LaTeX output keep their figures, a secondmaidr_on()no longer risks infinite hook recursion, and non-HTML output uses the original knitr plot hook instead of hardcoding the markdown hook.Shiny/widgets:
show(as_widget = TRUE)and widget rendering now support Base R plots,render_maidr()renders nothing (instead of erroring) for NULL reactives, and device storage is cleared on the shiny/widget paths.Rendering: maidr-data JSON keeps full numeric precision (previously values were silently rounded to 4 decimal digits),
NAhandling is unchanged, and non-ASCII text in iframes is UTF-8 safe on all locales.Rendering: plot IDs no longer consume random numbers, preserving
set.seed()reproducibility of user scripts.maidr_set_fallback()now keeps unspecified settings instead of silently resetting them to defaults.Scale label mapping keys labels by actual break positions, fixing mislabeled categories with custom break subsets.
Enhancements
- CDN assets are pinned to the bundled MAIDR.js version instead of
@latest, so the emitted schema and frontend can no longer drift apart. - Iframe height auto-resize now also works in RMarkdown documents (the postMessage listener previously shipped only with the htmlwidgets binding).
Performance
- ggplot2 plots are built once per render instead of three times; the faceted and patchwork paths reuse the built plot instead of rebuilding it per layer.
- Base R renders reuse the replayed gtable instead of re-replaying every recorded call on each access.
- Candlestick SVG post-processing parses the document once instead of five times.
- The multi-megabyte bundled JS/CSS assets are read once per session instead of once per rendered plot, and the offline-detection probe is cached per session instead of hitting the network for every plot.
maidr 0.4.0
CRAN release: 2026-07-10
New Features
- Added candlestick (OHLC) chart support for ‘ggplot2’ via the ‘tidyquant’ package’s
geom_candlestick(). Each candle is exposed as a single navigable element withopen,high,low,close, optionalvolume, and computedtrend(Bull / Bear / Neutral) andvolatility(high − low) fields. - Added Base R candlestick (OHLC) chart support via
quantmod::chartSeries(x, type = "candlesticks"). The xts/zoo input is validated withquantmod::has.OHLC()and each row is emitted as a navigableCandlestickPointwithvalue(ISO date),open,high,low,close, computedtrend(Bull / Bear / Neutral) andvolatility(high − low) fields, plus optionalvolumewhenquantmod::has.Vo()isTRUE.
maidr 0.2.0
CRAN release: 2026-03-07
New Features
- Added violin plot support for ‘ggplot2’ (
geom_violin()), including both vertical and horizontal orientations. - Violin plots produce two interactive layers: a box-summary layer (
violin_box) with min, Q1, median, Q3, max highlights, and a KDE density-curve layer (violin_kde) with navigable density points. - Added Ramer-Douglas-Peucker (RDP) curve simplification to reduce KDE density points to ~30 per violin while preserving shape fidelity.
- SVG coordinate injection for violin KDE points enables accurate highlight positioning in the maidr frontend.
Enhancements
- Renamed option
maidr.enabledtomaidr.auto_showfor clarity. - Added
domMapping.iqrDirectionsupport for violin box layers, aligning with the existing box plot pattern for correct Q1/Q3 highlighting under gridSVG Y-flip transforms. - Added plot augmentation API (
augment_plot(),needs_augmentation()) to theLayerProcessorbase class, enabling processors to inject additional geom layers before rendering. - Added multi-layer expansion in the orchestrator for plot types that produce more than one maidr layer from a single geom.
Documentation
- Added violin plot examples to
show(),save_html(), vignettes, and example scripts. - Updated DESCRIPTION to list violin plots as a supported type.
maidr 0.1.1
Resubmission after CRAN archival. Fixes CRAN policy compliance issues.
Bug Fixes
- Removed all
assign(..., envir = .GlobalEnv)calls that violated CRAN policy. Base R function wrappers are now installed into the package namespace during.onLoadand controlled via an active/inactive flag, eliminating any modification of the user’s global environment. - Removed
attach()usage that produced R CMD check NOTE. - Fixed Rd documentation warning caused by unicode escape sequences in
prefix_to_currency_codeparameter documentation.
maidr 0.1.0
Initial CRAN release.
Features
-
show()- Display interactive, accessible visualizations from ggplot2 or Base R plots with keyboard navigation and screen reader support -
save_html()- Export accessible visualizations to standalone HTML files -
render_maidr()andmaidr_output()- Shiny integration for interactive web applications
Supported Plot Types
ggplot2 - Basic
- Bar charts (
geom_bar(),geom_col()) - Grouped/dodged bar charts (
position = "dodge") - Stacked bar charts (
position = "stack") - Histograms (
geom_histogram()) - Line plots (
geom_line()) - Scatter plots (
geom_point()) - Box plots (
geom_boxplot()) - Heatmaps (
geom_tile()) - Smooth/density curves (
geom_smooth(),geom_density())
ggplot2 - Advanced
- Faceted plots (
facet_wrap(),facet_grid()) - Multi-panel layouts with patchwork package
- Multi-layered plots (e.g., histogram + density, scatter + smooth)
Base R - Advanced
- Multi-panel plots (
par(mfrow),par(mfcol)) - Faceted-style plots (using
par()with loops) - Multi-layered plots (sequential plotting calls)