<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Blog on Posit Open Source</title>
    <link>https://opensource.posit.co/blog/</link>
    <description>Recent content in Blog on Posit Open Source</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <lastBuildDate>Fri, 19 Jun 2026 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://opensource.posit.co/blog/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>AI Newsletter: LLMs are getting much better at interpreting counterintuitive plots</title>
      <link>https://opensource.posit.co/blog/2026-06-19_ai-newsletter/</link>
      <pubDate>Fri, 19 Jun 2026 00:00:00 +0000</pubDate>
      <guid>https://opensource.posit.co/blog/2026-06-19_ai-newsletter/</guid>
      <dc:creator>Sara Altman</dc:creator>
      <dc:creator>Simon Couch</dc:creator><description><![CDATA[<p>Around a year ago, we noticed a concerning behavior in the LLMs we built agents for data analysis with: when models were asked to make and interpret a plot that showed a counterintuitive trend, the <a href="https://posit.co/blog/introducing-bluffbench" target="_blank" rel="noopener">models did not faithfully interpret the plot</a>.</p>
<p><div class="not-prose"><figure>
    <img class="h-auto max-w-full rounded-lg"
      src="https://opensource.posit.co/blog/2026-06-19_ai-newsletter/images/original-bluffbench-results.png"
      alt="Three-panel bar chart of the initial bluffbench results for Claude Sonnet 4.5, Gemini Pro 2.5, and GPT-5. In the mocked condition, all three models score in the single digits; in the intuitive condition they score in the 50–65% range; in the baseline condition, 67–85%."  title="Initial bluffbench results from November 2025." 
      loading="lazy"
    ><figcaption class="text-sm text-center text-gray-500">Initial bluffbench results from November 2025.</figcaption>
  </figure></div>
</p>
<p>In the &ldquo;mocked&rdquo; condition, we secretly tamper with well-known datasets, like <code>mtcars</code>, so that when the model plots the data, the trend shown is not what it expects. This is especially adversarial and not representative of the use case we&rsquo;re most concerned about, the more realistic &ldquo;intuitive&rdquo; condition. For these samples, we synthesized data, but named the datasets and variables to suggest an intuitive relationship that the actual data contradicts. Finally, the &ldquo;baseline&rdquo; condition asks models to interpret plots with generic column names, and the results establish that models can &lsquo;see&rsquo; just fine.</p>
<p><div class="not-prose"><figure>
    <img class="h-auto max-w-full rounded-lg"
      src="https://opensource.posit.co/blog/2026-06-19_ai-newsletter/images/students.png"
      alt="Two scatterplots of exam score versus weekly study hours demonstrating the bluffbench intervention.  The left panel, labeled &lsquo;Intuitive relationship,&rsquo; shows a gentle positive trend from about 60 to 75 as study hours increase. The right panel, labeled &lsquo;Bluffbench relationship,&rsquo; shows a flat trend around 60–70 that jumps sharply above 20 hours, with scores reaching near 100, creating a discontinuity a model might not expect."  title="Example of an intuitive sample. The model might expect a positive association between study time and exam score, but the bluffbench dataset shows a discontinuity." 
      loading="lazy"
    ><figcaption class="text-sm text-center text-gray-500">Example of an intuitive sample. The model might expect a positive association between study time and exam score, but the bluffbench dataset shows a discontinuity.</figcaption>
  </figure></div>
</p>
<p>We <a href="https://posit.co/blog/llm-plot-interpretation" target="_blank" rel="noopener">tried all sorts of things to drive those scores up</a>, to little effect. We let the model write a &lsquo;memo&rsquo; to itself in a private scratchpad, enabled thinking, introduced a &ldquo;model-in-the-middle&rdquo; that pre-interprets the plot (so that the main agent sees only text), and even prefilled the response so that the agent was forced to say the correct interpretation (which it would then directly contradict).</p>
<!-- Source: https://posit.co/blog/llm-plot-interpretation (intervention-comparison-2.png) -->
<p><div class="not-prose"><figure>
    <img class="h-auto max-w-full rounded-lg"
      src="https://opensource.posit.co/blog/2026-06-19_ai-newsletter/images/intervention-comparison.png"
      alt="Two-panel bar chart of interventions run with Claude Opus 4.5. In the mocked condition, none of the interventions (memo, extended thinking, model-in-the-middle) lift accuracy above ~10%; in the intuitive condition there&rsquo;s modest improvement over the no-intervention baseline." 
      loading="lazy"
    >
  </figure></div>
</p>
<div class="callout callout-note" role="note" aria-label="Note">
<div class="callout-body">
<p>One promising finding was that, even if axis labels could activate the priors, models without access to the rest of the conversation history could still reliably interpret plots when told to ignore axis labels.</p>
</div>
</div>
<h2 id="bluffbench-is-near-saturation">bluffbench is near saturation
</h2>
<p>As is often the case, the best approach to drive these eval scores up was to wait a few months. A couple months ago, with the releases of Gemini 3.5 Flash and Opus 4.8, we saw our first &gt;50% scores on the hardest &ldquo;mocked&rdquo; case in the eval. Then, last week, Fable 5 nearly aced the &ldquo;mocked&rdquo; case; almost all of the samples that it failed on had triggered the biology classifier and fallen back to Opus 4.8.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p><div class="not-prose"><figure>
    <img class="h-auto max-w-full rounded-lg"
      src="https://opensource.posit.co/blog/2026-06-19_ai-newsletter/images/near-saturation-results.png"
      alt="Three-panel bar chart of recent bluffbench results across the mocked, intuitive, and baseline conditions, with models ordered by mocked-condition performance. Claude Fable 5 (medium) leads the mocked condition at ~73%, followed by Gemini 3.5 Flash (high) and Claude Opus 4.8 (high); scores on the intuitive and baseline conditions are uniformly high." 
      loading="lazy"
    >
  </figure></div>
</p>
<p>There&rsquo;s still certainly room for improvement here; any human data scientist would likely score near 100% on all three conditions. A score in the 70s is still meaningfully sub-human. That said, it doesn&rsquo;t seem like it will be long until bluffbench is saturated:</p>
<p><div class="not-prose"><figure>
    <img class="h-auto max-w-full rounded-lg"
      src="https://opensource.posit.co/blog/2026-06-19_ai-newsletter/images/mocked-frontier.png"
      alt="Scatter plot of the best mocked-case bluffbench score against model release date, showing only models that set a new record when released. The frontier climbs from Gemini Pro 2.5 (9%, March 2025) and Claude Sonnet 4.5 (12%, September 2025) through GPT-5.2 (33%, December 2025), Gemma4 26B A4B (39%, March 2026), and Gemini 3.5 Flash (67%, May 2026) to Claude Fable 5 (73%, June 2026)." 
      loading="lazy"
    >
  </figure></div>
</p>
<p>As is the fate of many saturated evals, we&rsquo;ll soon be using bluffbench primarily to identify models on the Pareto frontier: what is the cheapest model that can reliably interpret plots, even when the plotted results are counterintuitive? While it&rsquo;s certainly impressive that Fable 5 (medium) can interpret counterintuitive plots this well, driving all data analysis conversations with a model this expensive is not economically feasible for most of our users.</p>
<h2 id="theres-still-more-to-do">There&rsquo;s still more to do
</h2>
<p>Even though bluffbench itself is nearing saturation, the problem of LLM agents failing to reliably incorporate surprising evidence and subtle artifacts into their analyses remains an issue. We see this qualitatively in our own work, and also have recently been able to quantify this in evals we&rsquo;ll release soon.</p>
<p>Notably, the bluffbench harness and conversations are relatively contrived and unrealistic. There is no system prompt (by default) and the models receive only a single tool called <code>create_ggplot()</code>, which they use to create the plot that they&rsquo;re then immediately asked to interpret.</p>
<p>For actual coding agents, system prompts can be extensive. Posit Assistant&rsquo;s and Claude Code&rsquo;s stretch into the tens of thousands of tokens. And in contrast to bluffbench, where the plot interpretation task appears in the first user message, real agents might create and interpret plots hundreds of turns into a conversation, and there&rsquo;s evidence that performance may degrade as conversations go on.</p>
<p>Further, the user messages in the bluffbench eval are quite &ldquo;clean.&rdquo; They are composed only of the user&rsquo;s relatively clear directive, with no &ldquo;fluff&rdquo; automatically injected by the harness. In most real coding agents, there are all sorts of hidden information and system reminders appended to the user message that are broadly unrelated to what the user is requesting in most situations, and agents need to ignore it.</p>
<p>Because of this lack of realism, and the fact that the eval and associated writing may now appear in the training data of newer models, models may know they are being evaluated in bluffbench. This risks &lsquo;sandbagging&rsquo;, where LLMs alter their behavior in situations where they suspect they&rsquo;re being evaluated.</p>
<p>We&rsquo;re seeing that in long-context, multi-turn, messy situations, even the most capable frontier models still struggle to notice counterintuitive patterns shown in data. This will continue to inform the design of our own data agents, which we&rsquo;ve intentionally made auditable and less-autonomous than many other agents on the market: we continue to believe that there are not yet models and harnesses capable of highly independent, green-field data analyses.</p>
<h2 id="posit-assistants-harness-helps">Posit Assistant&rsquo;s harness helps
</h2>
<p>That said, we&rsquo;ve been encouraged (and somewhat surprised) to see that Posit Assistant&rsquo;s harness seems to improve agents&rsquo; ability to interpret counterintuitive plots. Through some confluence of factors arising from the agents&rsquo; prompting, Posit Assistant scores more highly on bluffbench than the minimally sufficient harness from the open source eval:</p>
<p><div class="not-prose"><figure>
    <img class="h-auto max-w-full rounded-lg"
      src="https://opensource.posit.co/blog/2026-06-19_ai-newsletter/images/harness-comparison.png"
      alt="Grouped bar chart comparing bluffbench accuracy on the intuitive condition under Posit Assistant&rsquo;s harness versus a minimal harness, for Claude Haiku 4.5, Sonnet 4.6, Sonnet 4.6 (medium), and Opus 4.8 (medium). Posit Assistant&rsquo;s harness scores higher for every model except Haiku 4.5, where the two are roughly even." 
      loading="lazy"
    >
  </figure></div>
</p>
<div class="callout callout-note" role="note" aria-label="Note">
<div class="callout-body">
<p>The scaffold that assembles Posit Assistant&rsquo;s prompting is not open source. That said, we&rsquo;ve instructed the agent to freely share its prompting; you&rsquo;re welcome to ask the agent what the version of prompting available in your session looks like.</p>
</div>
</div>
<p>This correctness boost also holds up in comparison to another popular coding agent. Coupled with the UI affordances that make it easier for users to audit the agent&rsquo;s code and results, we&rsquo;re excited to see that Posit Assistant seems to be a step forward in what&rsquo;s possible for correct agent-assisted data analysis.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://www.anthropic.com/news/claude-fable-5-mythos-5" target="_blank" rel="noopener">Per Anthropic</a>, &ldquo;When Fable’s classifiers detect a request related to cybersecurity, biology and chemistry, or distillation, the response is automatically handled by Claude Opus 4.8 instead.&rdquo;&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></description>
      <enclosure url="https://opensource.posit.co/blog/2026-06-19_ai-newsletter/images/featured.png" length="611941" type="image/png" />
    </item>
    <item>
      <title>Introducing debrief: profiling summaries for AI agents</title>
      <link>https://opensource.posit.co/blog/2026-06-18_debrief-0-1-0/</link>
      <pubDate>Thu, 18 Jun 2026 00:00:00 +0000</pubDate>
      <guid>https://opensource.posit.co/blog/2026-06-18_debrief-0-1-0/</guid>
      <dc:creator>Emil Hvitfeldt</dc:creator><description><![CDATA[<p>While AI agents are getting better at writing code,
they sometimes struggle with making the code faster.
We have a number of tools at our disposal to do this,
but some of them are not made with AI agents in mind.
The <a href="https://profvis.r-lib.org/" target="_blank" rel="noopener">profvis</a> package is one such example.
The interactive flame graph it produces is fantastic for human eyes,
it allows us to easily see the broad picture while also drilling down into the details.
This is what started the idea for this new package; debrief.
Allowing the AI agent to read the profvis results as text,
thus letting it make more informed decisions about how to optimize the code.</p>
<p>debrief is on CRAN:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="nf">install.packages</span><span class="p">(</span><span class="s">&#34;debrief&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># or the development version:</span>
</span></span><span class="line"><span class="cl"><span class="n">pak</span><span class="o">::</span><span class="nf">pak</span><span class="p">(</span><span class="s">&#34;r-lib/debrief&#34;</span><span class="p">)</span></span></span></code></pre></div></div>
<h2 id="from-guessing-to-measuring">From guessing to measuring
</h2>
<p>Prompt an AI agent to make your R code faster,
and it will either declare that a bottleneck exists without evidence,
or try to use <code>Rprof()</code> directly.
We find that <code>profvis()</code> is better and easier to use than <code>Rprof()</code> directly,
as it provides a more user-friendly interface and visualizations.
But that interface is designed for human eyes,
not for an LLM reading a transcript.</p>
<p>This is where debrief comes in.
It turns the output of <code>profvis()</code> into a text-based summary that can be easily read and understood by AI agents.
Every function is prefixed <code>pv_</code> (for profvis) and returns text designed to be read top to bottom:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="nf">library</span><span class="p">(</span><span class="n">debrief</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">p</span> <span class="o">&lt;-</span> <span class="n">profvis</span><span class="o">::</span><span class="nf">profvis</span><span class="p">(</span><span class="nf">slow_function</span><span class="p">(</span><span class="n">data</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="nf">pv_print_debrief</span><span class="p">(</span><span class="n">p</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt; ## PROFILING SUMMARY</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt; Total time: 190 ms (19 samples @ 10 ms interval)</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt; Source references: available</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt; ### TOP FUNCTIONS BY SELF-TIME</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;    110 ms ( 57.9%)  paste</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;     20 ms ( 10.5%)  &lt;GC&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;     10 ms (  5.3%)  .bincode</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;     10 ms (  5.3%)  any</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;     10 ms (  5.3%)  anyDuplicated.default</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;     10 ms (  5.3%)  apply</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;     10 ms (  5.3%)  rnorm</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;     10 ms (  5.3%)  unlist</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt; ### TOP FUNCTIONS BY TOTAL TIME</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;    180 ms ( 94.7%)  FUN</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;    180 ms ( 94.7%)  lapply</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;    180 ms ( 94.7%)  process_data</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;    140 ms ( 73.7%)  summarize_data</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;    110 ms ( 57.9%)  paste</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;     30 ms ( 15.8%)  clean_data</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;     20 ms ( 10.5%)  &lt;GC&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;     20 ms ( 10.5%)  apply</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;     10 ms (  5.3%)  .bincode</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;     10 ms (  5.3%)  [.data.frame</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt; ### HOT LINES (by self-time)</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;    120 ms ( 63.2%)  analysis.R:22</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;                    list(</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;     10 ms (  5.3%)  analysis.R:9</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;                    x &lt;- rnorm(n)</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt; ### HOT CALL PATHS</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt; 110 ms (57.9%) - 11 samples:</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;     lapply</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;   -&gt; FUN</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;   -&gt; process_data</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;   -&gt; summarize_data (analysis.R:5)</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;   -&gt; paste (analysis.R:22)</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt; 10 ms (5.3%) - 1 samples:</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;     base::tryCatch</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;   -&gt; any</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt; 10 ms (5.3%) - 1 samples:</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;     lapply</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;   -&gt; FUN</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;   -&gt; process_data</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;   -&gt; clean_data (analysis.R:4)</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;   -&gt; [.data.frame (analysis.R:15)</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;   -&gt; anyDuplicated.default</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt; 10 ms (5.3%) - 1 samples:</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;     lapply</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;   -&gt; FUN</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;   -&gt; process_data</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;   -&gt; clean_data (analysis.R:4)</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;   -&gt; cut.default</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;   -&gt; .bincode</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt; 10 ms (5.3%) - 1 samples:</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;     lapply</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;   -&gt; FUN</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;   -&gt; process_data</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;   -&gt; clean_data (analysis.R:4)</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;   -&gt; scale.default</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;   -&gt; apply</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;   -&gt; FUN</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;   -&gt; &lt;GC&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt; ### MEMORY ALLOCATION (by function)</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;    51.28 MB paste</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;    10.01 MB anyDuplicated.default</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;     2.90 MB any</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;     1.62 MB rnorm</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt; ### MEMORY ALLOCATION (by line)</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;    51.28 MB analysis.R:22</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;             list(</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;     1.62 MB analysis.R:9</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;             x &lt;- rnorm(n)</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt; ### Next steps</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt; pv_focus(p, &#34;paste&#34;)</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt; pv_source_context(p, &#34;analysis.R&#34;)</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt; pv_suggestions(p)</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt; pv_help()</span></span></span></code></pre></div></div>
<p><code>pv_print_debrief()</code> is meant as the first entry point after profiling.
It gives a comprehensive overview of the profile with a number of sections.</p>
<ul>
<li>Top functions by self-time and total time</li>
<li>Hot lines and hot call paths</li>
<li>Memory allocation by function and by line</li>
<li>Next steps</li>
</ul>
<p>Both the hot lines and hot paths sections link back to source references when available,
Especially relevant if the bottleneck is happening in a common function name like <code>paste()</code> that is scattered across your package.
With this, we know exactly which <code>paste()</code> call is the culprit.</p>
<p>Each function in {debrief} also prints the name of the possible next functions to run,
so the assistant can drill down into the details.</p>
<p>debrief is a new package.
Its output almost certainly isn&rsquo;t part of any model&rsquo;s training data.
And output is also very unlikely to be added,
as profiling code is rarely committed.
This means that the package tries to be self-documenting by design,
With the hope that an AI agent doesn&rsquo;t need to read the documentation to understand how to use it.</p>
<h2 id="getting-an-agent-into-the-session">Getting an agent into the session
</h2>
<p>While you can use debrief with any AI agent,
it works better with agents that keep a persistent R session.
Otherwise, it will need to rerun the profile every time it wants to check the results of an edit.
You also won&rsquo;t be able to take full advantage of the &ldquo;Next steps&rdquo; hints,
which are designed to guide the agent through drilling down into the profile.</p>
<p><a href="https://posit.com/assistant" target="_blank" rel="noopener">Posit Assistant</a> is the easiest way to do this.
It runs in the same session as you, so you can be a bigger part of the process.
The same assistant runs in Positron, RStudio, or the terminal,
so you can profile interactively or drive the whole loop headlessly from the <code>pa</code> CLI.</p>
<p>If you aren&rsquo;t using RStudio or Positron, or prefer an agent like Claude Code or Codex,
<a href="https://github.com/posit-dev/mcp-repl" target="_blank" rel="noopener">mcp-repl</a> gives you the same thing.
It&rsquo;s an MCP server that hands any MCP-capable agent a persistent R (or Python) session,
so you can load debrief and run the profiler once, then drill down as needed.</p>
<h2 id="trying-it-out">Trying it out
</h2>
<p>The point of debrief is to put profiling results in a form an agent can easily interact with,
so it can measure instead of guess and iterate toward faster code.
We have already used it this way:
<a href="https://github.com/tidymodels/textrecipes/pull/309" target="_blank" rel="noopener">tidymodels/textrecipes#309</a>
is a profiling-driven optimization of <code>step_word_embeddings()</code>,
where each round of profile, read, change, and re-measure was guided by debrief&rsquo;s summaries.</p>
<p>Install debrief, point an agent at a slow function, and let it profile.</p>
]]></description>
      <enclosure url="https://opensource.posit.co/blog/2026-06-18_debrief-0-1-0/featured.png" length="332953" type="image/png" />
    </item>
    <item>
      <title>pkgsite 0.1.0: Convert your `.Rd` files to Quarto</title>
      <link>https://opensource.posit.co/blog/2026-06-18_pkgsite-0-1-0/</link>
      <pubDate>Thu, 18 Jun 2026 00:00:00 +0000</pubDate>
      <guid>https://opensource.posit.co/blog/2026-06-18_pkgsite-0-1-0/</guid>
      <dc:creator>Edgar Ruiz</dc:creator><description><![CDATA[<p>We are happy to introduce <code>pkgsite</code>. It reads the compiled <code>.Rd</code> files in your
R package and converts them into <code>.qmd</code> files. It can also automatically create
a reference index page that lists all your exported functions. You can customize
the format of both using templates, then let Quarto render the HTML. <code>pkgsite</code>
is now available on CRAN, to install use:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="nf">install.packages</span><span class="p">(</span><span class="s">&#34;pkgsite&#34;</span><span class="p">)</span></span></span></code></pre></div></div>
<p><code>pkgsite</code> is inspired by Python&rsquo;s <a href="https://machow.github.io/quartodoc/get-started/overview.html" target="_blank" rel="noopener"><code>Quartodoc</code></a>,
which does the same for Python packages.</p>
<h2 id="why-pkgsite">Why <code>pkgsite</code>?
</h2>
<p>The main website builder for R packages is <a href="https://pkgdown.r-lib.org/" target="_blank" rel="noopener"><code>pkgdown</code></a>.
It goes all the way to a finished, publication-ready HTML website in one step,
and for most packages that is exactly what you want. It is mature, has a large
ecosystem of themes and extensions, and just works.</p>
<p><code>pkgsite</code>, on the other hand, only creates <code>.qmd</code> files with the help content.
You decide how to structure the site around them, and Quarto handles the final
HTML output.</p>
<p>To see what that looks like, compare the
<a href="https://github.com/mlverse/mall/blob/main/r/man/llm_sentiment.Rd" target="_blank" rel="noopener">source <code>.Rd</code> file</a>
for <code>llm_sentiment()</code> in <code>mall</code> with the
<a href="https://github.com/mlverse/mall/blob/main/reference/llm_sentiment.qmd" target="_blank" rel="noopener"><code>.qmd</code> file <code>pkgsite</code> generated from it</a>.</p>
<p>Two cases where we have seen it make a difference are:</p>
<ul>
<li>Examples that require local resources</li>
<li>Unified R and Python Quarto sites</li>
</ul>
<h3 id="examples-that-require-local-resources">Examples that require local resources
</h3>
<p>Some packages depend on things that are not available on automated build and
publishing platforms such as GitHub Actions or Netlify: databases, large
language models, or Spark clusters. Running their examples there is not
feasible, yet you still want working, rendered documentation.</p>
<p>Quarto&rsquo;s <a href="https://quarto.org/docs/projects/code-execution.html#freeze" target="_blank" rel="noopener">freeze</a>
solves this cleanly. You render the site once locally where those resources are
available, commit the <code>_freeze/</code> folder alongside your source, and GitHub
rebuilds the site on every push without re-executing a single line of code.</p>
<p>The <a href="https://mlverse.github.io/mall/" target="_blank" rel="noopener"><code>mall</code></a> and
<a href="https://mlverse.github.io/lang/" target="_blank" rel="noopener"><code>lang</code></a> packages are concrete examples.
Their function examples call an LLM, so they cannot run on those platforms.
With <code>pkgsite</code> and freeze, rendering happens on a developer machine where the
model is accessible, and the frozen output travels with the repository.</p>
<p>In the <code>llm_sentiment()</code> function, the example section looks like this in the
resulting <code>.qmd</code> file:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="cl"><span class="gu">## Examples
</span></span></span><span class="line"><span class="cl">``<span class="sb">`{r}
</span></span></span><span class="line"><span class="cl"><span class="sb">library(mall)
</span></span></span><span class="line"><span class="cl"><span class="sb">
</span></span></span><span class="line"><span class="cl"><span class="sb">data(&#34;reviews&#34;)
</span></span></span><span class="line"><span class="cl"><span class="sb">
</span></span></span><span class="line"><span class="cl"><span class="sb">llm_use(&#34;ollama&#34;, &#34;llama3.2&#34;, seed = 100, .silent = TRUE)
</span></span></span><span class="line"><span class="cl"><span class="sb">
</span></span></span><span class="line"><span class="cl"><span class="sb">llm_sentiment(reviews, review)
</span></span></span><span class="line"><span class="cl"><span class="sb">
</span></span></span><span class="line"><span class="cl"><span class="sb"># Use &#39;pred_name&#39; to customize the new column&#39;s name
</span></span></span><span class="line"><span class="cl"><span class="sb">llm_sentiment(reviews, review, pred_name = &#34;review_sentiment&#34;)
</span></span></span><span class="line"><span class="cl"><span class="sb">`</span>``</span></span></code></pre></div></div>
<p>And this is what the <a href="https://mlverse.github.io/mall/reference/llm_sentiment.html#examples" target="_blank" rel="noopener">rendered page</a> looks like on the <code>mall</code> website:</p>
<p><div class="not-prose"><figure>
    <img class="h-auto max-w-full rounded-lg"
      src="https://opensource.posit.co/blog/2026-06-18_pkgsite-0-1-0/llm-sentiment-page.png"
      alt="The rendered llm_sentiment() reference page on the mall website, showing the function description and executed example output."  title="Examples rendered locally using Quarto freeze" 
      loading="lazy"
    ><figcaption class="text-sm text-center text-gray-500">Examples rendered locally using Quarto freeze</figcaption>
  </figure></div>
</p>
<h3 id="unified-r-and-python-sites">Unified R and Python sites
</h3>
<p>If your project ships both an R package and a Python package, you can combine
<code>pkgsite</code>&rsquo;s output with
<a href="https://machow.github.io/quartodoc/get-started/overview.html" target="_blank" rel="noopener"><code>Quartodoc</code></a>&rsquo;s
output into a single Quarto website. Both tools write <code>.qmd</code> reference pages
that Quarto assembles together, giving R and Python users a consistent
experience on one site. The <code>mall</code> package&rsquo;s
<a href="https://mlverse.github.io/mall/reference/" target="_blank" rel="noopener">reference section</a> is a live
example: R and Python pages side by side, built from two different tools,
served as one site.</p>
<h2 id="getting-started">Getting started
</h2>
<p>From within your package directory, one function call does the work:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="nf">library</span><span class="p">(</span><span class="n">pkgsite</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nf">write_reference</span><span class="p">()</span></span></span></code></pre></div></div>
<p><code>write_reference()</code> creates a <code>reference/index.qmd</code> that links to all your
exported functions, and converts each <code>.Rd</code> file in <code>man/</code> into its own <code>.qmd</code>
reference page.</p>
<p>You can customize its behavior through arguments. For example, if you want to
skip running the examples when Quarto renders the reference pages:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="nf">write_reference</span><span class="p">(</span><span class="n">examples</span> <span class="o">=</span> <span class="kc">FALSE</span><span class="p">,</span> <span class="n">not_run_examples</span> <span class="o">=</span> <span class="kc">FALSE</span><span class="p">)</span></span></span></code></pre></div></div>
<p>Calling it without arguments reads any configuration from a <code>pkgsite:</code> section
at the top level of <code>_quarto.yml</code>. Following the same convention as
<a href="https://machow.github.io/quartodoc/get-started/overview.html" target="_blank" rel="noopener"><code>Quartodoc</code></a>,
the Python equivalent, this is where you set the package root, the output
folder, templates, and optionally how functions are grouped and ordered in the
index:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">pkgsite</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">dir</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;.&#34;</span><span class="w">                    </span><span class="c"># path to the package root</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">reference</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">dir</span><span class="p">:</span><span class="w"> </span><span class="l">reference           </span><span class="w"> </span><span class="c"># where to write the .qmd files</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">not_run_examples</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">   </span><span class="c"># whether to execute \dontrun{} examples</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">template</span><span class="p">:</span><span class="w"> </span><span class="l">inst/templates/_reference.qmd  </span><span class="w"> </span><span class="c"># custom page template</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">index</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">file</span><span class="p">:</span><span class="w"> </span><span class="l">index.qmd        </span><span class="w"> </span><span class="c"># name of the index file</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">template</span><span class="p">:</span><span class="w"> </span><span class="l">inst/templates/_index.qmd    </span><span class="w"> </span><span class="c"># custom index template</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">contents:               # optional</span><span class="p">:</span><span class="w"> </span><span class="l">custom function grouping</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">section</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;Write files&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">contents</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="l">write_reference.qmd</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="l">write_reference_index.qmd</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="l">write_reference_pages.qmd</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">section</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;Conversion&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">contents</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="l">rd_to_qmd.qmd</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="l">rd_to_list.qmd</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="l">index_to_qmd.qmd</span></span></span></code></pre></div></div>
<p>If you omit <code>contents</code>, <code>pkgsite</code> falls back to grouping by <code>roxygen2</code>
<code>@family</code> tags, then alphabetical order. You only need to re-run
<code>write_reference()</code> when you add, rename, or remove exported functions.</p>
<p>Use arguments for one-off adjustments; the <code>_quarto.yml</code> configuration is the
better choice when you want those settings to apply consistently every time
<code>write_reference()</code> is called.</p>
<p>This is what the rendered reference index page looks like on the <code>pkgsite</code>
website, using the grouping specified in the example YAML above:</p>
<p><div class="not-prose"><figure>
    <img class="h-auto max-w-full rounded-lg"
      src="https://opensource.posit.co/blog/2026-06-18_pkgsite-0-1-0/pkgsite-reference-index.png"
      alt="The pkgsite reference index page, with exported functions organized into named sections like &ldquo;Write files&rdquo; and &ldquo;Conversion&rdquo;."  title="Group functions into sections in the YAML file" 
      loading="lazy"
    ><figcaption class="text-sm text-center text-gray-500">Group functions into sections in the YAML file</figcaption>
  </figure></div>
</p>
<h2 id="customizing-the-page-layout">Customizing the page layout
</h2>
<p>The layout of every reference page and the index is driven by a Quarto template
file that uses four-curly-brace placeholders like <code>{{{{title.description}}}}</code>.
The prefix — <code>title.</code> or <code>notitle.</code> — controls whether the section heading is
included. The defaults work well for most packages, but if you want to
re-order sections, add a logo, link to source code, or adjust per-page
frontmatter, you can supply your own template. For example, a minimal template
that shows only the title, description, and examples looks like this:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="cl">---
</span></span><span class="line"><span class="cl">title: &#34;{{{{notitle.title}}}}&#34;
</span></span><span class="line"><span class="cl">---
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="gu">## {{{{notitle.title}}}}
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="gu">## Description
</span></span></span><span class="line"><span class="cl">{{{{title.description}}}}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="gu">## Examples
</span></span></span><span class="line"><span class="cl">{{{{title.examples}}}}</span></span></code></pre></div></div>
<p>The
<a href="https://edgararuiz.github.io/pkgsite/articles/customize.html" target="_blank" rel="noopener">Customize the pages</a>
article covers the full set of available placeholders.</p>
<p>The <code>mall</code> package is a good example of this. Its custom template makes two
additions to the default: it adjusts how <code>knitr</code> renders table column widths,
and it adds a link to the R source code of each function on GitHub:</p>
<p><div class="not-prose"><figure>
    <img class="h-auto max-w-full rounded-lg"
      src="https://opensource.posit.co/blog/2026-06-18_pkgsite-0-1-0/mall-custom-template.png"
      alt="A mall reference page built from a custom template, with a &ldquo;Source code&rdquo; link to the function&rsquo;s R source file on GitHub."  title="Add custom elements to reference pages with a template" 
      loading="lazy"
    ><figcaption class="text-sm text-center text-gray-500">Add custom elements to reference pages with a template</figcaption>
  </figure></div>
</p>
<h2 id="publishing-to-github-pages">Publishing to GitHub Pages
</h2>
<p>To publish your site on every push to <code>main</code>, you need a GitHub Actions
workflow that runs <code>quarto render</code> and deploys the output to the <code>gh-pages</code>
branch. The
<a href="https://edgararuiz.github.io/pkgsite/articles/github-actions.html" target="_blank" rel="noopener">GitHub Pages</a>
article on the <code>pkgsite</code> website walks through the full setup with a working
example.</p>
<h3 id="auto-linking-function-names">Auto-linking function names
</h3>
<p><code>pkgsite</code> can turn every function name in your prose into a link pointing to
its own reference page automatically. Mention <code>llm_sentiment()</code> anywhere in
your documentation and it becomes a clickable link to the <code>llm_sentiment</code>
reference page, no extra markup needed. The same
<a href="https://edgararuiz.github.io/pkgsite/articles/github-actions.html" target="_blank" rel="noopener">GitHub Pages</a>
article covers how to enable it.</p>
<h2 id="learn-more">Learn more
</h2>
<p>The full documentation lives at
<a href="https://edgararuiz.github.io/pkgsite/" target="_blank" rel="noopener">edgararuiz.github.io/pkgsite</a>, and the
source is on <a href="https://github.com/edgararuiz/pkgsite" target="_blank" rel="noopener">GitHub</a>. Issues and
feature requests go to the
<a href="https://github.com/edgararuiz/pkgsite/issues" target="_blank" rel="noopener">issue tracker</a>.</p>
]]></description>
      <enclosure url="https://opensource.posit.co/blog/2026-06-18_pkgsite-0-1-0/logo.png" length="1076855" type="image/png" />
    </item>
    <item>
      <title>webR 0.6.0</title>
      <link>https://opensource.posit.co/blog/2026-06-18_webr-0-6-0/</link>
      <pubDate>Thu, 18 Jun 2026 00:00:00 +0000</pubDate>
      <guid>https://opensource.posit.co/blog/2026-06-18_webr-0-6-0/</guid>
      <dc:creator>George Stagg</dc:creator><description><![CDATA[<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/codemirror.min.css">
<style>
  .webr-run-button {
    background-color: #F7F7F7;
    border: 1px solid #EEE;
    padding: 0.1em 0.5em;
    border-top-left-radius: 5px;
    border-top-right-radius: 5px;
    cursor: pointer;
    font-size: 0.9em;
  }
  .webr-run-button:hover {
    background-color: #EEE;
  }
  .webr-run-button:active {
    background-color: #DDD;
  }
  .webr-run-button:disabled {
    background-color: #DDD;
    cursor: not-allowed;
    transform: none;
  }
  .webr-code-output pre {
    margin-top: 0;
    border-top-left-radius: 0;
    border-top-right-radius: 0;
  }
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/codemirror.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/r/r.js"></script>
<script type="module">
  import { WebR } from 'https://webr.r-wasm.org/latest/webr.mjs';
  globalThis.webRIndex = 0;
  globalThis.webREditorCode = [];
  globalThis.webR = new WebR();
  globalThis.webRPlotWidth = 640;
  globalThis.webRPlotHeight = 400;
  await globalThis.webR.init();
  await webR.evalRVoid("webr::shim_install()");
  await webR.evalRVoid("webr::global_prompt_install()", { withHandlers: false });
  globalThis.webRCodeShelter = await new globalThis.webR.Shelter();
  document.querySelectorAll(".webr-run-button").forEach((btn) => {
    btn.innerText = 'Run code';
    btn.disabled = false;
  });
</script>
<p>We&rsquo;re delighted to announce the release of webR 0.6.0. WebR brings R to the web browser using WebAssembly, powering <a href="https://r-wasm.github.io/quarto-live/" target="_blank" rel="noopener">Quarto Live</a>, <a href="https://shinylive.io/r/examples/" target="_blank" rel="noopener">Shinylive</a>, and other interactive R experiences.</p>
<p>As a reminder of what webR can do, here is an interactive code editor running R in the browser. Try it out!</p>
<script type="module">
 globalThis.webREditorCode.push(`model <- lm(body_mass ~ bill_len, penguins)
summary(model)

boxplot(
  penguins$body_mass ~ penguins$species,
  xlab = 'Species',
  ylab = 'Body Mass (g)',
  col = c('#06a', '#f80', '#085')
)`)
</script>
<div><button class="webr-run-button" disabled type="button">Loading webR...</button></div>
<div class="webr-editor"></div>
<div class="webr-code-output"><pre style="visibility: hidden"></pre></div>
<script type="module">
  const runButton = document.getElementsByClassName('webr-run-button')[globalThis.webRIndex];
  const editorDiv = document.getElementsByClassName('webr-editor')[globalThis.webRIndex];
  const outputDiv = document.getElementsByClassName('webr-code-output')[globalThis.webRIndex];
  const codeValue = globalThis.webREditorCode[globalThis.webRIndex];
  globalThis.webRIndex++;
  const editor = CodeMirror((elt) => {
    elt.style.border = '1px solid #eee';
    elt.style.height = 'auto';
    editorDiv.append(elt);
  },{
    value: codeValue,
    lineNumbers: true,
    mode: 'r',
    theme: 'light default',
    viewportMargin: Infinity,
  });
  runButton.onclick = async () => {
    runButton.disabled = true;
    let canvas = undefined;
    await webR.init();
    await webR.evalRVoid(`webr::canvas(width = ${globalThis.webRPlotWidth}, height = ${globalThis.webRPlotHeight})`);
    const result = await webRCodeShelter.captureR(editor.getValue(), {
      withAutoprint: true,
      captureStreams: true,
      captureConditions: false,
      captureGraphics: false,
      env: {},
    });
    try {
      await webR.evalRVoid("dev.off()");
      const out = result.output.filter(
        evt => evt.type == 'stdout' || evt.type == 'stderr'
      ).map((evt) => evt.data).join('\n');
      outputDiv.innerHTML = '';
      const pre = document.createElement("pre");
      if (/\S/.test(out)) {
        const code = document.createElement("code");
        code.innerText = out;
        pre.appendChild(code);
      } else {
        pre.style.visibility = 'hidden';
      }
      outputDiv.appendChild(pre);
      const msgs = await webR.flush();
      msgs.forEach(msg => {
        if (msg.type === 'canvas'){
          if (msg.data.event === 'canvasImage') {
            canvas.getContext('2d').drawImage(msg.data.image, 0, 0);
          } else if (msg.data.event === 'canvasNewPage') {
            canvas = document.createElement('canvas');
            canvas.setAttribute('width', 2 * globalThis.webRPlotWidth);
            canvas.setAttribute('height', 2 * globalThis.webRPlotHeight);
            canvas.style.width="640px";
            canvas.style.display="block";
            canvas.style.margin="auto";
            const p = document.createElement("p");
            p.appendChild(canvas);
            outputDiv.appendChild(p);
          }
        }
      });
    } finally {
      webRCodeShelter.purge();
      runButton.disabled = false;
    }
  }
</script>
<p>It&rsquo;s been almost a year since I&rsquo;ve written a blog post about webR, and so this post highlights some key features and improvements over the last few releases. For a full list of changes, see the recent <a href="https://github.com/r-wasm/webr/releases" target="_blank" rel="noopener">release notes on GitHub</a>.</p>
<h2 id="updates-to-r-emscripten-and-llvm">Updates to R, Emscripten, and LLVM
</h2>
<p>We&rsquo;ve updated the version of R on which webR is based to version 4.6.0, ensuring we have all the latest improvements and bug fixes from the R core team. For example, the new <code>%notin%</code> operator can now be used:</p>
<script type="module">
 globalThis.webREditorCode.push("sessionInfo()[[1]]$version.string\n4 %notin% 1:10\n4 %notin% c(1, 2, 3)")
</script>
<div><button class="webr-run-button" disabled type="button">Loading webR...</button></div>
<div class="webr-editor"></div>
<div class="webr-code-output"><pre style="visibility: hidden"></pre></div>
<script type="module">
  const runButton = document.getElementsByClassName('webr-run-button')[globalThis.webRIndex];
  const editorDiv = document.getElementsByClassName('webr-editor')[globalThis.webRIndex];
  const outputDiv = document.getElementsByClassName('webr-code-output')[globalThis.webRIndex];
  const codeValue = globalThis.webREditorCode[globalThis.webRIndex];
  globalThis.webRIndex++;
  const editor = CodeMirror((elt) => {
    elt.style.border = '1px solid #eee';
    elt.style.height = 'auto';
    editorDiv.append(elt);
  },{
    value: codeValue,
    lineNumbers: true,
    mode: 'r',
    theme: 'light default',
    viewportMargin: Infinity,
  });
  runButton.onclick = async () => {
    runButton.disabled = true;
    let canvas = undefined;
    await webR.init();
    await webR.evalRVoid(`webr::canvas(width = ${globalThis.webRPlotWidth}, height = ${globalThis.webRPlotHeight})`);
    const result = await webRCodeShelter.captureR(editor.getValue(), {
      withAutoprint: true,
      captureStreams: true,
      captureConditions: false,
      captureGraphics: false,
      env: {},
    });
    try {
      await webR.evalRVoid("dev.off()");
      const out = result.output.filter(
        evt => evt.type == 'stdout' || evt.type == 'stderr'
      ).map((evt) => evt.data).join('\n');
      outputDiv.innerHTML = '';
      const pre = document.createElement("pre");
      if (/\S/.test(out)) {
        const code = document.createElement("code");
        code.innerText = out;
        pre.appendChild(code);
      } else {
        pre.style.visibility = 'hidden';
      }
      outputDiv.appendChild(pre);
      const msgs = await webR.flush();
      msgs.forEach(msg => {
        if (msg.type === 'canvas'){
          if (msg.data.event === 'canvasImage') {
            canvas.getContext('2d').drawImage(msg.data.image, 0, 0);
          } else if (msg.data.event === 'canvasNewPage') {
            canvas = document.createElement('canvas');
            canvas.setAttribute('width', 2 * globalThis.webRPlotWidth);
            canvas.setAttribute('height', 2 * globalThis.webRPlotHeight);
            canvas.style.width="640px";
            canvas.style.display="block";
            canvas.style.margin="auto";
            const p = document.createElement("p");
            p.appendChild(canvas);
            outputDiv.appendChild(p);
          }
        }
      });
    } finally {
      webRCodeShelter.purge();
      runButton.disabled = false;
    }
  }
</script>
<p>Under the hood, we&rsquo;ve also upgraded <a href="https://emscripten.org" target="_blank" rel="noopener">Emscripten</a> to version 5.0.7. Emscripten serves as the crucial layer between the web browser and R&rsquo;s source code, playing a role similar to an operating system. We&rsquo;ve also updated the version of LLVM we use to compile Fortran sources to 21.1.8.</p>
<h3 id="improvements-to-fortran-on-webassembly">Improvements to Fortran on WebAssembly
</h3>
<p>The R source code contains a surprising amount of Fortran code (the bulk of which written in F77-style), even today. For webR, we rely on a forked version of LLVM Flang with custom patches to add support for emitting WebAssembly. I won&rsquo;t go into the deep technical details here, but I have <a href="https://gws.phd/posts/fortran_wasm/" target="_blank" rel="noopener">written about it before</a> if you are interested.</p>
<p>For a long time, dynamic array operations introduced in more modern Fortran standards were not supported by our fork. This meant that some packages which use these Fortran features would not work in webR, crashing with a dense and largely unhelpful runtime error message:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fortran" data-lang="fortran"><span class="line"><span class="cl"><span class="k">program</span><span class="w"> </span><span class="n">p</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">integer</span><span class="w"> </span><span class="kd">::</span><span class="w"> </span><span class="n">a</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="mi">3</span><span class="p">,</span><span class="w"> </span><span class="mi">4</span><span class="p">,</span><span class="w"> </span><span class="mi">5</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">print</span><span class="w"> </span><span class="o">*</span><span class="p">,</span><span class="w"> </span><span class="nb">sum</span><span class="p">(</span><span class="n">a</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">end</span><span class="w"> </span><span class="k">program</span></span></span></code></pre></div></div>
<pre><code>$ ./previous/flang -c arr.f90 -o arr.o
$ emcc arr.o libFortranRuntime.a -o arr.js
$ node arr.js
  fatal Fortran runtime error(...reduction-templates.h(90)): Internal error:
  RUNTIME_CHECK(TypeCode(CAT, KIND) == x.type() || ...) failed
</code></pre>
<p>However, thanks to a <a href="https://github.com/r-wasm/flang-wasm/issues/9" target="_blank" rel="noopener">hint originating from a community member</a>, we&rsquo;ve now added a workaround for the underlying compiler problem, and R packages which rely on these Fortran features should now work in webR without crashing:</p>
<pre><code>$ ./latest/flang -c arr.f90 -o arr.o
$ emcc arr.o libFortranRuntime.a -o arr.js
$ node arr.js
  15
</code></pre>
<h2 id="additional-system-libraries">Additional system libraries
</h2>
<p>We&rsquo;ve also added or updated the following system libraries compiled to WebAssembly, which are now available for use by R packages in webR:</p>
<ul>
<li>cacert.pem 2026-05-14</li>
<li>fontconfig v2.15.0</li>
<li>libpoppler v24.12.0</li>
<li>libsodium v1.0.22</li>
<li>libtiff v4.7.1</li>
<li>libuv v1.44.2</li>
<li>openssl v3.5.1</li>
</ul>
<h2 id="asyncawait-in-webreval_js">Async/await in <code>webr::eval_js()</code>
</h2>
<p>One of the biggest issues when working with webR&rsquo;s JavaScript API is that we must block the worker thread running the WebAssembly R process. This is required to make sure we can wait and take input from the user, both at the top-level and in functions like <code>readline()</code> or <code>browser()</code>. Previously, this meant that only synchronous JavaScript code could be run from R via <code>webr::eval_js()</code>, and JavaScript features like <code>Promise</code>, <code>async</code> or <code>await</code> were not available.</p>
<p>By proxying the JavaScript code to the main thread, we&rsquo;ve added support for running asynchronous JavaScript code in <code>webr::eval_js()</code>. You can now use <code>await = TRUE</code> to wait for a <code>Promise</code> to resolve before returning a value to R:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="n">webr</span><span class="o">::</span><span class="nf">eval_js</span><span class="p">(</span><span class="s">&#34;new Promise((res) =&gt; res(1729))&#34;</span><span class="p">)</span></span></span></code></pre></div></div>
<pre><code>[1] 1729
</code></pre>
<p>Or equivalently, you can use <code>await</code> inside an <code>async</code> JavaScript function:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="n">webr</span><span class="o">::</span><span class="nf">eval_js</span><span class="p">(</span><span class="n">r</span><span class="s">&#34;{
</span></span></span><span class="line"><span class="cl"><span class="s">  (async () =&gt; {
</span></span></span><span class="line"><span class="cl"><span class="s">    await new Promise(r =&gt; setTimeout(r, 500));
</span></span></span><span class="line"><span class="cl"><span class="s">    return 87539319;
</span></span></span><span class="line"><span class="cl"><span class="s">  })();
</span></span></span><span class="line"><span class="cl"><span class="s">}&#34;</span><span class="p">,</span> <span class="n">await</span> <span class="o">=</span> <span class="kc">TRUE</span><span class="p">)</span></span></span></code></pre></div></div>
<pre><code>[1] 87539319
</code></pre>
<h2 id="using-curl-under-webassembly">Using curl under WebAssembly
</h2>
<p>Using similar techniques, we have also added support for proxying WebSocket traffic via the main thread. This, combined with some <a href="https://jeroen.github.io/notes/webassembly-curl/" target="_blank" rel="noopener">other work for libcurl in WebAssembly</a>, means we can now use the curl and httr2 R packages in webR.</p>
<script type="module">
 globalThis.webREditorCode.push(`# Load the R package
library(httr2)

# Create a request
req <- request("https://r-project.org")
req`)
</script>
<div><button class="webr-run-button" disabled type="button">Loading webR...</button></div>
<div class="webr-editor"></div>
<div class="webr-code-output"><pre style="visibility: hidden"></pre></div>
<script type="module">
  const runButton = document.getElementsByClassName('webr-run-button')[globalThis.webRIndex];
  const editorDiv = document.getElementsByClassName('webr-editor')[globalThis.webRIndex];
  const outputDiv = document.getElementsByClassName('webr-code-output')[globalThis.webRIndex];
  const codeValue = globalThis.webREditorCode[globalThis.webRIndex];
  globalThis.webRIndex++;
  const editor = CodeMirror((elt) => {
    elt.style.border = '1px solid #eee';
    elt.style.height = 'auto';
    editorDiv.append(elt);
  },{
    value: codeValue,
    lineNumbers: true,
    mode: 'r',
    theme: 'light default',
    viewportMargin: Infinity,
  });
  runButton.onclick = async () => {
    runButton.disabled = true;
    let canvas = undefined;
    await webR.init();
    await webR.evalRVoid(`webr::canvas(width = ${globalThis.webRPlotWidth}, height = ${globalThis.webRPlotHeight})`);
    const result = await webRCodeShelter.captureR(editor.getValue(), {
      withAutoprint: true,
      captureStreams: true,
      captureConditions: false,
      captureGraphics: false,
      env: {},
    });
    try {
      await webR.evalRVoid("dev.off()");
      const out = result.output.filter(
        evt => evt.type == 'stdout' || evt.type == 'stderr'
      ).map((evt) => evt.data).join('\n');
      outputDiv.innerHTML = '';
      const pre = document.createElement("pre");
      if (/\S/.test(out)) {
        const code = document.createElement("code");
        code.innerText = out;
        pre.appendChild(code);
      } else {
        pre.style.visibility = 'hidden';
      }
      outputDiv.appendChild(pre);
      const msgs = await webR.flush();
      msgs.forEach(msg => {
        if (msg.type === 'canvas'){
          if (msg.data.event === 'canvasImage') {
            canvas.getContext('2d').drawImage(msg.data.image, 0, 0);
          } else if (msg.data.event === 'canvasNewPage') {
            canvas = document.createElement('canvas');
            canvas.setAttribute('width', 2 * globalThis.webRPlotWidth);
            canvas.setAttribute('height', 2 * globalThis.webRPlotHeight);
            canvas.style.width="640px";
            canvas.style.display="block";
            canvas.style.margin="auto";
            const p = document.createElement("p");
            p.appendChild(canvas);
            outputDiv.appendChild(p);
          }
        }
      });
    } finally {
      webRCodeShelter.purge();
      runButton.disabled = false;
    }
  }
</script>
<p>Try out a <a href="https://webr.r-wasm.org/latest/#code=eJxdjzFuwkAQRfs9xQoaWwpeQYmiNGlTREipV4M9sU3s3c3MWAiJjhNwBzpukDr3IstipIhq%2Fteb%2FzVzPJwc9Hj%2BcNJKh9W8WJ0CSPNjGt%2Bj2eLaDoxk%2FvMKBH4vU%2F3modLSoF7pAOUX1KhaxwJdV4yes0kjQotJrrp2TUC7LPlcqal%2BJQRBDZrwe0AWFad%2Bnt1tigZeGkOzQH6DpRSe6lgVF675d6RPT326YMw8RSEDudbVqZeDd4zqKsZmG26pLOr8BvYvadOW3gk6sbILmD2w%2BJUMbCvkckR%2Fegdw2w%3D%3D" target="_blank" rel="noopener">full example R script using httr2 in the webR app</a>.</p>
<h2 id="dark-mode-for-the-webr-app">Dark mode for the webR app
</h2>
<p>Speaking of the webR app, if your system is set to dark mode the webR app will now automatically switch to a dark theme. This feature was contributed by an attendee of <a href="https://tidyverse.org/tags/tidyverse-dev-day/" target="_blank" rel="noopener">Tidyverse Developer Day 2025</a>. Thanks, Kyle!</p>
<figure>
<img src="https://opensource.posit.co/blog/2026-06-18_webr-0-6-0/dark.png" data-fig-alt="A screenshot of the webR app. On the left shows the app with light mode theme. On the right shows the app in dark mode theme." alt="The webR app in light and dark." />
<figcaption aria-hidden="true">The webR app in light and dark.</figcaption>
</figure>
<h2 id="acknowledgements">Acknowledgements
</h2>
<p>And, as always, a huge thank you to everyone else who has contributed to webR, either by submitting bug reports, contributing code, or just sharing feedback.</p>
<p><a href="https://github.com/AABoyles" target="_blank" rel="noopener">@AABoyles</a>, <a href="https://github.com/ai-petri" target="_blank" rel="noopener">@ai-petri</a>, <a href="https://github.com/brendanhcullen" target="_blank" rel="noopener">@brendanhcullen</a>, <a href="https://github.com/bryce-carson" target="_blank" rel="noopener">@bryce-carson</a>, <a href="https://github.com/chizapoth" target="_blank" rel="noopener">@chizapoth</a>, <a href="https://github.com/coatless" target="_blank" rel="noopener">@coatless</a>, <a href="https://github.com/daniel-woodie" target="_blank" rel="noopener">@daniel-woodie</a>, <a href="https://github.com/dipterix" target="_blank" rel="noopener">@dipterix</a>, <a href="https://github.com/dusadrian" target="_blank" rel="noopener">@dusadrian</a>, <a href="https://github.com/georgestagg" target="_blank" rel="noopener">@georgestagg</a>, <a href="https://github.com/goebbe" target="_blank" rel="noopener">@goebbe</a>, <a href="https://github.com/HenrikBengtsson" target="_blank" rel="noopener">@HenrikBengtsson</a>, <a href="https://github.com/jeroen" target="_blank" rel="noopener">@jeroen</a>, <a href="https://github.com/jgf5013" target="_blank" rel="noopener">@jgf5013</a>, <a href="https://github.com/jmbo1190" target="_blank" rel="noopener">@jmbo1190</a>, <a href="https://github.com/JosiahParry" target="_blank" rel="noopener">@JosiahParry</a>, <a href="https://github.com/khusmann" target="_blank" rel="noopener">@khusmann</a>, <a href="https://github.com/kyleGrealis" target="_blank" rel="noopener">@kyleGrealis</a>, <a href="https://github.com/laderast" target="_blank" rel="noopener">@laderast</a>, <a href="https://github.com/lpmi-13" target="_blank" rel="noopener">@lpmi-13</a>, <a href="https://github.com/pepijn-devries" target="_blank" rel="noopener">@pepijn-devries</a>, <a href="https://github.com/seanbirchall" target="_blank" rel="noopener">@seanbirchall</a>, <a href="https://github.com/szcf-weiya" target="_blank" rel="noopener">@szcf-weiya</a>, <a href="https://github.com/timelyportfolio" target="_blank" rel="noopener">@timelyportfolio</a>, <a href="https://github.com/tulaydixon" target="_blank" rel="noopener">@tulaydixon</a>, <a href="https://github.com/WardBrian" target="_blank" rel="noopener">@WardBrian</a>, and <a href="https://github.com/WillemSleegers" target="_blank" rel="noopener">@WillemSleegers</a>.</p>
]]></description>
      <enclosure url="https://opensource.posit.co/blog/2026-06-18_webr-0-6-0/bricks.jpg" length="612027" type="image/jpeg" />
    </item>
    <item>
      <title>Ask more of your dashboard with querychat and ggsql</title>
      <link>https://opensource.posit.co/blog/2026-06-17_querychat-ggsql/</link>
      <pubDate>Wed, 17 Jun 2026 00:00:00 +0000</pubDate>
      <guid>https://opensource.posit.co/blog/2026-06-17_querychat-ggsql/</guid>
      <dc:creator>Carson Sievert</dc:creator><description><![CDATA[<p>I&rsquo;m super excited to announce that <a href="https://posit-dev.github.io/querychat/" target="_blank" rel="noopener">querychat</a> now supports <a href="https://opensource.posit.co/blog/2026-04-20_ggsql_alpha_release">ggsql</a>. As a result, querychat users can now go beyond predetermined dashboard views and ask the LLM for bespoke visualizations on the fly. With this addition, querychat now comes with three pre-built tools that LLMs can use in response to user questions:</p>
<ol>
<li><strong>Visualize:</strong> execute <code>ggsql</code> queries, rendered as visualizations in the chat.</li>
<li><strong>Query:</strong> execute SQL queries, rendered as tables/text in the chat.</li>
<li><strong>Filter:</strong> execute filter SQL queries on predetermined views, allowing the LLM to reactively drill into the data in response to user questions.</li>
</ol>
<p>Equipped with these tools, your dashboard can offer a balance between &ldquo;curated insights&rdquo; and &ldquo;explorable data&rdquo; &mdash; surfacing interesting trends and summaries upfront, but also letting users ask follow-up questions that go beyond the predetermined views. The video below gives you a feel for this workflow in action<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. Note how the conversation starts off by filtering the predetermined views, but then progresses into more general exploration.</p>
<script src="https://fast.wistia.com/player.js" async></script>
<script src="https://fast.wistia.com/embed/at01coepkh.js" async type="module"></script>
<style>wistia-player[media-id='at01coepkh']:not(:defined) { background: center / contain no-repeat url('https://fast.wistia.com/embed/medias/at01coepkh/swatch'); display: block; filter: blur(5px); padding-top:58.76%; }</style>
<p><wistia-player media-id="at01coepkh" aspect="1.701851851851852"></wistia-player></p>
<p>Crucially, every one of <code>querychat</code>&rsquo;s tools works by executing SQL (or <code>ggsql</code>) &mdash; never arbitrary code. Every query is also fully visible and reproducible: users can inspect the code behind any response, and copy it to run independently. In the next version of <code>querychat</code>, you can also expect to see a rich export capability, allowing you to save the insights you care about in a format you care about (e.g. a Quarto dashboard, Jupyter notebook, or R Markdown file).</p>
<p>Restricting <code>querychat</code>&rsquo;s code execution capability to SQL (or <code>ggsql</code>) is an intentional design choice. We can guarantee that querychat does not run arbitrary R or python code, making it viable for production environments where security and control are required. As a result, <code>querychat</code> may not be as capable as a general coding assistant like <a href="https://posit-dev.github.io/assistant" target="_blank" rel="noopener">Posit Assistant</a>, but you generally don&rsquo;t want production apps that let users execute arbitrary code. By focusing on SQL execution, <code>querychat</code> can maintain a strong security posture while still enabling useful exploration of data via natural language.</p>
<h2 id="get-started">Get started
</h2>
<p>Getting started is as easy as pointing <code>QueryChat()</code> at your data source and getting set up with an LLM. Although we recommend frontier models for an ideal experience, open-weight models have recently become <a href="https://simonpcouch.com/blog/2026-04-16-local-agents-2/" target="_blank" rel="noopener">quite capable</a>, and can be a great way to get started without needing API access or incurring costs.</p>
<p>For this article, I&rsquo;m using <a href="https://lmstudio.ai/" target="_blank" rel="noopener">LMStudio</a> to run <code>google/gemma-4-26b-a4b</code> locally on my fairly standard MacBook Pro. If you prefer a different LLM or provider, refer to the <a href="https://posit-dev.github.io/querychat/py/models.html" target="_blank" rel="noopener">docs</a> (<a href="https://posit-dev.github.io/querychat/r/articles/models.html" target="_blank" rel="noopener">R</a>) to see how to configure.</p>
<p>Once you&rsquo;ve picked an LLM, next step is ensuring both <code>querychat</code> and <code>ggsql</code> are installed. Here we&rsquo;ll also use the <code>palmerpenguins</code> data set to keep examples lightweight and self-contained, but you can point <code>querychat</code> at anything from a data frame to a <a href="https://posit-dev.github.io/querychat/py/data-sources.html" target="_blank" rel="noopener">remote database connection</a> (<a href="https://posit-dev.github.io/querychat/r/articles/data-sources.html" target="_blank" rel="noopener">R</a>).</p>
<div class="panel-tabset" data-tabset-group="language">
<ul id="tabset-1" class="panel-tabset-tabby">
<li><a data-tabby-default href="#tabset-1-1">Python</a></li>
<li><a href="#tabset-1-2">R</a></li>
</ul>
<div id="tabset-1-1">
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">pip install <span class="s2">&#34;querychat[viz]&#34;</span> palmerpenguins</span></span></code></pre></div></div>
</div>
<div id="tabset-1-2">
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="nf">install.packages</span><span class="p">(</span><span class="nf">c</span><span class="p">(</span><span class="s">&#34;querychat&#34;</span><span class="p">,</span> <span class="s">&#34;ggsql&#34;</span><span class="p">,</span> <span class="s">&#34;palmerpenguins&#34;</span><span class="p">))</span></span></span></code></pre></div></div>
</div>
</div>
<h2 id="basic-usage">Basic usage
</h2>
<p>Once installed, you can get a basic chat UI using the code below. We&rsquo;ll include just the <code>query</code> and <code>visualize</code> tools here since the <code>filter</code> tool is designed to work in the context of a larger dashboard, which we&rsquo;ll cover in the next section.</p>
<div class="panel-tabset" data-tabset-group="language">
<ul id="tabset-2" class="panel-tabset-tabby">
<li><a data-tabby-default href="#tabset-2-1">Python</a></li>
<li><a href="#tabset-2-2">R</a></li>
</ul>
<div id="tabset-2-1">
<div class="code-block code-with-filename" role="group" aria-labelledby="code-filename-2">
  <div class="code-with-filename-label" id="code-filename-2"><span class="font-mono text-sm">app.py</span></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">shiny.express</span> <span class="kn">import</span> <span class="n">ui</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">querychat.express</span> <span class="kn">import</span> <span class="n">QueryChat</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">palmerpenguins</span> <span class="kn">import</span> <span class="n">load_penguins</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">qc</span> <span class="o">=</span> <span class="n">QueryChat</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">load_penguins</span><span class="p">(),</span> 
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;penguins&#34;</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">    <span class="n">client</span><span class="o">=</span><span class="s2">&#34;lmstudio/google/gemma-4-26b-a4b&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">tools</span><span class="o">=</span><span class="p">(</span><span class="s2">&#34;query&#34;</span><span class="p">,</span> <span class="s2">&#34;visualize&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">qc</span><span class="o">.</span><span class="n">ui</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">ui</span><span class="o">.</span><span class="n">page_opts</span><span class="p">(</span><span class="n">fillable</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span></span></span></code></pre></div></div>
<p>With <code>app.py</code> saved locally and your LLM running, start the app with:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">shiny run app.py</span></span></code></pre></div></div>
</div>
<div id="tabset-2-2">
<div class="code-block code-with-filename" role="group" aria-labelledby="code-filename-4">
  <div class="code-with-filename-label" id="code-filename-4"><span class="font-mono text-sm">app.R</span></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="nf">library</span><span class="p">(</span><span class="n">shiny</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nf">library</span><span class="p">(</span><span class="n">querychat</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nf">library</span><span class="p">(</span><span class="n">palmerpenguins</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">qc</span> <span class="o">&lt;-</span> <span class="n">QueryChat</span><span class="o">$</span><span class="nf">new</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">  <span class="n">penguins</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="n">client</span> <span class="o">=</span> <span class="s">&#34;lmstudio/google/gemma-4-26b-a4b&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="n">tools</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="s">&#34;query&#34;</span><span class="p">,</span> <span class="s">&#34;visualize&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">ui</span> <span class="o">&lt;-</span> <span class="n">bslib</span><span class="o">::</span><span class="nf">page_fillable</span><span class="p">(</span><span class="n">qc</span><span class="o">$</span><span class="nf">ui</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"><span class="n">server</span> <span class="o">&lt;-</span> <span class="kr">function</span><span class="p">(</span><span class="n">input</span><span class="p">,</span> <span class="n">output</span><span class="p">,</span> <span class="n">session</span><span class="p">)</span> <span class="p">{</span> <span class="n">qc</span><span class="o">$</span><span class="nf">server</span><span class="p">()</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nf">shinyApp</span><span class="p">(</span><span class="n">ui</span><span class="p">,</span> <span class="n">server</span><span class="p">)</span></span></span></code></pre></div></div>
<p>With <code>app.R</code> saved locally and your LLM running, start the app with:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="n">shiny</span><span class="o">::</span><span class="nf">runApp</span><span class="p">(</span><span class="s">&#34;app.R&#34;</span><span class="p">)</span></span></span></code></pre></div></div>
</div>
</div>
<p>Upon opening the app, the LLM greets us with a welcome message tailored to the data source we&rsquo;ve provided:</p>
<img src="https://opensource.posit.co/blog/2026-06-17_querychat-ggsql/hello-chat.png" alt="Basic QueryChat interface to the Palmer Penguins dataset" class="shadow rounded" />
<h3 id="query-tool">Query tool
</h3>
<p>For questions addressable via numerical summaries, the LLM requests the <code>query</code> tool with relevant SQL. The LLM may also choose to collapse details of the tool call, especially when it decides to weave the results within its actual response. That said, full details can always be inspected by clicking on the &ldquo;Query Data&rdquo; tool call display:</p>
<img src="https://opensource.posit.co/blog/2026-06-17_querychat-ggsql/hello-query.png" alt="Query tool in action, showing the SQL query and the resulting summary in the chat response" class="shadow rounded" />
<h3 id="visualize-tool">Visualize tool
</h3>
<p>For questions that are better suited to a visual response, the LLM requests the <code>visualize</code> tool with relevant ggsql. In addition to rendering the plot inline for the user to see, that same plot is also provided to the LLM so that it can interpret the result:</p>
<img src="https://opensource.posit.co/blog/2026-06-17_querychat-ggsql/hello-viz.png" alt="Visualize tool in action, showing the ggsql query and the resulting chart in the chat response" class="shadow rounded" />
<p>A couple noteworthy things about this experience:</p>
<ul>
<li>The footer includes options to view the underlying ggsql query, download the plot as an image, and expand the display to full screen.</li>
<li>Plots are always shown by default, but can be collapsed by clicking the header.</li>
</ul>
<p>This hopefully gives you a taste for how easy it is to get set up with a basic <code>querychat</code> UI and start asking questions of your data.
You may, however, be wondering how <code>querychat</code> is able to produce correct queries and how the code execution actually works. Let&rsquo;s dive into those details next.</p>
<h2 id="how-does-it-work">How does it work?
</h2>
<h3 id="execution">Execution
</h3>
<p>One important thing to understand about <code>querychat</code> is that the LLM itself is not handling the SQL execution &ndash; <code>querychat</code> is. Where that execution ultimately happens depends on what data source you&rsquo;re providing. If you&rsquo;re pointing <code>querychat</code> at an in-memory data frame, the execution happens in-process using DuckDB. If you&rsquo;re pointing it at a database connection, the execution happens against that database. In either case, the LLM is generating queries based on its understanding of the data schema and any additional context you&rsquo;ve provided, not the actual data. This separation is what allows <code>querychat</code> to maintain a strong security posture, scale to large data, and deliver a good user experience.</p>
<h3 id="schema-discovery">Schema discovery
</h3>
<p>To elaborate on what &ldquo;understanding of the data schema&rdquo; actually means: when you point <code>querychat</code> at a data source, it automatically extracts column names, types, numerical ranges, and categorical values. This information is included in the system prompt for the LLM, so it has a clear picture of what the data looks like and can generate accurate queries without needing to see the actual data. For many datasets, this is enough to get good results out of the box.</p>
<h3 id="additional-context">Additional context
</h3>
<p>For more complex datasets or domain-specific questions, you can also provide additional context through a plain-text data description. <code>querychat</code> now also automatically picks up on <a href="https://www.snowflake.com/en/developers/guides/snowflake-semantic-view/" target="_blank" rel="noopener">Snowflake Semantic Models</a> when connected to a Snowflake database, giving the LLM access to authoritative definitions of business logic and metrics without any manual configuration. We hope to add more integrations to other semantic layer formats in the future.</p>
<p>To learn more, <code>querychat</code>&rsquo;s website has more details on <a href="https://posit-dev.github.io/querychat/py/data-sources.html" target="_blank" rel="noopener">data sources</a> (<a href="https://posit-dev.github.io/querychat/r/articles/data-sources.html" target="_blank" rel="noopener">R</a>), <a href="https://posit-dev.github.io/querychat/py/context.html" target="_blank" rel="noopener">providing context</a> (<a href="https://posit-dev.github.io/querychat/r/articles/context.html" target="_blank" rel="noopener">R</a>), and <a href="https://posit-dev.github.io/querychat/py/tools.html" target="_blank" rel="noopener">tool execution</a> (<a href="https://posit-dev.github.io/querychat/r/articles/tools.html" target="_blank" rel="noopener">R</a>).</p>
<h2 id="chat-driven-dashboards">Chat-driven dashboards
</h2>
<p>A chat interface alone likely isn&rsquo;t the experience you want to ship to end users &mdash; the better pattern is to surface interesting findings first, then let users explore beyond them. This is where <code>querychat</code> begins to really shine: the chat interface we already covered can easily be embedded inside of a larger app that includes other outputs &ndash; plots, tables, value boxes, etc. <code>querychat</code> comes with another tool designed for this use case &ndash; the filter tool &ndash; allowing the LLM to effectively drill down into relevant sections of the data in the dashboard in response to user questions.</p>
<p>The key integration point is <code>df()</code>, a reactive value that reflects the current state of the data after any filtering applied by the LLM. Use <code>df()</code> as the data source for your plots, tables, and value boxes, and they&rsquo;ll automatically update in response to user questions in the chat.</p>
<style>
style + .panel-tabset .highlight .chroma {
  max-height: 500px;
  overflow-y: auto !important;
}
</style>
<div class="panel-tabset" data-tabset-group="language">
<ul id="tabset-3" class="panel-tabset-tabby">
<li><a data-tabby-default href="#tabset-3-1">Python</a></li>
<li><a href="#tabset-3-2">R</a></li>
</ul>
<div id="tabset-3-1">
<div class="code-block code-with-filename" role="group" aria-labelledby="code-filename-6">
  <div class="code-with-filename-label" id="code-filename-6"><span class="font-mono text-sm">app.py</span></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">shiny.express</span> <span class="kn">import</span> <span class="n">render</span><span class="p">,</span> <span class="n">ui</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">querychat.express</span> <span class="kn">import</span> <span class="n">QueryChat</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">palmerpenguins</span> <span class="kn">import</span> <span class="n">load_penguins</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">faicons</span> <span class="kn">import</span> <span class="n">icon_svg</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">qc</span> <span class="o">=</span> <span class="n">QueryChat</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">load_penguins</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;penguins&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">client</span><span class="o">=</span><span class="s2">&#34;lmstudio/google/gemma-4-26b-a4b&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">tools</span><span class="o">=</span><span class="p">(</span><span class="s2">&#34;filter&#34;</span><span class="p">,</span> <span class="s2">&#34;query&#34;</span><span class="p">,</span> <span class="s2">&#34;visualize&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">qc</span><span class="o">.</span><span class="n">sidebar</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">with</span> <span class="n">ui</span><span class="o">.</span><span class="n">layout_columns</span><span class="p">(</span><span class="n">fill</span><span class="o">=</span><span class="kc">False</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">with</span> <span class="n">ui</span><span class="o">.</span><span class="n">value_box</span><span class="p">(</span><span class="n">showcase</span><span class="o">=</span><span class="n">icon_svg</span><span class="p">(</span><span class="s2">&#34;binoculars&#34;</span><span class="p">)):</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;Penguins&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="nd">@render.text</span>
</span></span><span class="line"><span class="cl">        <span class="k">def</span> <span class="nf">count</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="nb">len</span><span class="p">(</span><span class="n">qc</span><span class="o">.</span><span class="n">df</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">with</span> <span class="n">ui</span><span class="o">.</span><span class="n">value_box</span><span class="p">(</span><span class="n">showcase</span><span class="o">=</span><span class="n">icon_svg</span><span class="p">(</span><span class="s2">&#34;fingerprint&#34;</span><span class="p">)):</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;Species&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="nd">@render.text</span>
</span></span><span class="line"><span class="cl">        <span class="k">def</span> <span class="nf">species</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">qc</span><span class="o">.</span><span class="n">df</span><span class="p">()[</span><span class="s2">&#34;species&#34;</span><span class="p">]</span><span class="o">.</span><span class="n">nunique</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">with</span> <span class="n">ui</span><span class="o">.</span><span class="n">value_box</span><span class="p">(</span><span class="n">showcase</span><span class="o">=</span><span class="n">icon_svg</span><span class="p">(</span><span class="s2">&#34;gauge-high&#34;</span><span class="p">)):</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;Avg Body Mass&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="nd">@render.text</span>
</span></span><span class="line"><span class="cl">        <span class="k">def</span> <span class="nf">avg_mass</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="sa">f</span><span class="s2">&#34;</span><span class="si">{</span><span class="n">qc</span><span class="o">.</span><span class="n">df</span><span class="p">()[</span><span class="s1">&#39;body_mass_g&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">mean</span><span class="p">()</span><span class="si">:</span><span class="s2">.0f</span><span class="si">}</span><span class="s2">g&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">with</span> <span class="n">ui</span><span class="o">.</span><span class="n">layout_columns</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">with</span> <span class="n">ui</span><span class="o">.</span><span class="n">card</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">card_header</span><span class="p">(</span><span class="s2">&#34;Bill Dimensions&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="nd">@render.plot</span>
</span></span><span class="line"><span class="cl">        <span class="k">def</span> <span class="nf">scatter</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">            <span class="kn">import</span> <span class="nn">plotnine</span> <span class="k">as</span> <span class="nn">p9</span>
</span></span><span class="line"><span class="cl">            
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="n">p9</span><span class="o">.</span><span class="n">ggplot</span><span class="p">(</span><span class="n">qc</span><span class="o">.</span><span class="n">df</span><span class="p">(),</span> <span class="n">p9</span><span class="o">.</span><span class="n">aes</span><span class="p">(</span><span class="s2">&#34;bill_length_mm&#34;</span><span class="p">,</span> <span class="s2">&#34;bill_depth_mm&#34;</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s2">&#34;species&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">                <span class="o">+</span> <span class="n">p9</span><span class="o">.</span><span class="n">geom_point</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">            <span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">with</span> <span class="n">ui</span><span class="o">.</span><span class="n">card</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">card_header</span><span class="p">(</span><span class="s2">&#34;Measurements&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="nd">@render.data_frame</span>
</span></span><span class="line"><span class="cl">        <span class="k">def</span> <span class="nf">table</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">qc</span><span class="o">.</span><span class="n">df</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">ui</span><span class="o">.</span><span class="n">page_opts</span><span class="p">(</span><span class="n">fillable</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span></span></span></code></pre></div></div>
</div>
<div id="tabset-3-2">
<div class="code-block code-with-filename" role="group" aria-labelledby="code-filename-7">
  <div class="code-with-filename-label" id="code-filename-7"><span class="font-mono text-sm">app.R</span></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="nf">library</span><span class="p">(</span><span class="n">shiny</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nf">library</span><span class="p">(</span><span class="n">bslib</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nf">library</span><span class="p">(</span><span class="n">querychat</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nf">library</span><span class="p">(</span><span class="n">DT</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nf">library</span><span class="p">(</span><span class="n">ggplot2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nf">library</span><span class="p">(</span><span class="n">palmerpenguins</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">qc</span> <span class="o">&lt;-</span> <span class="n">QueryChat</span><span class="o">$</span><span class="nf">new</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">  <span class="n">penguins</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="n">client</span> <span class="o">=</span> <span class="s">&#34;lmstudio/google/gemma-4-26b-a4b&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="n">tools</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="s">&#34;filter&#34;</span><span class="p">,</span> <span class="s">&#34;query&#34;</span><span class="p">,</span> <span class="s">&#34;visualize&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">ui</span> <span class="o">&lt;-</span> <span class="nf">page_sidebar</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">  <span class="n">sidebar</span> <span class="o">=</span> <span class="n">qc</span><span class="o">$</span><span class="nf">sidebar</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">  <span class="nf">layout_columns</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">fill</span> <span class="o">=</span> <span class="kc">FALSE</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nf">value_box</span><span class="p">(</span><span class="s">&#34;Penguins&#34;</span><span class="p">,</span> <span class="nf">textOutput</span><span class="p">(</span><span class="s">&#34;count&#34;</span><span class="p">),</span> <span class="n">showcase</span> <span class="o">=</span> <span class="n">bsicons</span><span class="o">::</span><span class="nf">bs_icon</span><span class="p">(</span><span class="s">&#34;binoculars&#34;</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">    <span class="nf">value_box</span><span class="p">(</span><span class="s">&#34;Species&#34;</span><span class="p">,</span> <span class="nf">textOutput</span><span class="p">(</span><span class="s">&#34;species&#34;</span><span class="p">),</span> <span class="n">showcase</span> <span class="o">=</span> <span class="n">bsicons</span><span class="o">::</span><span class="nf">bs_icon</span><span class="p">(</span><span class="s">&#34;fingerprint&#34;</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">    <span class="nf">value_box</span><span class="p">(</span><span class="s">&#34;Avg Body Mass&#34;</span><span class="p">,</span> <span class="nf">textOutput</span><span class="p">(</span><span class="s">&#34;avg_mass&#34;</span><span class="p">),</span> <span class="n">showcase</span> <span class="o">=</span> <span class="n">bsicons</span><span class="o">::</span><span class="nf">bs_icon</span><span class="p">(</span><span class="s">&#34;speedometer&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">  <span class="p">),</span>
</span></span><span class="line"><span class="cl">  <span class="nf">layout_columns</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="nf">card</span><span class="p">(</span><span class="nf">card_header</span><span class="p">(</span><span class="s">&#34;Bill Dimensions&#34;</span><span class="p">),</span> <span class="nf">plotOutput</span><span class="p">(</span><span class="s">&#34;scatter&#34;</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">    <span class="nf">card</span><span class="p">(</span><span class="nf">card_header</span><span class="p">(</span><span class="s">&#34;Measurements&#34;</span><span class="p">),</span> <span class="nf">DTOutput</span><span class="p">(</span><span class="s">&#34;table&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">  <span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">server</span> <span class="o">&lt;-</span> <span class="kr">function</span><span class="p">(</span><span class="n">input</span><span class="p">,</span> <span class="n">output</span><span class="p">,</span> <span class="n">session</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">qc_vals</span> <span class="o">&lt;-</span> <span class="n">qc</span><span class="o">$</span><span class="nf">server</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">  <span class="n">df</span> <span class="o">&lt;-</span> <span class="n">qc_vals</span><span class="o">$</span><span class="n">df</span>
</span></span><span class="line"><span class="cl">  <span class="n">output</span><span class="o">$</span><span class="n">count</span> <span class="o">&lt;-</span> <span class="nf">renderText</span><span class="p">(</span><span class="nf">nrow</span><span class="p">(</span><span class="nf">df</span><span class="p">()))</span>
</span></span><span class="line"><span class="cl">  <span class="n">output</span><span class="o">$</span><span class="n">species</span> <span class="o">&lt;-</span> <span class="nf">renderText</span><span class="p">(</span><span class="nf">length</span><span class="p">(</span><span class="nf">unique</span><span class="p">(</span><span class="nf">df</span><span class="p">()</span><span class="o">$</span><span class="n">species</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">  <span class="n">output</span><span class="o">$</span><span class="n">avg_mass</span> <span class="o">&lt;-</span> <span class="nf">renderText</span><span class="p">(</span><span class="nf">paste0</span><span class="p">(</span><span class="nf">round</span><span class="p">(</span><span class="nf">mean</span><span class="p">(</span><span class="nf">df</span><span class="p">()</span><span class="o">$</span><span class="n">body_mass_g</span><span class="p">,</span> <span class="n">na.rm</span> <span class="o">=</span> <span class="kc">TRUE</span><span class="p">)),</span> <span class="s">&#34;g&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">  <span class="n">output</span><span class="o">$</span><span class="n">scatter</span> <span class="o">&lt;-</span> <span class="nf">renderPlot</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="nf">ggplot</span><span class="p">(</span><span class="nf">df</span><span class="p">(),</span> <span class="nf">aes</span><span class="p">(</span><span class="n">bill_length_mm</span><span class="p">,</span> <span class="n">bill_depth_mm</span><span class="p">,</span> <span class="n">color</span> <span class="o">=</span> <span class="n">species</span><span class="p">))</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">      <span class="nf">geom_point</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl">  <span class="n">output</span><span class="o">$</span><span class="n">table</span> <span class="o">&lt;-</span> <span class="nf">renderDT</span><span class="p">(</span><span class="nf">df</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">shinyApp</span><span class="p">(</span><span class="n">ui</span><span class="p">,</span> <span class="n">server</span><span class="p">)</span></span></span></code></pre></div></div>
</div>
</div>
<img src="https://opensource.posit.co/blog/2026-06-17_querychat-ggsql/dashboard.png" alt="QueryChat embedded in a dashboard with value boxes, a scatter plot, and a data table — all reactively driven by the chat's filter state" class="shadow rounded" />
<div class="callout callout-note" role="note" aria-label="Note">
<div class="callout-header">
<span class="callout-title">Python users: Shiny v Streamlit/Dash/Gradio</span>
</div>
<div class="callout-body">
<p><code>querychat</code> also supports <code>streamlit</code>, <code>dash</code>, and <code>gradio</code>. That said, the reactive <code>df()</code> pattern shown above &mdash; where the LLM&rsquo;s filter state automatically drives every view in the app &mdash; is what Shiny&rsquo;s reactive programming model was designed for. In other frameworks, keeping the chat&rsquo;s state in sync with multiple outputs typically requires more manual wiring.</p>
</div>
</div>
<h2 id="custom-tools">Custom tools
</h2>
<p><code>querychat</code> comes with three built-in tools, but you can also easily add your own custom tools. This is possible thanks to the extensible foundation provided by <a href="https://posit-dev.github.io/chatlas/" target="_blank" rel="noopener">chatlas</a> (<a href="https://ellmer.tidyverse.org" target="_blank" rel="noopener">ellmer</a>). These packages also make it quite easy to implement tools &ndash; all you really need is a Python / R function that performs some operation. You can also fully customize the display shown, thanks to their rich support for <a href="https://posit-dev.github.io/chatlas/tool-calling/displays.html" target="_blank" rel="noopener">tool call displays</a> (<a href="https://posit-dev.github.io/shinychat/r/articles/tool-ui.html" target="_blank" rel="noopener">R</a>).</p>
<p>To give you a sense of what other capabilities are possible, you could start out as simple as querying <a href="https://posit-dev.github.io/chatlas/get-started/tools.html" target="_blank" rel="noopener">real-time weather information</a>, but also get as sophisticated as a <a href="https://posit-dev.github.io/chatlas/misc/RAG.html#dynamic-retrieval" target="_blank" rel="noopener">RAG-like knowledge retrieval agent</a>. For example, here&rsquo;s how you&rsquo;d let users ask whether weather conditions might relate to trends in your data:</p>
<div class="panel-tabset" data-tabset-group="language">
<ul id="tabset-4" class="panel-tabset-tabby">
<li><a data-tabby-default href="#tabset-4-1">Python</a></li>
<li><a href="#tabset-4-2">R</a></li>
</ul>
<div id="tabset-4-1">
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">requests</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">querychat.express</span> <span class="kn">import</span> <span class="n">QueryChat</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">chatlas</span> <span class="kn">import</span> <span class="n">ChatLMStudio</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">palmerpenguins</span> <span class="kn">import</span> <span class="n">load_penguins</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">chat_client</span> <span class="o">=</span> <span class="n">ChatLMStudio</span><span class="p">(</span><span class="n">model</span><span class="o">=</span><span class="s2">&#34;lmstudio/google/gemma-4-26b-a4b&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">get_current_weather</span><span class="p">(</span><span class="n">lat</span><span class="p">:</span> <span class="nb">float</span><span class="p">,</span> <span class="n">lng</span><span class="p">:</span> <span class="nb">float</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Get the current weather for a location.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">lat_lng</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&#34;latitude=</span><span class="si">{</span><span class="n">lat</span><span class="si">}</span><span class="s2">&amp;longitude=</span><span class="si">{</span><span class="n">lng</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">url</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&#34;https://api.open-meteo.com/v1/forecast?</span><span class="si">{</span><span class="n">lat_lng</span><span class="si">}</span><span class="s2">&amp;current=temperature_2m,wind_speed_10m&amp;hourly=temperature_2m,relative_humidity_2m,wind_speed_10m&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">response</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">response</span><span class="o">.</span><span class="n">json</span><span class="p">()[</span><span class="s2">&#34;current&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">chat_client</span><span class="o">.</span><span class="n">register_tool</span><span class="p">(</span><span class="n">get_current_weather</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">qc</span> <span class="o">=</span> <span class="n">QueryChat</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">load_penguins</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;penguins&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">client</span><span class="o">=</span><span class="n">chat_client</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span></span></span></code></pre></div></div>
</div>
<div id="tabset-4-2">
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="nf">library</span><span class="p">(</span><span class="n">querychat</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nf">library</span><span class="p">(</span><span class="n">ellmer</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">chat_client</span> <span class="o">&lt;-</span> <span class="nf">chat_lmstudio</span><span class="p">(</span><span class="n">model</span> <span class="o">=</span> <span class="s">&#34;lmstudio/google/gemma-4-26b-a4b&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">get_current_weather</span> <span class="o">&lt;-</span> <span class="kr">function</span><span class="p">(</span><span class="n">lat</span><span class="p">,</span> <span class="n">lng</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">lat_lng</span> <span class="o">&lt;-</span> <span class="nf">paste0</span><span class="p">(</span><span class="s">&#34;latitude=&#34;</span><span class="p">,</span> <span class="n">lat</span><span class="p">,</span> <span class="s">&#34;&amp;longitude=&#34;</span><span class="p">,</span> <span class="n">lng</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">url</span> <span class="o">&lt;-</span> <span class="nf">paste0</span><span class="p">(</span><span class="s">&#34;https://api.open-meteo.com/v1/forecast?&#34;</span><span class="p">,</span> <span class="n">lat_lng</span><span class="p">,</span> <span class="s">&#34;&amp;current=temperature_2m,wind_speed_10m&amp;hourly=temperature_2m,relative_humidity_2m,wind_speed_10m&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">response</span> <span class="o">&lt;-</span> <span class="n">httr</span><span class="o">::</span><span class="nf">GET</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">httr</span><span class="o">::</span><span class="nf">content</span><span class="p">(</span><span class="n">response</span><span class="p">)</span><span class="o">$</span><span class="n">current</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">chat_client</span><span class="o">$</span><span class="nf">register_tool</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">  <span class="nf">tool</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">get_current_weather</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#34;Get the current weather for a location&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">lat</span> <span class="o">=</span> <span class="nf">type_number</span><span class="p">(</span><span class="s">&#34;Latitude&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="n">lng</span> <span class="o">=</span> <span class="nf">type_number</span><span class="p">(</span><span class="s">&#34;Longitude&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">qc</span> <span class="o">&lt;-</span> <span class="n">QueryChat</span><span class="o">$</span><span class="nf">new</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">  <span class="n">palmerpenguins</span><span class="o">::</span><span class="n">penguins</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="n">client</span> <span class="o">=</span> <span class="n">chat_client</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span></span></span></code></pre></div></div>
</div>
</div>
<p>Underneath the hood, <code>querychat</code>&rsquo;s built-in tools use this same foundation. The &ldquo;magic&rdquo; of these tools really breaks down to three key insights:</p>
<ol>
<li>LLMs are very good at translating natural language into code.</li>
<li>Tool call arguments can be executable code (like SQL or ggsql) rather than just simple values.</li>
<li>Tool calls can <a href="https://shiny.posit.co/py/docs/genai-tools.html#managing-state" target="_blank" rel="noopener">manage reactive state in a Shiny app</a> &mdash; for <code>querychat</code>, that state is the current filter query, but the same pattern could control input controls, tab selection, or other UI state.</li>
</ol>
<h2 id="whats-next">What&rsquo;s next?
</h2>
<p>This release is just the beginning of our vision for (responsibly) exploring data via natural language. We&rsquo;re excited to see what you build with it, and we&rsquo;re already thinking about the next set of features to add. Some things on our near-term roadmap:</p>
<ul>
<li>Multiple tables.</li>
<li>Reproducible takeaway artifacts, like <a href="https://quarto.org/docs/dashboards/" target="_blank" rel="noopener">Quarto dashboards</a></li>
<li>Support for more semantic layer solutions beyond Snowflake, so more users can give the LLM authoritative definitions of their business logic.</li>
</ul>
<h2 id="learn-more">Learn more
</h2>
<ul>
<li><a href="https://posit-dev.github.io/querychat/py/" target="_blank" rel="noopener">querychat documentation</a> (<a href="https://posit-dev.github.io/querychat/r/" target="_blank" rel="noopener">R</a>) &mdash; full guides on data sources, context, tools, and deployment</li>
<li><a href="https://ggsql.org" target="_blank" rel="noopener">ggsql</a> &mdash; the grammar of graphics for SQL that powers querychat&rsquo;s visualizations</li>
<li><a href="https://posit-dev.github.io/chatlas/" target="_blank" rel="noopener">chatlas</a> (<a href="https://ellmer.tidyverse.org" target="_blank" rel="noopener">ellmer</a>) &mdash; the underlying LLM tool-calling libraries, useful for building custom tools</li>
<li><a href="https://posit-dev.github.io/shinychat/py/" target="_blank" rel="noopener">shinychat</a> (<a href="https://posit-dev.github.io/shinychat/r/" target="_blank" rel="noopener">R</a>) &mdash; the chat UI component that querychat builds on</li>
<li><a href="https://shiny.posit.co/py/" target="_blank" rel="noopener">Shiny</a> (<a href="https://shiny.posit.co/r/" target="_blank" rel="noopener">R</a>) &mdash; the web framework powering querychat apps</li>
<li><a href="https://github.com/posit-dev/querychat" target="_blank" rel="noopener">Source on GitHub</a> &mdash; issues, discussions, and contributions welcome</li>
</ul>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://gist.github.com/cpsievert/ca0d7e4637fdf994671c9d9fc90cd89f" target="_blank" rel="noopener">Source code</a> is available for reference, though you&rsquo;ll need your own Snowflake account and credentials to run it.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></description>
      <enclosure url="https://opensource.posit.co/blog/2026-06-17_querychat-ggsql/featured.png" length="454060" type="image/png" />
    </item>
    <item>
      <title>dbplyr 2.6.0</title>
      <link>https://opensource.posit.co/blog/2026-06-17_dbplyr-2-6-0/</link>
      <pubDate>Wed, 17 Jun 2026 00:00:00 +0000</pubDate>
      <guid>https://opensource.posit.co/blog/2026-06-17_dbplyr-2-6-0/</guid>
      <dc:creator>Hadley Wickham</dc:creator><description><![CDATA[<p>I&rsquo;m pleased to announce that <a href="https://dbplyr.tidyverse.org" target="_blank" rel="noopener">dbplyr 2.6.0</a> is now on CRAN.
dbplyr is the database backend for <a href="https://dplyr.tidyverse.org" target="_blank" rel="noopener">dplyr</a>: it automatically translates the dplyr code you&rsquo;d write for a data frame into SQL so you can run the same pipeline against tables in a remote database.
You can install it with:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="nf">install.packages</span><span class="p">(</span><span class="s">&#34;dbplyr&#34;</span><span class="p">)</span></span></span></code></pre></div></div>
<p>This is another release that has benefited from Claude Code&rsquo;s help, allowing me to rip through a large number of smaller issues (~50!) where the correct solution is easily verified.
And those time savings allowed me to tackle some more challenging and more impactful features, such as new ADBC and JDBC backends and a new <code>sql_dialect()</code> generic that finally separates <em>how you connect to a database</em> from <em>which SQL dialect to use</em>.
The release also includes new translations, a couple of useful helpers, and a substantial cleanup of long-standing deprecations.
This post covers the highlights for everyday users in the first half, then dives into the changes that matter for backend developers at the end.
Read the full list of changes in the <a href="https://github.com/tidyverse/dbplyr/releases/tag/v2.6.0" target="_blank" rel="noopener">release notes</a>.</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="nf">library</span><span class="p">(</span><span class="n">dbplyr</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nf">library</span><span class="p">(</span><span class="n">dplyr</span><span class="p">,</span> <span class="n">warn.conflicts</span> <span class="o">=</span> <span class="kc">FALSE</span><span class="p">)</span></span></span></code></pre></div></div>
<h2 id="lifecycle-changes">Lifecycle changes
</h2>
<p>A handful of things you may have been using have been deprecated or removed.
The biggest ones to know about:</p>
<ul>
<li>
<p><strong><code>do()</code> is deprecated.</strong> It was the only piece of dplyr 0.x syntax that dbplyr still supported, and it never fit cleanly into SQL.
If you have code that uses it, the simplest path is to <code>collect()</code> first and then use your favourite tidyverse tools on the resulting data frame.</p>
</li>
<li>
<p><strong><code>str_like(ignore_case = TRUE)</code> is deprecated</strong> in favour of the new <code>str_ilike()</code> (thanks to <a href="https://github.com/edward-burn" target="_blank" rel="noopener">@edward-burn</a>).
This brings the dbplyr translation in line with the recent updates to <a href="https://stringr.tidyverse.org" target="_blank" rel="noopener">stringr</a>.</p>
</li>
<li>
<p><strong>The <code>cte</code> argument</strong> of <code>collect()</code>, <code>compute()</code>, <code>show_query()</code>, <code>remote_query()</code>, and <code>db_sql_render()</code> is deprecated.
Pass <code>sql_options = sql_options(cte = TRUE)</code> instead.
This consolidates a growing pile of one-off flags into a single options object that we can extend without tweaking every render function.</p>
</li>
</ul>
<p>A few deprecations that have been warning for multiple years are now defunct: passing <code>...</code> to <code>across()</code>/<code>if_all()</code>/<code>if_any()</code>, using <code>by = character()</code> to perform a cross join (use <code>cross_join()</code> instead), and calling <code>compute(temporary = FALSE)</code> without a <code>name</code>.
A few defunct functions/arguments have been removed entirely, including <code>group_by(add = )</code>, <code>partial_eval(var)</code>, and <code>src_sql()</code>.</p>
<p>If you&rsquo;re a backend developer, there&rsquo;s a longer list of changes that affect you &mdash; see the <a href="#notes-for-backend-developers">Notes for backend developers</a> at the end of this post.</p>
<h2 id="new-backends-adbc-and-jdbc">New backends: ADBC and JDBC
</h2>
<p>dbplyr already worked with ODBC (thanks to {<a href="https://odbc.r-dbi.org" target="_blank" rel="noopener">odbc</a>}).
With 2.6.0, you can now connect through two more transport layers:</p>
<ul>
<li>
<p><strong><a href="https://arrow.apache.org/adbc/" target="_blank" rel="noopener">ADBC</a></strong> (Arrow Database Connectivity) is a relatively new project from the Apache Arrow community.
It moves data between R and the database as Arrow buffers, which avoids the per-row serialization overhead of older protocols and can be much (much!) faster.
dbplyr supports ADBC connections through {<a href="https://github.com/r-dbi/adbi" target="_blank" rel="noopener">adbi</a>}.</p>
</li>
<li>
<p><strong><a href="https://en.wikipedia.org/wiki/Java_Database_Connectivity" target="_blank" rel="noopener">JDBC</a></strong> (Java Database Connectivity) is the standard database protocol on the JVM, with high-quality drivers for nearly every database under the sun.
dbplyr supports JDBC connections through {<a href="https://github.com/s-u/RJDBC" target="_blank" rel="noopener">RJDBC</a>}.</p>
</li>
</ul>
<p>The reason these new backends slot in so cleanly is a new generic, <code>sql_dialect()</code>.
Until now, dbplyr picked SQL translations based on the connection class itself, which meant every new transport had to either masquerade as an existing connection or carry its own copy of every translation.
(Or as with {odbc}, hack the database name into the object class.)
That&rsquo;s why ADBC and JDBC support was hard to add in the past.</p>
<p><code>sql_dialect()</code> separates the two concerns: a connection&rsquo;s job is to know how to talk to the server and fetch data, and the dialect&rsquo;s job is to know how to generate the right SQL.
For example, when you connect over JDBC or ADBC to Postgres, dbplyr now uses the same translations it would use over ODBC or via <a href="https://rpostgres.r-dbi.org" target="_blank" rel="noopener">RPostgres</a>.
This simplified a bunch of dbplyr code and made it very easy to add the new ADBC and JDBC backends.
Finally, if the default dialect isn&rsquo;t quite right, you can override it with <code>with_dialect()</code>.</p>
<h2 id="new-translations">New translations
</h2>
<p>A few translations are now available.</p>
<p><code>bind_queries()</code> is the dbplyr equivalent of <code>dplyr::bind_rows()</code>: it combines several lazy queries into one using <code>UNION ALL</code>, aligning columns as needed.</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="n">con</span> <span class="o">&lt;-</span> <span class="n">DBI</span><span class="o">::</span><span class="nf">dbConnect</span><span class="p">(</span><span class="n">RSQLite</span><span class="o">::</span><span class="nf">SQLite</span><span class="p">(),</span> <span class="s">&#34;:memory:&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">db1</span> <span class="o">&lt;-</span> <span class="nf">copy_to</span><span class="p">(</span><span class="n">con</span><span class="p">,</span> <span class="n">tibble</span><span class="o">::</span><span class="nf">tibble</span><span class="p">(</span><span class="n">x</span> <span class="o">=</span> <span class="m">1</span><span class="o">:</span><span class="m">3</span><span class="p">),</span> <span class="s">&#34;db1&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">db2</span> <span class="o">&lt;-</span> <span class="nf">copy_to</span><span class="p">(</span><span class="n">con</span><span class="p">,</span> <span class="n">tibble</span><span class="o">::</span><span class="nf">tibble</span><span class="p">(</span><span class="n">y</span> <span class="o">=</span> <span class="m">1</span><span class="o">:</span><span class="m">3</span><span class="p">,</span> <span class="n">x</span> <span class="o">=</span> <span class="m">4</span><span class="o">:</span><span class="m">6</span><span class="p">),</span> <span class="s">&#34;db2&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">bind_queries</span><span class="p">(</span><span class="n">db1</span><span class="p">,</span> <span class="n">db2</span><span class="p">)</span> <span class="o">|&gt;</span> <span class="nf">show_query</span><span class="p">()</span></span></span></code></pre></div></div>
<pre><code>&lt;SQL&gt;
SELECT *, NULL AS `y`
FROM `db1`

UNION ALL

SELECT `x`, `y`
FROM `db2`
</code></pre>
<p><code>filter_out()</code> (from <a href="https://opensource.posit.co/blog/2026-02-04_dplyr-1-2-0">dplyr 1.2.0</a>) is now translated.
It&rsquo;s the opposite of <code>filter()</code>, dropping rows where the conditions are true, rather than keeping those rows.</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="n">db3</span> <span class="o">&lt;-</span> <span class="nf">copy_to</span><span class="p">(</span><span class="n">con</span><span class="p">,</span> <span class="n">tibble</span><span class="o">::</span><span class="nf">tibble</span><span class="p">(</span><span class="n">x</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="m">1</span><span class="p">,</span> <span class="m">2</span><span class="p">,</span> <span class="kc">NA</span><span class="p">),</span> <span class="n">g</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="m">1</span><span class="p">,</span> <span class="m">1</span><span class="p">,</span> <span class="m">2</span><span class="p">)),</span> <span class="s">&#34;db3&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">db3</span> <span class="o">|&gt;</span> <span class="nf">filter_out</span><span class="p">(</span><span class="n">x</span> <span class="o">==</span> <span class="m">2</span><span class="p">)</span></span></span></code></pre></div></div>
<pre><code># A query:  ?? x 2
# Database: sqlite 3.52.0 [:memory:]
      x     g
  &lt;dbl&gt; &lt;dbl&gt;
1     1     1
2    NA     2
</code></pre>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="n">db3</span> <span class="o">|&gt;</span> <span class="nf">filter</span><span class="p">(</span><span class="n">x</span> <span class="o">==</span> <span class="m">2</span><span class="p">)</span></span></span></code></pre></div></div>
<pre><code># A query:  ?? x 2
# Database: sqlite 3.52.0 [:memory:]
      x     g
  &lt;dbl&gt; &lt;dbl&gt;
1     2     1
</code></pre>
<p><code>anyNA()</code> is now translated, in the same way as <code>any(is.na(x))</code>:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="n">db3</span> <span class="o">|&gt;</span> <span class="nf">summarise</span><span class="p">(</span><span class="n">missing</span> <span class="o">=</span> <span class="nf">anyNA</span><span class="p">(</span><span class="n">x</span><span class="p">),</span> <span class="n">.by</span> <span class="o">=</span> <span class="n">g</span><span class="p">)</span> <span class="o">|&gt;</span> <span class="nf">show_query</span><span class="p">()</span></span></span></code></pre></div></div>
<pre><code>&lt;SQL&gt;
SELECT `g`, MAX((`x` IS NULL)) AS `missing`
FROM `db3`
GROUP BY `g`
</code></pre>
<p>And <code>%notin%</code> (new in R 4.6.0) is now translated to <code>NOT IN</code>:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="n">db3</span> <span class="o">|&gt;</span> <span class="nf">filter</span><span class="p">(</span><span class="n">x</span> <span class="o">%notin%</span> <span class="nf">c</span><span class="p">(</span><span class="m">1</span><span class="p">,</span> <span class="m">2</span><span class="p">))</span> <span class="o">|&gt;</span> <span class="nf">show_query</span><span class="p">()</span></span></span></code></pre></div></div>
<pre><code>&lt;SQL&gt;
SELECT *
FROM `db3`
WHERE (`x` NOT IN (1.0, 2.0))
</code></pre>
<p>Three other changes are worth knowing about:</p>
<ul>
<li>
<p><strong><code>last_sql()</code></strong> returns the SQL of the most recent query dbplyr generated.
This is mostly useful when debugging a problem that surfaces inside <code>collect()</code> or <code>compute()</code>, when you don&rsquo;t have a convenient handle on the query to call <code>show_query()</code> on.</p>
</li>
<li>
<p><strong><code>copy = &quot;inline&quot;</code></strong> is now an option for joins, set operations, and row operations.
When you join a local data frame to a remote table, dbplyr normally copies it to a temporary table on the server.
With <code>copy = &quot;inline&quot;</code>, it inlines the data into the SQL directly using <code>copy_inline()</code>, which is handy when you can&rsquo;t (or don&rsquo;t want to) create a temporary table.</p>
</li>
<li>
<p>Thanks to <a href="https://github.com/shearerpmm" target="_blank" rel="noopener">@shearerpmm</a>, this release also gains a translation layer for IBM DB2.
The translation includes <code>paste()</code>/<code>paste0()</code> (using <code>||</code>), DB2-specific casts, <code>runif()</code>, a comprehensive set of string and date functions, the <a href="https://clock.r-lib.org" target="_blank" rel="noopener">clock</a> helpers, and statistical aggregates.</p>
</li>
</ul>
<h2 id="notes-for-backend-developers">Notes for backend developers
</h2>
<p>If you maintain a dbplyr backend, several things have changed and you may need to make some adjustments.
Hopefully none of this is a surprise, since I&rsquo;ve already filed PRs to all CRAN packages that needed changes 😀.</p>
<p>Most importantly, the new <code>sql_dialect()</code> generic separates out the connection details from SQL generation.
You should create a <code>sql_dialect()</code> method for your connection that returns a <code>new_sql_dialect()</code> object.
<code>new_sql_dialect()</code> lets you customize the full surface of SQL generation, including how identifiers are quoted (which allows tests to look much closer to real SQL).
Once you have that object, all the <code>sql_</code> generics can dispatch on it, rather than on the connection object.</p>
<p>There are two new extension points for customization:</p>
<ul>
<li>
<p><code>db_table_drop_if_exists()</code> is a new generic that lets backends customize how tables are dropped when <code>overwrite = TRUE</code>.
It was added to support Oracle, which needs a slightly different <code>DROP TABLE</code> incantation.</p>
</li>
<li>
<p><code>sql_set_op_method()</code> is a new generic that lets set operations (<code>union()</code>, <code>intersect()</code>, <code>setdiff()</code>) customize the SQL keyword they generate.
Useful when a backend needs <code>UNION DISTINCT</code> instead of <code>UNION</code>, or <code>MINUS</code> instead of <code>EXCEPT</code>.</p>
</li>
</ul>
<p>And two deprecations:</p>
<ul>
<li>
<p><code>sql_expr_matches()</code> is deprecated. Provide <code>is_distinct_from()</code> and <code>is_not_distinct_from()</code> translations instead. These power joins with <code>na_matches = &quot;na&quot;</code> and the new <code>filter_out()</code> translation.</p>
</li>
<li>
<p><code>as.sql()</code> is deprecated as part of a major internal refactor of how <code>sql()</code> and <code>ident()</code> are used. You can generally replace it with <code>as_table_path()</code> if used to refer to a table, or <code>sql()</code> if you want to indicate it&rsquo;s raw SQL.</p>
</li>
</ul>
<p>I&rsquo;ve also overhauled the exported tools for generating SQL strings. The newly exported <code>sql_glue()</code> (implicit dialect, for use inside <code>sql_translation()</code>) and <code>sql_glue2()</code> (explicit dialect, for everywhere else) provide a glue-style syntax for building SQL strings. They replace the now superseded <code>build_sql()</code>, <code>sql_expr()</code>, and <code>sql_call2()</code>.</p>
]]></description>
      <enclosure url="https://opensource.posit.co/blog/2026-06-17_dbplyr-2-6-0/featured.jpg" length="281420" type="image/jpeg" />
    </item>
    <item>
      <title>Make your SciPy presentation in Quarto</title>
      <link>https://opensource.posit.co/blog/2026-06-15_build-your-scipy-slides-with-quarto/</link>
      <pubDate>Mon, 15 Jun 2026 00:00:00 +0000</pubDate>
      <guid>https://opensource.posit.co/blog/2026-06-15_build-your-scipy-slides-with-quarto/</guid>
      <dc:creator>Isabella Velásquez</dc:creator>
      <dc:creator>Charlotte Wickham</dc:creator><description><![CDATA[<p><a href="https://www.scipy2026.scipy.org/" target="_blank" rel="noopener">SciPy 2026</a> is soon, and we are psyched! We have a <a href="https://opensource.posit.co/events/scipy-2026/" target="_blank" rel="noopener">whole troupe of Posit folks</a> heading to Minneapolis to celebrate open source, scientific computing, and Python tooling.</p>
<p>If you are preparing for your next talk (whether at SciPy or another conference), you might be looking for the most effective way to showcase code and its output in your deck. And, if we do say so ourselves, <a href="https://quarto.org/" target="_blank" rel="noopener">Quarto</a> is the premier tool for creating polished presentations that shine in scientific contexts.</p>
<p>Why? Because Quarto was designed just for this purpose! As an open-source scientific and technical publishing system, Quarto allows you to craft dynamic content that weaves your narrative together with Markdown and Python code, and then renders them into a single file that elegantly showcases your results. By using a single source file, you don&rsquo;t have to manually copy and paste code or its outputs. Any changes you make are reflected in the latest version by rerendering the document.</p>
<p>Hopefully, this has piqued your interest!
Curious how it looks? We built the deck below in Quarto; keep reading to learn how.</p>
<iframe class="slide-deck" src="https://opensource.posit.co/blog/2026-06-15_build-your-scipy-slides-with-quarto/slide-08.html" loading="lazy" width="560" height="373">
</iframe>
<div class="callout callout-tip" role="note" aria-label="Tip">
<div class="callout-header">
<span class="callout-title">Navigating slides</span>
</div>
<div class="callout-body">
<p>To advance through the slides, hit <kbd>Space</kbd> or <kbd>→</kbd>.
To go back, hit <kbd>←</kbd>.
Click the hamburger menu (<span class="icon-[codicon--menu] align-text-bottom"></span>) in the slide deck to see other options.</p>
</div>
</div>
<h2 id="get-started-with-slides-in-quarto">Get started with slides in Quarto
</h2>
<h3 id="install-quarto">Install Quarto
</h3>
<p>We recommend using <a href="https://positron.posit.co/" target="_blank" rel="noopener">Positron</a> as your editor, since Quarto is preinstalled. If you are using VS Code, install the <a href="https://quarto.org/docs/download/" target="_blank" rel="noopener">Quarto CLI</a> and the <a href="https://marketplace.visualstudio.com/items?itemName=quarto.quarto" target="_blank" rel="noopener">Quarto VS Code Extension</a>.</p>
<h3 id="create-a-new-document">Create a new document
</h3>
<p>Open a new folder, enter the Command Palette (<kbd>Cmd</kbd>+<kbd>Shift</kbd>+<kbd>P</kbd>) and search for <code>Quarto: New Quarto document (qmd)</code>. Save the file as <code>index.qmd</code>.</p>
<h3 id="change-the-format-to-revealjs">Change the format to revealjs
</h3>
<p>Your new document will include a YAML header for managing your document&rsquo;s metadata. You&rsquo;ll see that the default is set to HTML via <code>format: html</code>:</p>
<div class="code-block code-with-filename" role="group" aria-labelledby="code-filename-0">
  <div class="code-with-filename-label" id="code-filename-0"><span class="font-mono text-sm">index.qmd</span></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nn">---</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">title</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;Untitled&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">format</span><span class="p">:</span><span class="w"> </span><span class="l">html</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nn">---</span></span></span></code></pre></div></div>
<p>However, Quarto is incredibly versatile, and it can render your work into PDFs, Word documents, and much more! We recommend the reveal.js format for creating aesthetically pleasing code-centric slides. reveal.js also provides a variety of useful features tailored for displaying scientific results and technical workflows.</p>
<p>You can switch your document from HTML to reveal.js by updating the format in your YAML header to <code>format: revealjs</code>:</p>
<div class="code-block code-with-filename" role="group" aria-labelledby="code-filename-1">
  <div class="code-with-filename-label" id="code-filename-1"><span class="font-mono text-sm">index.qmd</span></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nn">---</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">title</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;Untitled&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">format</span><span class="p">:</span><span class="w"> </span><span class="l">revealjs</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nn">---</span></span></span></code></pre></div></div>
<p>Click the Preview button (<span class="icon-[codicon--preview] align-text-bottom"></span>) at the top left of your editor to see what your presentation looks like!</p>
<p>You can also preview from the command line:</p>
<div class="code-block code-with-filename" role="group" aria-labelledby="code-filename-2">
  <div class="code-with-filename-label" id="code-filename-2"><span class="font-mono text-sm">Terminal</span></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">quarto preview index.qmd</span></span></code></pre></div></div>
<iframe class="slide-deck" src="https://opensource.posit.co/blog/2026-06-15_build-your-scipy-slides-with-quarto/slide-01.html" loading="lazy" width="560" height="373">
</iframe>
<h2 id="build-the-slides">Build the slides
</h2>
<p>From here, we can move on to the fun part: building our slides!</p>
<h3 id="edit-metadata">Edit metadata
</h3>
<p>As alluded to earlier, we edit the metadata document by modifying the YAML header. You can change the title, add yourself as an author, and add your presentation date:</p>
<div class="code-block code-with-filename" role="group" aria-labelledby="code-filename-3">
  <div class="code-with-filename-label" id="code-filename-3"><span class="font-mono text-sm">index.qmd</span></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nn">---</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">title</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;Make your SciPy presentation in Quarto&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">author</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">Isabella Velásquez</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">Charlotte Wickham</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">date</span><span class="p">:</span><span class="w"> </span><span class="ld">2026-07-14</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">format</span><span class="p">:</span><span class="w"> </span><span class="l">revealjs</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nn">---</span></span></span></code></pre></div></div>
<p>Those values then set up your title slide.</p>
<iframe class="slide-deck" src="https://opensource.posit.co/blog/2026-06-15_build-your-scipy-slides-with-quarto/slide-02.html" loading="lazy" width="560" height="373">
</iframe>
<h3 id="add-your-first-slide">Add your first slide
</h3>
<p>Below the YAML header is where you write your slides. Quarto uses Markdown, a text formatting language, for text:</p>
<div class="code-block code-with-filename" role="group" aria-labelledby="code-filename-4">
  <div class="code-with-filename-label" id="code-filename-4"><span class="font-mono text-sm">index.qmd</span></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="cl">---
</span></span><span class="line"><span class="cl">title: &#34;Make your SciPy presentation in Quarto&#34;
</span></span><span class="line"><span class="cl">author:
</span></span><span class="line"><span class="cl">  <span class="k">-</span> Isabella Velásquez
</span></span><span class="line"><span class="cl">  <span class="k">-</span> Charlotte Wickham
</span></span><span class="line"><span class="cl">date: 2026-07-14
</span></span><span class="line"><span class="cl">format: revealjs
</span></span><span class="line"><span class="cl">---
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Slides are just Markdown and code:
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">-</span> Write your narrative in <span class="gs">**Markdown**</span>
</span></span><span class="line"><span class="cl"><span class="k">-</span> Weave in <span class="gs">**Python**</span> code and its output
</span></span><span class="line"><span class="cl">- Render it all to one reproducible file</span></span></code></pre></div></div>
<p>You can control aspects of your presentation by nesting options under <code>revealjs</code> in the YAML header.
For example, you can make any list display incrementally by adding <code>incremental: true</code>:</p>
<div class="code-block code-with-filename" role="group" aria-labelledby="code-filename-5">
  <div class="code-with-filename-label" id="code-filename-5"><span class="font-mono text-sm">index.qmd</span></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nn">---</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">title</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;Make your SciPy presentation in Quarto&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">author</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">Isabella Velásquez</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">Charlotte Wickham</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">date</span><span class="p">:</span><span class="w"> </span><span class="ld">2026-07-14</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">format</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">revealjs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">incremental</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nn">---</span></span></span></code></pre></div></div>
<p>Now as you advance the slides, the bullets appear one at a time.</p>
<iframe class="slide-deck" src="https://opensource.posit.co/blog/2026-06-15_build-your-scipy-slides-with-quarto/slide-03.html#/1" loading="lazy" width="560" height="373">
</iframe>
<h3 id="add-a-new-slide">Add a new slide
</h3>
<p>To add a new slide, add a new heading. A Level 1 heading adds a new section, while a Level 2 heading adds a new slide.</p>
<div class="code-block code-with-filename" role="group" aria-labelledby="code-filename-6">
  <div class="code-with-filename-label" id="code-filename-6"><span class="font-mono text-sm">index.qmd</span></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="cl"><span class="gh"># Features for showing code
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">## Add Python code cells</span></span></code></pre></div></div>
<iframe class="slide-deck" src="https://opensource.posit.co/blog/2026-06-15_build-your-scipy-slides-with-quarto/slide-04.html#/features-for-showing-code" loading="lazy" width="560" height="373">
</iframe>
<p>Continue adding new slides until you&rsquo;re done!</p>
<h3 id="add-python-code-cells">Add Python code cells
</h3>
<p>To add Python code to your slide, put it within a code cell, which is delineated by three backticks <code>```</code> and <code>python</code> within curly braces. By default, Quarto reveal.js presentations do not show your code cells, because they assume you are presenting just your output. If you do want to show your code, you can add a <em>cell option</em>, designated by a special comment, <code>#|</code>, followed by the option using YAML syntax. In this case, <code>echo: true</code> lets Quarto know to show the code.</p>
<div class="code-block code-with-filename" role="group" aria-labelledby="code-filename-7">
  <div class="code-with-filename-label" id="code-filename-7"><span class="font-mono text-sm">index.qmd</span></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="cl"><span class="gu">## Add Python code cells
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">``<span class="sb">`{python}
</span></span></span><span class="line"><span class="cl"><span class="sb">#| echo: true
</span></span></span><span class="line"><span class="cl"><span class="sb">from plotnine import *
</span></span></span><span class="line"><span class="cl"><span class="sb">from plotnine.data import anscombe_quartet
</span></span></span><span class="line"><span class="cl"><span class="sb">`</span>``</span></span></code></pre></div></div>
<iframe class="slide-deck" src="https://opensource.posit.co/blog/2026-06-15_build-your-scipy-slides-with-quarto/slide-04b.html#/add-python-code-cells" loading="lazy" width="560" height="373">
</iframe>
<h3 id="code-output-is-included">Code output is included
</h3>
<p>When you render, Quarto runs the code cells and includes their output right below the code:</p>
<div class="code-block code-with-filename" role="group" aria-labelledby="code-filename-8">
  <div class="code-with-filename-label" id="code-filename-8"><span class="font-mono text-sm">index.qmd</span></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="cl"><span class="gu">## Code output is included
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">``<span class="sb">`{python}
</span></span></span><span class="line"><span class="cl"><span class="sb">#| echo: true
</span></span></span><span class="line"><span class="cl"><span class="sb">anscombe_quartet.head()
</span></span></span><span class="line"><span class="cl"><span class="sb">`</span>``</span></span></code></pre></div></div>
<iframe class="slide-deck" src="https://opensource.posit.co/blog/2026-06-15_build-your-scipy-slides-with-quarto/slide-04c.html#/code-output-is-included" loading="lazy" width="560" height="373">
</iframe>
<h3 id="show-code-without-running-it">Show code without running it
</h3>
<p>Some code only makes sense to run live &mdash; like <code>%view</code>, which opens your data in Positron&rsquo;s Data Explorer. To show such code without running it, add the <code>eval: false</code> cell option, which tells Quarto to show the cell but not evaluate it:</p>
<div class="code-block code-with-filename" role="group" aria-labelledby="code-filename-9">
  <div class="code-with-filename-label" id="code-filename-9"><span class="font-mono text-sm">index.qmd</span></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="cl"><span class="gu">## You can show code without running it
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">``<span class="sb">`{python}
</span></span></span><span class="line"><span class="cl"><span class="sb">#| echo: true
</span></span></span><span class="line"><span class="cl"><span class="sb">#| eval: false
</span></span></span><span class="line"><span class="cl"><span class="sb">%view anscombe_quartet
</span></span></span><span class="line"><span class="cl"><span class="sb">`</span>``</span></span></code></pre></div></div>
<iframe class="slide-deck" src="https://opensource.posit.co/blog/2026-06-15_build-your-scipy-slides-with-quarto/slide-05.html#/you-can-show-code-without-running-it" loading="lazy" width="560" height="373">
</iframe>
<h3 id="add-a-theme">Add a theme
</h3>
<p>The Quarto default look is great, but say you want to add your own theming. You can do that in a single YAML file using <a href="https://posit-dev.github.io/brand-yml/" target="_blank" rel="noopener">brand.yml</a>.</p>
<p>In your root directory, create a file called <code>_brand.yml</code>. Add specifications as listed on the <a href="https://posit-dev.github.io/brand-yml/brand/" target="_blank" rel="noopener">brand.yml documentation site</a>. You can edit the font, colors, and more (add as much or as little as you&rsquo;d like). Next time you render your slides, the brand will be automatically picked up!</p>
<p>If you&rsquo;d like a <code>_brand.yml</code> file to get you started, <a href="https://gist.github.com/ivelasq/fa6e52022ee9e390c0b12d4242155fa5" target="_blank" rel="noopener">here is one that mimics the SciPy conference style</a>. Put it in the root directory of your presentation, and it will look like this:</p>
<iframe class="slide-deck" src="https://opensource.posit.co/blog/2026-06-15_build-your-scipy-slides-with-quarto/slide-brand.html" loading="lazy" width="560" height="373">
</iframe>
<h2 id="features-for-showcasing-python-code">Features for showcasing Python code
</h2>
<p>You may be thinking, this is great, but I can make slides in any old software! True, but Quarto has specific features that really elevate your audience&rsquo;s experience.</p>
<h3 id="code-line-highlighting">Code line highlighting
</h3>
<p>Sure, you can show a block of code on your slide. But you&rsquo;re usually talking about a specific line or lines of code at a time. If you want to draw your audience&rsquo;s attention to a specific section of your code, you can use <a href="https://quarto.org/docs/presentations/revealjs/index.html#line-highlighting" target="_blank" rel="noopener">code line highlighting</a>.</p>
<p>For example, if you want to first highlight line 2, then lines 3 through 4, then line 5, you can add the <code>code-line-numbers</code> cell option:</p>
<div class="code-block code-with-filename" role="group" aria-labelledby="code-filename-10">
  <div class="code-with-filename-label" id="code-filename-10"><span class="font-mono text-sm">index.qmd</span></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="cl">``<span class="sb">`{python}
</span></span></span><span class="line"><span class="cl"><span class="sb">#| echo: true
</span></span></span><span class="line"><span class="cl"><span class="sb">#| code-line-numbers: &#34;2|3-4|5&#34;
</span></span></span><span class="line"><span class="cl"><span class="sb">(
</span></span></span><span class="line"><span class="cl"><span class="sb">    anscombe_quartet
</span></span></span><span class="line"><span class="cl"><span class="sb">    .groupby(&#34;dataset&#34;)[[&#34;x&#34;, &#34;y&#34;]]
</span></span></span><span class="line"><span class="cl"><span class="sb">    .apply(lambda g: g[&#34;x&#34;].corr(g[&#34;y&#34;]))
</span></span></span><span class="line"><span class="cl"><span class="sb">    .round(2).to_frame(&#34;correlation&#34;)
</span></span></span><span class="line"><span class="cl"><span class="sb">)
</span></span></span><span class="line"><span class="cl"><span class="sb">`</span>``</span></span></code></pre></div></div>
<p>Scroll through to see what it looks like:</p>
<iframe class="slide-deck" src="https://opensource.posit.co/blog/2026-06-15_build-your-scipy-slides-with-quarto/slide-06.html#/highlight-lines-as-you-talk" loading="lazy" width="560" height="373">
</iframe>
<h3 id="show-code-output-in-a-new-slide">Show code output in a new slide
</h3>
<p>When rendered, the slides will automatically resize based on the content. However, this means if you have a lot of code and a large output (like an image), it will be hard to see both in a single slide.</p>
<p>Luckily, the Quarto developers have thought of this and have the <code>output-location</code> cell option where you designate where the output should show up. For example, to show the output on the new slide, set <code>output-location</code> to <code>slide</code>:</p>
<div class="code-block code-with-filename" role="group" aria-labelledby="code-filename-11">
  <div class="code-with-filename-label" id="code-filename-11"><span class="font-mono text-sm">index.qmd</span></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="cl">``<span class="sb">`{python}
</span></span></span><span class="line"><span class="cl"><span class="sb">#| echo: true
</span></span></span><span class="line"><span class="cl"><span class="sb">#| output-location: slide
</span></span></span><span class="line"><span class="cl"><span class="sb">(
</span></span></span><span class="line"><span class="cl"><span class="sb">    ggplot(anscombe_quartet, aes(&#34;x&#34;, &#34;y&#34;))
</span></span></span><span class="line"><span class="cl"><span class="sb">    + geom_point(color=&#34;sienna&#34;, fill=&#34;orange&#34;, size=3)
</span></span></span><span class="line"><span class="cl"><span class="sb">    + geom_smooth(method=&#34;lm&#34;, se=False, fullrange=True,
</span></span></span><span class="line"><span class="cl"><span class="sb">                  color=&#34;steelblue&#34;, size=1)
</span></span></span><span class="line"><span class="cl"><span class="sb">    + facet_wrap(&#34;dataset&#34;)
</span></span></span><span class="line"><span class="cl"><span class="sb">    + labs(title=&#34;Anscombe’s Quartet&#34;)
</span></span></span><span class="line"><span class="cl"><span class="sb">    + scale_y_continuous(breaks=(4, 8, 12))
</span></span></span><span class="line"><span class="cl"><span class="sb">    + coord_fixed(xlim=(3, 22), ylim=(2, 14))
</span></span></span><span class="line"><span class="cl"><span class="sb">    + theme_tufte(base_family=&#34;Futura&#34;, base_size=16)
</span></span></span><span class="line"><span class="cl"><span class="sb">    + theme(
</span></span></span><span class="line"><span class="cl"><span class="sb">        axis_line=element_line(color=&#34;#4d4d4d&#34;),
</span></span></span><span class="line"><span class="cl"><span class="sb">        axis_ticks_major=element_line(color=&#34;#00000000&#34;),
</span></span></span><span class="line"><span class="cl"><span class="sb">        axis_title=element_blank(),
</span></span></span><span class="line"><span class="cl"><span class="sb">        panel_spacing=0.09,
</span></span></span><span class="line"><span class="cl"><span class="sb">    )
</span></span></span><span class="line"><span class="cl"><span class="sb">)
</span></span></span><span class="line"><span class="cl"><span class="sb">`</span>``</span></span></code></pre></div></div>
<iframe class="slide-deck" src="https://opensource.posit.co/blog/2026-06-15_build-your-scipy-slides-with-quarto/slide-07.html#/send-big-output-to-its-own-slide" loading="lazy" width="560" height="373">
</iframe>
<h3 id="add-a-filename-and-extension">Add a filename and extension
</h3>
<p>A subtle (but very helpful) feature of Quarto is the ability to specify the filename and extension of a code block, which you can use if you are talking about different files or programming languages and want to visually distinguish them. Add <code>filename=</code> to your code block:</p>
<div class="code-block code-with-filename" role="group" aria-labelledby="code-filename-12">
  <div class="code-with-filename-label" id="code-filename-12"><span class="font-mono text-sm">index.qmd</span></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="cl">``<span class="sb">`{.bash filename=&#34;Terminal&#34;}
</span></span></span><span class="line"><span class="cl"><span class="sb">pip install pandas
</span></span></span><span class="line"><span class="cl"><span class="sb">`</span>`<span class="sb">`
</span></span></span><span class="line"><span class="cl"><span class="sb">
</span></span></span><span class="line"><span class="cl"><span class="sb">&lt;br&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">
</span></span></span><span class="line"><span class="cl"><span class="sb">`</span>`<span class="sb">`{.python filename=&#34;script.py&#34;}
</span></span></span><span class="line"><span class="cl"><span class="sb">import pandas as pd
</span></span></span><span class="line"><span class="cl"><span class="sb">`</span>``</span></span></code></pre></div></div>
<iframe class="slide-deck" src="https://opensource.posit.co/blog/2026-06-15_build-your-scipy-slides-with-quarto/slide-08.html#/label-code-with-filenames" loading="lazy" width="560" height="373">
</iframe>
<div class="callout callout-tip" role="note" aria-label="Tip">
<div class="callout-header">
<span class="callout-title">Code blocks vs. code cells</span>
</div>
<div class="callout-body">
<p>The <code>.</code> in front of the language in the code above (e.g. <code>.python</code>) signals these are <em>code blocks</em>, not <em>executable code cells</em>.
Quarto doesn&rsquo;t attempt to execute code blocks, but you get nice syntax highlighting based on the language specified.</p>
</div>
</div>
<h2 id="extensions-for-scientific-presentation">Extensions for scientific presentation
</h2>
<p>So far, we&rsquo;ve just mentioned features that are built into Quarto; however, one of the most powerful features of Quarto is its <a href="https://quarto.org/docs/extensions/" target="_blank" rel="noopener">extension system</a>. Both the Posit developers and the community have built an incredible ecosystem of extensions that augment what Quarto can do. There are hundreds of extensions (see the <a href="https://m.canouil.dev/quarto-extensions/" target="_blank" rel="noopener">Quarto gallery</a> by Mickaël Canouil), but here are a curated few.</p>
<h3 id="quartolive-extension-for-interactive-python-code-blocks">QuartoLive extension for interactive Python code blocks
</h3>
<p>You no longer have to switch between your slides and your IDE to run code! Using <a href="https://r-wasm.github.io/quarto-live/" target="_blank" rel="noopener">QuartoLive</a>, you can embed interactive Python code blocks. Run the code, edit it, and rerun, all within your Quarto reveal.js presentation.</p>
<h3 id="provide-a-direct-link-to-your-slides">Provide a direct link to your slides
</h3>
<p>Usually, the first question asked during a presentation is, &ldquo;Will the slides be provided?&rdquo; Evade that question by using the <a href="https://github.com/jmbuhr/quarto-qrcode" target="_blank" rel="noopener">QR code extension</a>, which adds a QR code that the audience can easily scan while they watch so they can find the slides after the presentation is over.</p>
<h3 id="add-accessibility-features">Add accessibility features
</h3>
<p>For your audience to be able to read the text on your slides, the font size should <a href="https://thinkoutsidetheslide.com/wp-content/uploads/2012/08/ViewingDistanceTable16x9.pdf" target="_blank" rel="noopener">probably be larger than you anticipate</a>.</p>
<p>The <a href="https://github.com/mcanouil/quarto-revealjs-a11y" target="_blank" rel="noopener">reveal.js A11y</a> extension provides accessibility features for your slide deck. One nifty feature is the ability to increase the font size on the fly. Enter the menu by pressing <kbd>A</kbd>, and then zoom in to what you need.</p>
<h2 id="publish-and-share-your-slides">Publish and share your slides
</h2>
<p>After you&rsquo;ve done the fun work of creating your slides, you&rsquo;ll want to publish and share them with others. We recommend <a href="https://connect.posit.cloud/" target="_blank" rel="noopener">Posit Connect Cloud</a>, which has a generous free tier for Quarto documents, Streamlit and Shiny apps, and much more.</p>
<p>If you&rsquo;re working in Positron, the <a href="https://docs.posit.co/connect-cloud/user/publish/ide.html" target="_blank" rel="noopener">Posit Publisher extension</a> is built in and gives you push-button deployment to Posit Connect Cloud right from your editor (in VS Code, grab it from the <a href="https://marketplace.visualstudio.com/items?itemName=Posit.publisher" target="_blank" rel="noopener">Marketplace</a>).</p>
<p>If not, you can also publish from the command line with <code>quarto publish</code>, which renders your slides and uploads them:</p>
<div class="code-block code-with-filename" role="group" aria-labelledby="code-filename-13">
  <div class="code-with-filename-label" id="code-filename-13"><span class="font-mono text-sm">Terminal</span></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">quarto publish posit-connect-cloud index.qmd</span></span></code></pre></div></div>
<p>The slides for this very post are published at <a href="https://posit-devrel-make-your-scipy-presentation-in-quarto.share.connect.posit.cloud" target="_blank" rel="noopener">posit-devrel-make-your-scipy-presentation-in-quarto.share.connect.posit.cloud</a>.</p>
<p>Press <kbd>F</kbd> to enter full screen, and you&rsquo;re ready to present!</p>
<h2 id="welcome-to-the-wonderful-world-of-quarto-slidecrafting">Welcome to the wonderful world of Quarto slidecrafting
</h2>
<p>We hope that this post inspired you to give Quarto presentations a try!</p>
<p>We&rsquo;ve only scratched the surface &mdash; the <a href="https://quarto.org/docs/presentations/revealjs/" target="_blank" rel="noopener">Quarto reveal.js documentation</a> covers the full set of features for building presentations.</p>
<p>If you are completely new to Quarto, <a href="https://www.youtube.com/watch?v=QU0N--y-skA&amp;lc=Ugxz1YOUWPrHjQAq0oZ4AaABAg" target="_blank" rel="noopener">Charlotte&rsquo;s YouTube video</a> is a great introduction to setting it up, creating a document from scratch, and rendering it.</p>
<p>If you want to see what else is possible, Emil Hvitfeldt is working on a book called <a href="https://slidecrafting-book.com/" target="_blank" rel="noopener">Slidecrafting</a> on creating functional and beautiful Quarto reveal.js presentations through theming, layout, interactivity, and extensions.</p>
<p>And definitely look through Mickaël&rsquo;s <a href="https://m.canouil.dev/quarto-extensions/" target="_blank" rel="noopener">Quarto extension gallery</a> to see if other extensions are useful to you. His blog post, <a href="https://mickael.canouil.fr/posts/2026-04-21-quarto-revealjs-extensions/" target="_blank" rel="noopener">Quarto Reveal.js Extensions to Sharpen Your Slides</a>, also has more advanced tricks you can apply to your scientific and technical publications.</p>
<p>Do let us know what you end up creating!</p>
]]></description>
      <enclosure url="https://opensource.posit.co/blog/2026-06-15_build-your-scipy-slides-with-quarto/featured.jpg" length="132652" type="image/jpeg" />
    </item>
    <item>
      <title>tidyclust 0.3.0</title>
      <link>https://opensource.posit.co/blog/2026-06-15_tidyclust-0-3-0/</link>
      <pubDate>Mon, 15 Jun 2026 00:00:00 +0000</pubDate>
      <guid>https://opensource.posit.co/blog/2026-06-15_tidyclust-0-3-0/</guid>
      <dc:creator>Emil Hvitfeldt</dc:creator><description><![CDATA[<p>I&rsquo;m thrilled to announce that <a href="https://tidyclust.tidymodels.org/" target="_blank" rel="noopener">tidyclust 0.3.0</a> is now on CRAN.
tidyclust allows you to fit, interact, and evaluate unsupervised clustering models inside the tidymodels framework.
You can install it with:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="nf">install.packages</span><span class="p">(</span><span class="s">&#34;tidyclust&#34;</span><span class="p">)</span></span></span></code></pre></div></div>
<p>This release brings three new families of clustering models,
closer alignment with the rest of tidymodels,
and a number of smaller improvements.
This post covers the highlights of new models and engines,
tighter integration with tidymodels,
and updated parallel processing support.
You can read the full list of changes in the <a href="https://tidyclust.tidymodels.org/news/index.html#tidyclust-030" target="_blank" rel="noopener">release notes</a>.</p>
<p>To get started,
load the tidymodels and tidyclust packages:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="nf">library</span><span class="p">(</span><span class="n">tidymodels</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nf">library</span><span class="p">(</span><span class="n">tidyclust</span><span class="p">)</span></span></span></code></pre></div></div>
<p>For the examples, we will be using the <code>penguins</code> data set.
We are imputing missing values and normalizing the predictors since distance and density-based methods are sensitive to scale and missingness.</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="nf">data</span><span class="p">(</span><span class="n">penguins</span><span class="p">,</span> <span class="n">package</span> <span class="o">=</span> <span class="s">&#34;modeldata&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">rec_spec</span> <span class="o">&lt;-</span> <span class="nf">recipe</span><span class="p">(</span><span class="o">~</span> <span class="n">bill_length_mm</span> <span class="o">+</span> <span class="n">bill_depth_mm</span> <span class="o">+</span> <span class="n">flipper_length_mm</span> <span class="o">+</span> <span class="n">body_mass_g</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                   <span class="n">data</span> <span class="o">=</span> <span class="n">penguins</span><span class="p">)</span> <span class="o">|&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nf">step_impute_mean</span><span class="p">(</span><span class="nf">all_numeric_predictors</span><span class="p">())</span> <span class="o">|&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nf">step_normalize</span><span class="p">(</span><span class="nf">all_numeric_predictors</span><span class="p">())</span></span></span></code></pre></div></div>
<h2 id="new-models-and-engines">New models and engines
</h2>
<p>Until now, tidyclust supported partition-based methods (<code>k_means()</code>) and hierarchical clustering (<code>hier_clust()</code>).
This release expands that considerably with three new model specifications.</p>
<p><a href="https://tidyclust.tidymodels.org/reference/db_clust.html" target="_blank" rel="noopener"><code>db_clust()</code></a> fits density-based clustering models (DBSCAN),
with engines for both <code>&quot;dbscan&quot;</code> and <code>&quot;hdbscan&quot;</code>.
Density-based methods are well suited to finding clusters of arbitrary shape and identifying outliers as noise.</p>
<p>To use tidyclust,
we first make a specification for the clustering method,
combine it with the recipe, and fit the workflow as usual.</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="n">db_spec</span> <span class="o">&lt;-</span> <span class="nf">db_clust</span><span class="p">(</span><span class="n">radius</span> <span class="o">=</span> <span class="m">0.8</span><span class="p">,</span> <span class="n">min_points</span> <span class="o">=</span> <span class="m">5</span><span class="p">)</span> <span class="o">|&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nf">set_engine</span><span class="p">(</span><span class="s">&#34;dbscan&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">db_fit</span> <span class="o">&lt;-</span> <span class="nf">workflow</span><span class="p">(</span><span class="n">rec_spec</span><span class="p">,</span> <span class="n">db_spec</span><span class="p">)</span> <span class="o">|&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nf">fit</span><span class="p">(</span><span class="n">data</span> <span class="o">=</span> <span class="n">penguins</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">db_fit</span></span></span></code></pre></div></div>
<pre><code>══ Workflow [trained] ══════════════════════════════════════════════════════════
Preprocessor: Recipe
Model: db_clust()

── Preprocessor ────────────────────────────────────────────────────────────────
2 Recipe Steps

• step_impute_mean()
• step_normalize()

── Model ───────────────────────────────────────────────────────────────────────
DBSCAN clustering for 344 objects.
Parameters: eps = 0.8, minPts = 5
Using euclidean distances and borderpoints = TRUE
The clustering contains 2 cluster(s) and 5 noise points.

  0   1   2 
  5 218 121 

Available fields: cluster, eps, minPts, metric, borderPoints
</code></pre>
<p>Notice the <code>Outlier</code> level: points in low-density regions aren&rsquo;t forced into a cluster.
This is also our first new engine where you don&rsquo;t specify how many clusters to find up front.</p>
<p><a href="https://tidyclust.tidymodels.org/reference/gm_clust.html" target="_blank" rel="noopener"><code>gm_clust()</code></a> fits Gaussian mixture models via the <code>&quot;mclust&quot;</code> engine.
Rather than assigning each observation to a single cluster,
mixture models describe the data as a combination of Gaussian components.</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="n">gm_spec</span> <span class="o">&lt;-</span> <span class="nf">gm_clust</span><span class="p">(</span><span class="n">num_clusters</span> <span class="o">=</span> <span class="m">3</span><span class="p">)</span> <span class="o">|&gt;</span> 
</span></span><span class="line"><span class="cl">  <span class="nf">set_engine</span><span class="p">(</span><span class="s">&#34;mclust&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">gm_fit</span> <span class="o">&lt;-</span> <span class="nf">workflow</span><span class="p">(</span><span class="n">rec_spec</span><span class="p">,</span> <span class="n">gm_spec</span><span class="p">)</span> <span class="o">|&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nf">fit</span><span class="p">(</span><span class="n">data</span> <span class="o">=</span> <span class="n">penguins</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">tidy</span><span class="p">(</span><span class="n">gm_fit</span><span class="p">)</span></span></span></code></pre></div></div>
<pre><code># A tibble: 3 × 8
  component  size proportion variance mean.bill_length_mm mean.bill_depth_mm
      &lt;int&gt; &lt;int&gt;      &lt;dbl&gt;    &lt;dbl&gt;               &lt;dbl&gt;              &lt;dbl&gt;
1         1   160      0.458    0.288              -0.892              0.524
2         2    61      0.184    0.288               0.938              0.834
3         3   123      0.358    0.288               0.658             -1.10 
# ℹ 2 more variables: mean.flipper_length_mm &lt;dbl&gt;, mean.body_mass_g &lt;dbl&gt;
</code></pre>
<p>Each row summarizes one of the fitted Gaussian components:
its size, mixing proportion, and the per-predictor means that describe where it sits.</p>
<p><a href="https://tidyclust.tidymodels.org/reference/mean_shift.html" target="_blank" rel="noopener"><code>mean_shift()</code></a> fits mean shift models,
which iteratively shift observations toward regions of high density and determine the number of clusters automatically.
Engines <code>&quot;LPCM&quot;</code> and <code>&quot;meanShiftR&quot;</code> are supported.</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="n">ms_spec</span> <span class="o">&lt;-</span> <span class="nf">mean_shift</span><span class="p">(</span><span class="n">bandwidth</span> <span class="o">=</span> <span class="m">0.2</span><span class="p">)</span> <span class="o">|&gt;</span> 
</span></span><span class="line"><span class="cl">  <span class="nf">set_engine</span><span class="p">(</span><span class="s">&#34;LPCM&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">ms_fit</span> <span class="o">&lt;-</span> <span class="nf">workflow</span><span class="p">(</span><span class="n">rec_spec</span><span class="p">,</span> <span class="n">ms_spec</span><span class="p">)</span> <span class="o">|&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nf">fit</span><span class="p">(</span><span class="n">data</span> <span class="o">=</span> <span class="n">penguins</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">extract_cluster_assignment</span><span class="p">(</span><span class="n">ms_fit</span><span class="p">)</span></span></span></code></pre></div></div>
<pre><code># A tibble: 344 × 1
   .cluster 
   &lt;fct&gt;    
 1 Cluster_1
 2 Cluster_1
 3 Cluster_1
 4 Cluster_1
 5 Cluster_1
 6 Cluster_1
 7 Cluster_1
 8 Cluster_1
 9 Cluster_1
10 Cluster_1
# ℹ 334 more rows
</code></pre>
<p>This returns a cluster label for each observation,
with the number of clusters discovered automatically rather than set in advance like how it was in <code>db_clust()</code>.</p>
<p>Each of these models comes with its respective <code>dials</code> parameters for tuning.</p>
<h2 id="closer-alignment-with-tidymodels">Closer alignment with tidymodels
</h2>
<p>A major theme of this release is removing the seams between tidyclust and the rest of tidymodels.
Tidyclust was previously written as a slightly modified copy of the internals of the tune package.
We took some time to align the internals of tune such that we could use the same code paths for tidyclust and tune.
This means that we have a smaller maintenance burden and new features in tune should be more easily available in tidyclust.</p>
<p>The biggest change is that <code>finalize_model_tidyclust()</code> and <code>finalize_workflow_tidyclust()</code> are now deprecated.
The corresponding functions in tune <code>tune::finalize_model()</code> and <code>tune::finalize_workflow()</code> now support <code>cluster_spec</code> objects natively,
so there is no longer a need for tidyclust-specific variants.</p>
<p>Parallel processing in <code>tune_cluster()</code> now supports the <a href="https://mirai.r-lib.org/" target="_blank" rel="noopener">mirai</a> package in addition to <code>future</code>.
This matches the parallelism approach used across tidymodels.
As part of this change,
the <code>foreach</code> package is no longer supported for parallel processing&mdash;use <code>future</code> or <code>mirai</code> instead.
The <code>.config</code> column produced by <code>tune_cluster()</code> has also changed from the <code>Preprocessor{num}_Model{num}</code> pattern to <code>pre{num}_mod{num}_post{num}</code> to align with updates in tune.</p>
<p>A few other improvements worth calling out:</p>
<ul>
<li>A new <a href="https://tidyclust.tidymodels.org/articles/tidyclust.html" target="_blank" rel="noopener">&ldquo;Getting started with tidyclust&rdquo;</a> vignette.</li>
<li><code>butcher</code> support for <code>cluster_fit</code> objects, so you can strip training data and environment references from fitted models before saving them.</li>
<li><code>extract_cluster_assignment()</code>, <code>extract_centroids()</code>, and <code>predict()</code> gain a <code>labels</code> argument for supplying custom cluster labels.</li>
<li><code>hier_clust()</code> gains a <code>dist_fun</code> argument for specifying a custom distance function.</li>
</ul>
<h2 id="acknowledgements">Acknowledgements
</h2>
<p>A big thank you to everyone who has contributed issues, pull requests, and discussion since the last release!
<a href="https://github.com/brendad8" target="_blank" rel="noopener">@brendad8</a>, <a href="https://github.com/davidrsch" target="_blank" rel="noopener">@davidrsch</a>, <a href="https://github.com/dnldelarosa" target="_blank" rel="noopener">@dnldelarosa</a>, <a href="https://github.com/EmilHvitfeldt" target="_blank" rel="noopener">@EmilHvitfeldt</a>, <a href="https://github.com/jeroenjanssens" target="_blank" rel="noopener">@jeroenjanssens</a>, <a href="https://github.com/kbodwin" target="_blank" rel="noopener">@kbodwin</a>, <a href="https://github.com/lgaborini" target="_blank" rel="noopener">@lgaborini</a>, <a href="https://github.com/topepo" target="_blank" rel="noopener">@topepo</a>, and <a href="https://github.com/Wander03" target="_blank" rel="noopener">@Wander03</a>.</p>
]]></description>
      <enclosure url="https://opensource.posit.co/blog/2026-06-15_tidyclust-0-3-0/thumbnail.jpg" length="233780" type="image/jpeg" />
    </item>
    <item>
      <title>A brief and biased history of Posit data science agents</title>
      <link>https://opensource.posit.co/blog/2026-06-11_history-of-posit-data-science-agents/</link>
      <pubDate>Thu, 11 Jun 2026 00:00:00 +0000</pubDate>
      <guid>https://opensource.posit.co/blog/2026-06-11_history-of-posit-data-science-agents/</guid>
      <dc:creator>Joe Cheng</dc:creator><description><![CDATA[<p>For the last 18 months, many of us at Posit have been focused on building AI agents designed to help RStudio and Positron users get their work done faster and more effectively. And we truly believe that we&rsquo;ve succeeded! Our latest agent, <a href="https://posit-dev.github.io/assistant/" target="_blank" rel="noopener">Posit Assistant</a>, is extremely good and getting better with each passing week.</p>
<p>But Posit Assistant was not our first agent, and it&rsquo;s not likely to be our last. The proliferation of agents coming from Posit has started causing serious confusion. This post hopes to clear that up by providing a chronology of the agents we&rsquo;ve created, our rationale for their design and architecture, and what you need to know about them today (if anything).</p>
<h2 id="tldr">TL;DR
</h2>
<p>Posit Assistant is our newest, most powerful agent for data science. It builds on what we learned from creating our previous agents: Positron Assistant and Databot.</p>
<table>
  <thead>
      <tr>
          <th>Name</th>
          <th>Description</th>
          <th>Status</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong><a href="https://posit-dev.github.io/assistant/" target="_blank" rel="noopener">Posit Assistant</a></strong></td>
          <td>Coding agent available in RStudio and Positron (and more)</td>
          <td>Active</td>
      </tr>
      <tr>
          <td><strong><a href="https://positron.posit.co/assistant.html" target="_blank" rel="noopener">Positron Assistant</a></strong></td>
          <td>Positron&rsquo;s coding agent</td>
          <td>Superseded by Posit Assistant, still available until Q3 2026</td>
      </tr>
      <tr>
          <td><strong><a href="https://positron.posit.co/databot.html" target="_blank" rel="noopener">Databot</a></strong></td>
          <td>EDA agent in Positron</td>
          <td>Superseded by Posit Assistant</td>
      </tr>
  </tbody>
</table>
<div class="callout callout-note" role="note" aria-label="Note">
<div class="callout-header">
<span class="callout-title">What about Posit AI?</span>
</div>
<div class="callout-body">
<p><a href="https://posit.ai/" target="_blank" rel="noopener">Posit AI</a>, not to be confused with Posit Assistant or Positron Assistant, is a model subscription service that can be used to power Posit Assistant.</p>
</div>
</div>
<h2 id="positron-assistant">Positron Assistant
</h2>
<ul>
<li><strong>Strengths:</strong> Time to market; connects to a variety of providers; tight IDE integration</li>
<li><strong>Weaknesses:</strong> Legacy agent architecture; not available in RStudio IDE</li>
<li><strong>Status:</strong> Succeeded by Posit Assistant (as of June 2026)</li>
</ul>
<p>This was the first &ldquo;off the shelf&rdquo; data science agent we ever shipped, and is available exclusively in Positron. It was forked from GitHub Copilot, and like Copilot, you can ask it general coding questions, have it write code, and have it explain existing code to you.</p>
<p>While the <a href="https://positron.posit.co/assistant.html" target="_blank" rel="noopener">Positron Assistant</a> experience is largely the same as Copilot, there are two major differences. The first is that it can access Positron-specific features like running code in an interactive R or Python session, inspecting variables, and generating/inspecting plots. These features make it much more useful for data work.</p>
<p>The second major difference is the ability to connect to other LLM providers than the Copilot service, right out of the box. At this time, Positron Assistant can connect to Anthropic and OpenAI (with API key), AWS Bedrock, Snowflake Cortex, GitHub Copilot, Microsoft Foundry, Google Gemini, <a href="https://posit.ai/" target="_blank" rel="noopener">Posit AI</a>, and even custom (OpenAI-compatible) providers. When running in a properly configured Posit Workbench installation, these can often be seamlessly authenticated.</p>
<p>GitHub Copilot is tightly integrated with VS Code, and Positron Assistant is similarly well integrated with Positron. You can start an &ldquo;inline chat&rdquo; right at your editor cursor and ask Positron Assistant to fix or explain errors with one click. These were major benefits to starting with GitHub Copilot as a base.</p>
<p>Unfortunately, there are serious downsides to the GitHub Copilot heritage as well. The architecture and core APIs of Copilot were designed to be extended <a href="https://code.visualstudio.com/api/extension-guides/ai/chat" target="_blank" rel="noopener">in ways</a> that didn&rsquo;t end up being very popular, while tool calling felt grafted on and cache control nonexistent. The net effect was a lot of abstraction and complexity between the user prompt and what actually went into the LLM request, making it difficult for us to get the best performance out of the LLM. And users noticed this, saying that Positron Assistant was generally effective but also duller than other agents, including Databot and Claude Code, even when using the same underlying model.</p>
<p>Another big downside to GitHub Copilot was that its chat UI was more austere and harder to customize than what we could do on our own. Any changes we made were asking for technical debt, multiplied by the incredibly fast pace of upstream changes that we need to regularly merge.</p>
<p>Overall, the Positron Assistant project gave us what we needed at the time: a competitive, full-featured, IDE-integrated, agentic coding assistant. But for data work, we felt like we knew how to do better in a number of ways, including agent performance and UI/UX.</p>
<h2 id="databot">Databot
</h2>
<ul>
<li><strong>Strengths:</strong> Effective agent harness; well tailored to exploration tasks</li>
<li><strong>Weaknesses:</strong> Not general purpose enough; not available in RStudio IDE</li>
<li><strong>Status:</strong> Succeeded by Posit Assistant (as of June 2026)</li>
</ul>
<p>Databot was conceived in December 2024, in response to a specific question: what would happen if we took a frontier model (Claude 3.5 Sonnet), seeded it with simple instructions, and gave it unfettered access to the most powerful tool we could think of (a live R or Python session)?</p>
<p>At the time, it seemed hopelessly reckless to give so much power to an LLM, and we ran the first prototype with real trepidation! Fortunately, this approach is more effective and significantly less dangerous in practice than in theory. In a world before Claude Code, Databot felt like absolute magic. That magic was compounded by three decisions we made in an effort to make Databot excellent for exploratory data analysis (EDA):</p>
<ol>
<li>The ability not only to execute code but to see the results (including tables and plots)</li>
<li>Keeping the human in the feedback loop by only allowing so many tool calls before stopping to summarize progress and check in with the user</li>
<li>Always providing three to five suggestions of what to do next</li>
</ol>
<p>With a suitably capable LLM, this combination makes data exploration feel like flying.</p>
<p>It took us months to convince ourselves that a tool like this was safe enough to ship to users, and months more to turn the prototype into production-ready software. Since this felt like such an aggressive architecture, we constrained Databot to a narrow remit: it was only for interactively exploring data. After exploration was complete, it could also export a reproducible Quarto report that records both the findings and the methods, but that was pretty much it.</p>
<p>We officially launched Databot on August 28, 2025 with <a href="https://posit.co/blog/introducing-databot" target="_blank" rel="noopener">this blog post</a>. We were still so nervous about the dangers that we published a <a href="https://posit.co/blog/databot-is-not-a-flotation-device" target="_blank" rel="noopener">companion blog post</a> at the same time, telling users how <em>not</em> to use Databot. As models have gotten better, and tens of millions of users have grown accustomed to aggressively agentic AI tools, this second blog post remains technically accurate but perhaps reads a bit alarmist. In practice, agentic AI coding tools are not completely safe, but neither are they necessarily as dangerous as they may theoretically appear. Their usefulness across a wide variety of tasks helps balance this risk.</p>
<p>Databot&rsquo;s crucial weakness turned out not to be that it was too dangerous, but too narrowly scoped. EDA does not happen in a vacuum. It&rsquo;s often done in service of some larger project, and insights derived from EDA typically immediately lead to the creation of a reproducible cleaning script, interactive Shiny app, automated data transformation job, etc. As an EDA-focused agent harness, Databot did not have the tools needed to do those tasks well.</p>
<p>This constrained nature left users feeling frustrated. Databot could be so intelligent, so facile with data exploration, yet so suddenly clumsy the moment you asked it to edit a data cleaning script, and the distinction felt arbitrary. Even if you understood Databot&rsquo;s limitations, it still left you needing to manually perform a &ldquo;hand off&rdquo; between an EDA session in Databot and a Shiny app authoring session in Positron Assistant.</p>
<h3 id="why-did-we-make-two-agents-instead-of-one">Why did we make two agents instead of one?
</h3>
<p>Given the obvious downsides of creating two distinct agents for Positron instead of focusing on one — confusion, disjointed experience, duplication of effort — why did we do it?</p>
<p>First, as stated above, Databot felt not just risky but borderline reckless in how much power we were encouraging the agent to wield. Positron Assistant had very similar behavior to Copilot, so while not totally &ldquo;safe&rdquo;, it felt like a widely understood and accepted set of tradeoffs. It felt reasonable to have one conservative agent that we could tell everyone to use in Positron Assistant, and one aggressive agent for early adopters to play with in Databot.</p>
<p>Second, Positron Assistant seemed easier to build because we could start with so much existing functionality from Copilot. For Databot, we had to build everything from scratch: the agent harness, the chat UI, loading and saving past conversations, everything, and it was hard to know in the beginning how fast we could do it and how successful we would be. Again, it felt right to have one agent that we knew we could ship quickly, and one that might take longer but would potentially give us a better long-term platform.</p>
<p>These reasons made sense to us in early 2025, but they haven&rsquo;t stood the test of time. As Databot gestated, Claude Code showed the world that powerful AI agents had a usefulness-to-risk ratio that far, far exceeded most people&rsquo;s thresholds. And forking Copilot didn&rsquo;t give us nearly the boost in development speed we expected: while it did give Positron Assistant a huge head start, it also saddled Positron Assistant with many unnecessarily complex abstractions and introduced us to the very expensive recurring task of merging the torrent of upstream Copilot changes with our extensive modifications.</p>
<p>A final reason had to do not with risk but with specialization: Databot&rsquo;s <a href="https://posit.co/blog/introducing-databot" target="_blank" rel="noopener">WEAR loop</a> felt distinct from Positron Assistant&rsquo;s generic loop, and this felt like a pretty fundamental UX difference. Once we got serious about combining the two, it turned out not to be so fundamental after all, and for the most part we feel like our next agent succeeds at &ldquo;code-switching&rdquo; as needed.</p>
<h2 id="posit-assistant">Posit Assistant
</h2>
<ul>
<li><strong>Strengths:</strong> Effective agent harness; great for EDA, coding, and general-purpose agent tasks; integrated into both Positron and RStudio</li>
<li><strong>Weaknesses:</strong> Not designed for highly autonomous or highly parallel agentic work (if that&rsquo;s what you&rsquo;re into)</li>
<li><strong>Status:</strong> Now available in RStudio, in preview in Positron</li>
</ul>
<p>Posit Assistant is our newest data science agent, designed to build on what we liked most about each of the AI agents we&rsquo;ve built and used in the past:</p>
<ul>
<li>A simple agent harness with a small number of powerful and general tools</li>
<li>Prompting, tools, and UI/UX designed for the needs of data scientists</li>
<li>Full access to live R/Python sessions, including variables and plots</li>
<li>Integrated into our data science IDEs</li>
<li>A codebase designed for fast iteration</li>
<li>Extension points based on MCP and skills</li>
</ul>
<p>Crucially, we built Posit Assistant to live in both RStudio (since April 2026) and Positron (since June 2026). Not only did this finally bring an integrated agent to RStudio, where most R users still live today, but it also meant that every ounce of effort we put into improving Posit Assistant helps both RStudio and Positron users.</p>
<p>While Positron Assistant (the older one) grew out of Copilot, Posit Assistant (the new one) grew out of Databot. There was much we liked about Databot, and it didn&rsquo;t take much effort to expand its mission from the narrow task of exploratory data analysis to being a great general-purpose agent.</p>
<p>The big advantage of Positron Assistant in early 2025 turned out to be a big disadvantage by early 2026: being built on GitHub Copilot. While Copilot was arguably one of the two or three best coding agents for most of its existence, by the end of 2025 it was thoroughly outmatched by Claude Code.</p>
<p>At the risk of oversimplifying, the Copilot approach thinks the user should play a major role in deciding what context the underlying LLM gets to see. For example, Copilot and Positron Assistant have UI features that let you specify what files from your project you want to include with your next user prompt. You can also use <code>@-mentions</code> to bring in reference materials (for example, the Shiny VS Code extension lets you mention <code>@shiny</code> to bring in the Shiny docs).</p>
<p>In contrast, Claude Code&rsquo;s approach is to give an LLM access to powerful general-purpose abilities, like reading/writing/editing files and executing arbitrary bash commands, and then trusting the LLM to use them to gather the information it needs. This approach worked well when Claude Code was released in February 2025, and then dramatically improved over the course of 2025. The overall effect was that Claude Code felt significantly smarter and more engaged, even when using the same underlying language model.</p>
<p><strong>The highest compliment I can give to Posit Assistant is this: when exploring data, it feels a lot like Databot, and when coding, it feels a lot like Claude Code.</strong> And where Claude Code is a general-purpose coding agent, Posit Assistant is built specifically for people who work with data, with modes and skills for data tasks like data cleaning, Shiny app creation, and predictive modeling.</p>
<h3 id="why-did-we-call-it-posit-assistant-when-we-already-had-positron-assistant">Why did we call it &ldquo;Posit Assistant&rdquo; when we already had &ldquo;Positron Assistant&rdquo;!?
</h3>
<p>Maybe the most controversial thing about Posit Assistant is its name: Posit Assistant. First, it&rsquo;s quite dull. Second, why isn&rsquo;t it &ldquo;Posit Agent&rdquo;? Third, why would you give it almost-but-not-exactly the same name as the thing it&rsquo;s superseding?</p>
<p>These are all decisions that were made by me, Joe Cheng, and if you think they are bad decisions, then just know the Posit Assistant team agrees with you. (I stand by the name, but only just!)</p>
<p><strong>On dullness:</strong> A natively integrated AI chatbot for an IDE is table stakes, not a distinguishing feature. While we need a name today, I want users to eventually think of Posit Assistant as &ldquo;RStudio&rsquo;s chat pane&rdquo; or &ldquo;Positron&rsquo;s built-in AI&rdquo;, that is, for its identity to be subsumed into the host IDE. We believe most data practitioners will continue to use IDEs even as these models get more capable, and that the &ldquo;I&rdquo; in IDE is where we can offer real value. Instead of trying to build a new, distinct brand, I wanted there to barely be a brand at all.</p>
<p><strong>On &ldquo;Agent&rdquo;:</strong> The term &ldquo;agent&rdquo; is heavily overloaded these days. In the sense that it&rsquo;s been used so far in this article, it means <a href="https://simonwillison.net/2025/May/22/tools-in-a-loop/" target="_blank" rel="noopener">&ldquo;models using tools in a loop,&rdquo;</a> and Posit Assistant definitely fits that definition. But a lot of our customers understand the word to refer to &ldquo;models working highly autonomously for long periods of time&rdquo;, and we really wanted to avoid this connotation. Posit Assistant does have the ability to work autonomously if necessary and appropriate, but much of our harness encourages the model not to work autonomously in situations where it otherwise would.</p>
<p><strong>On Positron Assistant/Posit Assistant:</strong> This one is truly confusing, and the hardest one to defend. My defense is that the confusion is temporary. For most of Positron Assistant&rsquo;s short history, Posit Assistant didn&rsquo;t exist. For most of Posit Assistant&rsquo;s history, Positron Assistant will no longer exist. We&rsquo;re in a moment right now where we&rsquo;re mid-transition, so it&rsquo;s very confusing, but hopefully, that confusion is temporary.</p>
<p>I hope you will find this explanation to be satisfactory, and if not, at least now you know who to blame!</p>
]]></description>
      <enclosure url="https://opensource.posit.co/blog/2026-06-11_history-of-posit-data-science-agents/images/featured.png" length="248470" type="image/png" />
    </item>
    <item>
      <title>RStudio&#39;s Top Feature Requests ... In Positron</title>
      <link>https://opensource.posit.co/blog/2026-06-10_rstudios-top-features-in-positron/</link>
      <pubDate>Wed, 10 Jun 2026 00:00:00 +0000</pubDate>
      <guid>https://opensource.posit.co/blog/2026-06-10_rstudios-top-features-in-positron/</guid>
      <dc:creator>Jonathan McPherson</dc:creator><description><![CDATA[<p>One of the most fun aspects of developing software in the open is that you don&rsquo;t have to guess what your users want; you can just let them tell you. For over a decade, <a href="https://github.com/rstudio/rstudio/issues" target="_blank" rel="noopener">RStudio&rsquo;s issue tracker</a> has been public, available for anyone to report problems or request enhancements. And you, the community, have delivered, collectively filing thousands of bug reports and feature ideas.</p>
<p>One of the <em>least</em> fun aspects, though, is that you never have time to do most (or even <em>half</em>) of the great ideas that bubble up through the community. RStudio&rsquo;s issue tracker is full of worthy requests that have been unimplemented for years, often because they would require outsized changes to the core system&rsquo;s architecture or behavior.</p>
<p>When we set out to make Positron, RStudio&rsquo;s issue tracker became a gold mine of possibilities. Because Positron has a new architecture and is built on a different platform, many of the things that we&rsquo;d never been able to do in RStudio suddenly became possible.</p>
<p>Today, we take a look at 10 of the most upvoted RStudio feature requests of all time and how those features have been incorporated into Positron. Every one of these 10 requests is on the <a href="https://github.com/rstudio/rstudio/issues?q=is%3Aissue%20state%3Aopen%20sort%3Areactions-%2B1-desc" target="_blank" rel="noopener">front page of the most-upvoted RStudio issues</a> as of this writing.</p>
<h2 id="1-fully-configurable-pane-layouts">#1: Fully configurable pane layouts
</h2>
<blockquote>
<p>Most IDEs have a somewhat more flexible system which allows for arbitrary tab arrangement, often using mouse gestures to edit configuration on the fly.</p>
</blockquote>
<p>&ndash; #<a href="https://github.com/rstudio/rstudio/issues/2879" target="_blank" rel="noopener">2879</a></p>
<p>This has been one of RStudio&rsquo;s most-upvoted requests for years. In Positron, this feature is largely inherited from upstream Code OSS (the open source core of VS Code). It has a very flexible layout system which allows tabs to be rearranged, split, hidden, and more with natural dragging and mouse gestures, or via context menus. Try right-clicking on any tab or divider to show places you can move it:</p>
<img src="https://opensource.posit.co/blog/2026-06-10_rstudios-top-features-in-positron/01-pane-layout.png" height="1180" width="898" alt="Customizable pane layout in Positron" />
<p>Read more about it here:</p>
<p><a href="https://code.visualstudio.com/docs/configure/custom-layout" target="_blank" rel="noopener">VS Code Layout Configuration</a></p>
<p>Positron also ships with a variety of layout presets customized for specific tasks. Try the <em>View: Stacked Layout</em> command to make it look more like RStudio, or <em>View: Notebook Layout</em> to focus on a Quarto document or Jupyter Notebook.</p>
<h2 id="2-fixed-size-graphics-device">#2: Fixed size graphics device
</h2>
<blockquote>
<p>It would be excellent if one could set the RStudio graphics device to generate plots as they would be generated by a non-interactive device with fixed dimensions, so that one could do iterative plot-making within the IDE rather than call <code>png()</code>, look at the outputs in another program, and then fiddle with font scaling, etc. and repeat.</p>
</blockquote>
<p>&ndash; #<a href="https://github.com/rstudio/rstudio/issues/4422" target="_blank" rel="noopener">4422</a></p>
<p>RStudio always draws plots to fit the exact dimensions of its Plots pane, which can make it difficult to iterate on a plot you&rsquo;re preparing for publication.</p>
<p>When we added a Plots pane to Positron, we included a new tool that lets you indicate the dimensions at which you&rsquo;d like the plot to draw. You can have it fit to some common aspect ratios or &ndash; as in the RStudio feature request &ndash; specify the exact size at which you&rsquo;d like the plot to be drawn.</p>
<img src="https://opensource.posit.co/blog/2026-06-10_rstudios-top-features-in-positron/02-plot-size.png" height="1016" width="1026" alt="The Plots pane with a custom size dialog" />
<p>Positron&rsquo;s Plots pane also features a visual history browser, and it remembers the code used to create the plot so you can jump to it or re-run it.</p>
<h2 id="3-multi-line-statement-support-for-python-code">#3: Multi-line statement support for Python code
</h2>
<blockquote>
<p>It would be really great to have RStudio support of multi-line statements extended to Python code as well.</p>
</blockquote>
<p>&ndash; #<a href="https://github.com/rstudio/rstudio/issues/9014" target="_blank" rel="noopener">9014</a></p>
<p>One of RStudio&rsquo;s key R features is multi-line statement detection. When you tell RStudio to execute R code at your cursor location, it doesn&rsquo;t just send the current line of code; it gathers all the lines that are part of the statement and sends them together. RStudio doesn&rsquo;t know how to do this for Python, however.</p>
<p>In Positron, Python and R are peers, and we&rsquo;ve implemented multi-line statement detection for Python, too. Placing your cursor anywhere in a Python statement and invoking Run will execute the whole statement.</p>
<img src="https://opensource.posit.co/blog/2026-06-10_rstudios-top-features-in-positron/03-python-multi-line.png" height="1130" width="1280" alt="A multi-line Python statement" />
<h2 id="4-tree-view-for-browsing-files-and-directories">#4: Tree view for browsing files and directories
</h2>
<blockquote>
<p>Hi, is there any plan for browsing files and directories in tree view mode?</p>
</blockquote>
<p>&ndash; #<a href="https://github.com/rstudio/rstudio/issues/2552" target="_blank" rel="noopener">2552</a></p>
<p>RStudio&rsquo;s file browser lists only one directory at a time. Positron inherits Code OSS&rsquo;s tree-based explorer, which not only shows files and directories in context but even has <a href="https://code.visualstudio.com/docs/configure/themes#_file-icon-themes" target="_blank" rel="noopener">customizeable icons</a>!</p>
<img src="https://opensource.posit.co/blog/2026-06-10_rstudios-top-features-in-positron/04-file-explorer.png" alt="A tree-view file explorer" />
<h2 id="5-view-different-parts-of-a-source-file-at-the-same-time">#5: View different parts of a source file at the same time
</h2>
<blockquote>
<p>In TexShop there’s an option to split source which allows you to see (and edit) multiple areas in a document in one window so that you don’t need to keep scrolling up and down. It would be wonderful to have this as a functionality in the RStudio IDE editor</p>
</blockquote>
<p>&ndash; #<a href="https://github.com/rstudio/rstudio/issues/2129" target="_blank" rel="noopener">2129</a></p>
<p>In Positron, you can have several editors open at once against the same file. With a file open, try using the <em>Split Right</em> or <em>Split Down</em> commands, which will create a second tab for the file that can be scrolled independently. You can access this via the Command Palette, or via the editor action toolbar:</p>
<img src="https://opensource.posit.co/blog/2026-06-10_rstudios-top-features-in-positron/05-split-source-file.png" alt="A single R script open in two different editor tabs" />
<h2 id="6-add-builds-for-windows-on-arm">#6: Add builds for Windows on ARM
</h2>
<blockquote>
<p>RStudio is built for Windows only for Intel (x86) processors. However, there is now a version of Windows 11 available for ARM based processors.</p>
</blockquote>
<p>&ndash; #<a href="https://github.com/rstudio/rstudio/issues/11977" target="_blank" rel="noopener">11977</a></p>
<p>Microsoft has taken a page from Apple&rsquo;s playbook and started producing its own ARM-based hardware, such as the <a href="https://learn.microsoft.com/en-us/surface/surface-arm-faq" target="_blank" rel="noopener">ARM-based Surface devices</a>, and ARM-based Windows PCs are also now available from third-party manufacturers.</p>
<p>When we built Positron, we designed its native components to be easy to recompile against different CPU architectures. On Windows, Positron includes both x64 and arm64 R computation engines (kernels), so it can run natively with both x64 and arm64 versions of R. Its Python support, likewise, works great on ARM-based PCs.</p>
<img src="https://opensource.posit.co/blog/2026-06-10_rstudios-top-features-in-positron/06-windows-arm.png" alt="R aarch64 running natively in Positron" />
<h2 id="7-persistent-julia-engine">#7: Persistent Julia engine
</h2>
<blockquote>
<p>It would be useful if we can use Julia in RStudio seamlessly as well as Python.</p>
</blockquote>
<p>&ndash; #<a href="https://github.com/rstudio/rstudio/issues/1798" target="_blank" rel="noopener">1798</a></p>
<p>One of the main architectural goals of Positron was to make it adaptable to any language. It is a data science platform that supports &ldquo;language extensions&rdquo;. Positron&rsquo;s Python and R support subsystems are included in your Positron download, but are implemented as extensions.</p>
<p>While Posit doesn&rsquo;t currently have the expertise to develop well-rounded support for Julia, that hasn&rsquo;t stopped the community from creating a Positron extension that you can install to add Julia support. When installed, Julia becomes a peer of R and Python in Positron, and connects to Positron&rsquo;s Console, Variables, History, Packages, and Plots panes.</p>
<img src="https://opensource.posit.co/blog/2026-06-10_rstudios-top-features-in-positron/07-julia-support.png" alt="Positron running a Julia session" />
<p>Find it here: <a href="https://open-vsx.org/extension/ntluong95/positron-julia" target="_blank" rel="noopener">Julia for Positron</a></p>
<p>One of the questions we get a lot is why Positron itself isn&rsquo;t a VS Code extension. This is one of the reasons why: Positron isn&rsquo;t an extension; it <em>has</em> extensions.</p>
<h2 id="8-teachingpresentation-mode">#8: Teaching/presentation mode
</h2>
<blockquote>
<p>Quite a lot of people use RStudio hooked up to a projector to teach others. It would be great if those people had a key combo that could toggle between their current theme settings and some good defaults suitable for teaching/demos. &hellip; Perhaps the ultimate goal would be to have the teaching mode itself configurable.</p>
</blockquote>
<p>&ndash; #<a href="https://github.com/rstudio/rstudio/issues/4276" target="_blank" rel="noopener">4276</a></p>
<p>In Positron, it is possible to define different &ldquo;profiles&rdquo; that have different themes, font sizes, and other settings, and switch between them from the Command Palette, so it is possible set up a &ldquo;Presentation&rdquo; profile with a high-contrast theme and large text.</p>
<img src="https://opensource.posit.co/blog/2026-06-10_rstudios-top-features-in-positron/08-presentation-mode.png" alt="Positron's Profile editor showing a Presentation mode" />
<p>Profiles go beyond just layouts and themes. More documentation here:</p>
<p><a href="https://code.visualstudio.com/docs/configure/profiles" target="_blank" rel="noopener">https://code.visualstudio.com/docs/configure/profiles</a></p>
<h2 id="9-fix-columns-or-rows-when-scrolling-data">#9: Fix columns or rows when scrolling data
</h2>
<blockquote>
<p>The data viewer is one of the most used tool of data analysis in RStudio. However, a very useful feature is missing. In horizontal scrolling a big table with lots of column (it&rsquo;s very common to have more columns than one screen can fit), it will be very helpful to fix the row name column so that it&rsquo;s always visible(i.e. always keep it within screen with scrolling). &hellip; More generally, sometimes we also want to fix certain columns so that it remains visible when scrolled horizontally. The row name column is just a special case of this request.</p>
</blockquote>
<p>&ndash; #<a href="https://github.com/rstudio/rstudio/issues/3463" target="_blank" rel="noopener">3463</a></p>
<p>It&rsquo;s pretty common to have a column of data that represents a key value or name for the observation, or a value to which you wish to compare other values. In Positron, you can pin <em>any</em> column to fix it to the left, so that it is always visible as you scroll the other columns horizontally. Click on the column&rsquo;s action menu (vertical ellipsis) and choose <em>Pin Column</em>.</p>
<img src="https://opensource.posit.co/blog/2026-06-10_rstudios-top-features-in-positron/09-pin-column.png" alt="Positron's Data Explorer with a column pinned" />
<p>You can also pin rows! Right-click on the row and choose <em>Pin Row</em>.</p>
<h2 id="10-remove-all-objects-in-the-python-environment">#10: Remove all objects in the Python environment
</h2>
<blockquote>
<p>Normally, we can do it easily in R environment by just 1 click but Python doesn&rsquo;t support it.</p>
</blockquote>
<p>&ndash; #<a href="https://github.com/rstudio/rstudio/issues/8750" target="_blank" rel="noopener">8750</a></p>
<p>It&rsquo;s supported in Positron! With a Python session open in the Console, you can use the trash icon to clear all the objects from the session &ndash; in one click.</p>
<img src="https://opensource.posit.co/blog/2026-06-10_rstudios-top-features-in-positron/10-python-delete-all.png" alt="The Delete All Objects feature in a Python session" />
<h2 id="-and-a-lot-more">&hellip; And a lot more.
</h2>
<p>Some of our other favorite Positron-only features that don&rsquo;t have a top-upvoted RStudio issue attached:</p>
<ul>
<li><strong>Multiple R sessions</strong>: RStudio runs only one primary R session at a time; any concurrent work needs to be done in a non-interactive background job. Positron supports multiple concurrent interactive R sessions.</li>
<li><strong>Multiple R versions</strong>: RStudio can only work with one R version at a time, and you need to switch externally using a tool like <a href="https://github.com/r-lib/rig" target="_blank" rel="noopener">rig</a>. Positron will let you choose from any R installation on your system and can even associate specific R versions with specific projects.</li>
<li><strong>Crash recovery</strong>: RStudio crashes when R does, but in Positron all you&rsquo;ll lose is the R session itself, which just gets safely restarted.</li>
<li><strong>Remote sessions</strong>: Connect remotely to another computer over SSH and run R sessions inside it, or work with reproducible projects inside <a href="https://containers.dev/" target="_blank" rel="noopener">devcontainers</a>.</li>
</ul>
<p>See <a href="https://positron.posit.co/migrate-rstudio-compare.html" target="_blank" rel="noopener">Comparing RStudio and Positron Features</a> for more.</p>
<h2 id="what-about-rstudio">What about RStudio?
</h2>
<p>While Positron&rsquo;s design has made it more practical to make many of these advancements, the point of this post isn&rsquo;t that you should switch to Positron if you&rsquo;re happy in RStudio. In fact, features are also flowing in the other direction; many of Positron&rsquo;s features are making their way back into RStudio. For example, the latest release of RStudio has:</p>
<ul>
<li>a redesigned data viewer inspired by Positron&rsquo;s data viewer;</li>
<li>code formatting optionally <a href="https://posit-dev.github.io/air/editor-rstudio.html" target="_blank" rel="noopener">powered by air</a>, Positron&rsquo;s R formatter;</li>
<li>warning/error styling in the R console inspired by Positron&rsquo;s Console; and, of course</li>
<li><a href="https://posit-dev.github.io/assistant/" target="_blank" rel="noopener">Posit Assistant</a>, a platform-agnostic data analysis assistant that works in both IDEs.</li>
</ul>
<p>If you use RStudio and are interested in trying Positron, a good place to start is our <a href="https://positron.posit.co/migrate-rstudio.html" target="_blank" rel="noopener">Migrating from RStudio guide</a>.</p>
<p>Thank you for the creativity, ideas, and support you&rsquo;ve shown our IDEs over the years. Keep it coming!</p>
]]></description>
      <enclosure url="https://opensource.posit.co/blog/2026-06-10_rstudios-top-features-in-positron/header.png" length="111055" type="image/png" />
    </item>
    <item>
      <title>Posit Assistant is specialized for data work</title>
      <link>https://opensource.posit.co/blog/2026-06-08_comparing-posit-assistant-and-claude-code/</link>
      <pubDate>Mon, 08 Jun 2026 00:00:00 +0000</pubDate>
      <guid>https://opensource.posit.co/blog/2026-06-08_comparing-posit-assistant-and-claude-code/</guid>
      <dc:creator>Sara Altman</dc:creator><description><![CDATA[<p>We often get questions about how Posit Assistant compares to other coding agents like Claude Code. If you&rsquo;re already using Posit Assistant with a Claude model, are they really that different? At a high level, Claude Code is an extremely useful general-purpose coding agent, but <strong>we designed Posit Assistant specifically for people who work with data.</strong> This post covers three specific differences:</p>
<ol>
<li>Posit Assistant has out-of-the-box access to your R or Python session.</li>
<li>Posit Assistant can display plots in the chat, leading to easier interpretation for you and the agent.</li>
<li>Posit Assistant has specialized interaction modes, skills, and prompting for data analysis.</li>
</ol>
<p>You can also see a full demo of these features in this video:</p>
<div class="w-full aspect-video">
      <iframe
        src="https://www.youtube.com/embed/7GI6-4J0AXA"
        class="w-full h-full"
        
        frameborder="0"
        allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
        allowfullscreen></iframe>
    </div>
<div class="callout callout-tip" role="note" aria-label="Tip">
<div class="callout-header">
<span class="callout-title">What's the difference between Posit Assistant, Positron Assistant, and Posit AI?</span>
</div>
<div class="callout-body">
<p>This post talks about <a href="https://posit-dev.github.io/assistant/" target="_blank" rel="noopener">Posit Assistant</a>, Posit&rsquo;s latest coding agent available in RStudio and Positron. <a href="https://posit.co/products/ai" target="_blank" rel="noopener">Posit AI</a> is a model subscription service that can power Posit Assistant. <a href="https://positron.posit.co/assistant.html" target="_blank" rel="noopener">Posit<em>ron</em> Assistant</a> is a coding agent built into Positron. We plan for Posit Assistant to supersede Positron Assistant in Q3 2026.</p>
</div>
</div>
<h3 id="coding-agents-vs-chat-apps">Coding agents vs. chat apps
</h3>
<p>Claude Code and Posit Assistant are both <em>coding agents</em>. An <em>agent</em> is a <a href="https://simonwillison.net/2025/Sep/18/agents/" target="_blank" rel="noopener">&ldquo;model running tools in a loop,&rdquo;</a> meaning an agent can see your environment, take actions in that environment (e.g., run code), and iterate based on the outcome of those actions. These abilities make agents vastly more useful for coding than chat apps like ChatGPT.</p>
<p>Chat apps can&rsquo;t see your environment or take any actions in your environment. You have to copy-and-paste code into your session, run it yourself, and paste any errors or output back. This both makes the experience more frustrating and increases the probability of errors, because the model is writing code that operates on data and files that it doesn&rsquo;t actually have access to.</p>
<p>If you haven&rsquo;t tried a coding agent before and are still relying on chat apps for coding assistance, we recommend trying one out.</p>
<h2 id="built-in-access-to-your-rpython-session">Built-in access to your R/Python session
</h2>
<p><div class="not-prose"><figure>
    <img class="h-auto max-w-full rounded-lg"
      src="https://opensource.posit.co/blog/2026-06-08_comparing-posit-assistant-and-claude-code/images/pa-cc-session-access.png"
      alt="Screenshot comparing Posit Assistant and Claude Code&rsquo;s access to an R session. Posit Assistant can see session objects directly, while Claude Code must write and run scripts."  title="Posit Assistant has access to your R session out of the box." 
      loading="lazy"
    ><figcaption class="text-sm text-center text-gray-500">Posit Assistant has access to your R session out of the box.</figcaption>
  </figure></div>
</p>
<p><strong>The first major difference is that Posit Assistant has built-in access to your R or Python session.</strong> This means that Posit Assistant, without any additional configuration, can see your R or Python objects and run code directly in your active session.</p>
<p>Claude Code lacks this ability out of the box. It can write R code to files and run scripts, but it can&rsquo;t directly access data you&rsquo;ve loaded or run R code interactively.</p>
<p>If you ask a coding agent without session access to, for example, filter a tibble <code>df</code> you have in your environment, it will write R code to a script and then run that script with the <code>Rscript</code> command. This works, but has a couple of downsides:</p>
<ol>
<li>Before running the script, the agent can&rsquo;t actually tell that <code>df</code> exists. If it doesn&rsquo;t exist, it will have to wait for an error message from the script.</li>
<li>You might not want to create and run scripts for every bit of code.</li>
<li>It leaves you and the agent on different footing: you have direct access to your session, but the agent doesn&rsquo;t. This can make errors more likely and the general experience more frustrating.</li>
</ol>
<p>It is possible to give agents like Claude Code access to your R or Python session by using an MCP server. In the walkthrough video, we show how to do this using the <a href="https://github.com/posit-dev/btw" target="_blank" rel="noopener">btw package</a>, giving Claude Code the ability to access our R session and run R code directly.</p>
<p>Even with an MCP server, however, it may be difficult to see the code being run on your behalf. For some tasks, this might not matter, but if you&rsquo;re analyzing data it can be important to understand the analysis in detail. For data analysis tasks, Posit Assistant is designed for auditability and transparency. The code is visible in the tool call UI with syntax highlighting and styling, so you can easily audit what&rsquo;s happening to your data at each step.</p>
<p>See this comparison in the video: <a href="https://youtu.be/7GI6-4J0AXA?t=220" target="_blank" rel="noopener">Claude Code</a> | <a href="https://youtu.be/7GI6-4J0AXA?t=424" target="_blank" rel="noopener">Posit Assistant</a></p>
<h2 id="data-visualization">Data visualization
</h2>
<p><div class="not-prose"><figure>
    <img class="h-auto max-w-full rounded-lg"
      src="https://opensource.posit.co/blog/2026-06-08_comparing-posit-assistant-and-claude-code/images/pa-plots.png"
      alt="Screenshot of Posit Assistant in RStudio displaying a plot directly in the chat panel, as well as in the plots pane, alongside the code that generated it."  title="Posit Assistant displays plots directly in the chat, making them visible to both you and the model." 
      loading="lazy"
    ><figcaption class="text-sm text-center text-gray-500">Posit Assistant displays plots directly in the chat, making them visible to both you and the model.</figcaption>
  </figure></div>
</p>
<p>Claude Code can write code to create plots, but can&rsquo;t display them natively in the terminal and will need an alternative way to show them to you (e.g., opening a file in Preview). Even for the model itself to see the plot, it has to save the plot to a PNG file and then read that file.</p>
<p><strong>Because Posit Assistant runs code in your console, plots show up in the RStudio or Positron plot pane, as well as directly in the chat panel. This makes it easier for you to quickly inspect and analyze the plot and iterate if needed.</strong> Posit Assistant will also automatically see the plot image and typically interpret it or assess whether it is correct, which makes it very useful for EDA and data analysis.</p>
<p>This ability for you and the agent to both see the same plot is important for auditability. There&rsquo;s evidence that even the most advanced models <a href="https://simonpcouch.github.io/bluffbench/" target="_blank" rel="noopener">sometimes misinterpret plots that contradict their expectations</a>, and so having you and the agent looking at and interpreting the same plot makes it easier to catch mistakes.</p>
<p>See this comparison in the video: <a href="https://youtu.be/7GI6-4J0AXA?t=560" target="_blank" rel="noopener">Claude Code</a> | <a href="https://youtu.be/7GI6-4J0AXA?t=703" target="_blank" rel="noopener">Posit Assistant</a></p>
<h2 id="specialized-data-analysis-capabilities">Specialized data analysis capabilities
</h2>
<p>Session access and plotting affordances both directly support data analysis, but Posit Assistant also has interaction modes, prompting, and skills specifically designed for it.</p>
<p>Posit Assistant is both a general-purpose coding agent and a specialized data analysis agent, and it behaves slightly differently depending on your task. During data exploration, it only runs a few bits of code at a time, then summarizes what it found and suggests next steps. Because the purpose of data analysis, especially exploration, is for you to learn about the data, the entire process breaks down if the model runs ahead with analysis you can&rsquo;t keep up with. <strong>Posit Assistant&rsquo;s approach to co-analysis with the user is designed to support your understanding of the data rather than hinder it.</strong></p>
<figure>
<img src="https://opensource.posit.co/blog/2026-06-08_comparing-posit-assistant-and-claude-code/images/pa-data-analysis.gif" alt="GIF of Posit Assistant carrying out some data analysis on the likeliness words data. Posit Assistant does three rounds of tool calls, summarizes what it found, and provides some suggestions." width="600px" />
<figcaption>Posit Assistant explores data iteratively, running a few bits of code at a time before summarizing and suggesting next steps.</figcaption>
</figure>
<p>Claude Code excels at writing code, but is generally oriented toward writing that code to files, executing it, and reviewing it, rather than exploring data step-by-step with the user.</p>
<p><strong>Posit Assistant also has specialized prompting, skills, and tools that promote sound data analysis.</strong> These include skills for making Shiny apps and Quarto reports, a <a href="https://opensource.posit.co/blog/2026-05-08_ai-newsletter/" target="_blank" rel="noopener">data cleaning mode</a>, and prompting about rigorous statistical and modeling practices.</p>
<p><div class="not-prose"><figure>
    <img class="h-auto max-w-full rounded-lg"
      src="https://opensource.posit.co/blog/2026-06-08_comparing-posit-assistant-and-claude-code/images/data-cleaning-mode.png"
      alt="Screenshot of Posit Assistant&rsquo;s data cleaning mode, showing a tabbed interface with issues like &lsquo;Sparse early/late years&rsquo;. The assistant presents a table of row counts by year range, explains the imbalance, and offers actionable choices: keep all years, filter to 2011–2022, or filter to 2011–2019."  title="Posit Assistant in data cleaning mode, surfacing questions to the user." 
      loading="lazy"
    ><figcaption class="text-sm text-center text-gray-500">Posit Assistant in data cleaning mode, surfacing questions to the user.</figcaption>
  </figure></div>
</p>
<p>See this comparison in the video: <a href="https://youtu.be/7GI6-4J0AXA?t=990" target="_blank" rel="noopener">Claude Code</a> | <a href="https://youtu.be/7GI6-4J0AXA?t=1133" target="_blank" rel="noopener">Posit Assistant</a></p>
<h2 id="posit-assistant-is-designed-for-data-work">Posit Assistant is designed for data work
</h2>
<p>Claude Code, Codex, and other coding agents are very useful tools. They excel at general-purpose coding tasks, and you might want to use them for work that benefits from running multiple agents in parallel, or because you want to take advantage of your existing subscription plans.</p>
<p>However, we made Posit Assistant because we think the millions of data scientists, researchers, and analysts who rely on R and Python should have an excellent agent specifically designed for them and their needs. Those needs include data analysis and visualization, but also package development, Shiny app creation, and other coding-focused tasks in the data science ecosystem.</p>
<p>Learn more about Posit Assistant: <a href="https://posit-dev.github.io/assistant/" target="_blank" rel="noopener">https://posit-dev.github.io/assistant/</a>.</p>
]]></description>
      <enclosure url="https://opensource.posit.co/blog/2026-06-08_comparing-posit-assistant-and-claude-code/images/featured.png" length="134058" type="image/png" />
    </item>
    <item>
      <title>Positron June Release Highlights</title>
      <link>https://opensource.posit.co/blog/2026-06-08_positron-2026-06-release/</link>
      <pubDate>Mon, 08 Jun 2026 00:00:00 +0000</pubDate>
      <guid>https://opensource.posit.co/blog/2026-06-08_positron-2026-06-release/</guid>
      <dc:creator>Julia Silge</dc:creator><description><![CDATA[<div class="callout callout-note" role="note" aria-label="Note">
<div class="callout-header">
<span class="callout-title">Note</span>
</div>
<div class="callout-body">
<p><a href="https://positron.posit.co" target="_blank" rel="noopener">Positron</a> is Posit&rsquo;s new, next-generation IDE for data science. Positron is designed to be an extensible, polyglot tool for exploring data and reproducible authoring in Python, R, and more.</p>
</div>
</div>
<p>Welcome back to another edition of our monthly Positron updates! Each month we share highlights from our <a href="https://positron.posit.co/release-notes" target="_blank" rel="noopener">latest release</a> and useful resources.</p>
<h2 id="posit-assistant">Posit Assistant
</h2>
<p><a href="https://opensource.posit.co/blog/2026-05-11_positron-2026-05-release/" target="_blank" rel="noopener">Last release</a> we introduced <a href="https://pos.it/assistant" target="_blank" rel="noopener">Posit Assistant</a>, our unified, data-science-focused approach to AI assistance. This release continues that work, and we want to give you advance notice that the older Positron Assistant will be deprecated in the next release. If you are still using Positron Assistant, we encourage you to migrate to Posit Assistant now.</p>
<img src="https://opensource.posit.co/blog/2026-06-08_positron-2026-06-release/posit-assistant.gif" data-fig-align="center" data-fig-alt="Posit Assistant in Positron, Posit&#39;s unified data-science AI assistant, which is replacing the older Positron Assistant." />
<div class="callout callout-important" role="note" aria-label="Important">
<div class="callout-header">
<span class="callout-title">Important</span>
</div>
<div class="callout-body">
<p>During this migration period, you will likely want to keep both the old <a href="positron://settings/positron.assistant.enable"><code>positron.assistant.enable</code></a> setting and the new <a href="positron://settings/assistant.enabled"><code>assistant.enabled</code></a> set to true for best functionality, but do be aware that the older setting is in the process of being deprecated.</p>
</div>
</div>
<p>Posit Assistant supports the same broad set of providers as Positron Assistant, along with the Posit AI model provider and new experimental support for Google Vertex. Learn more about the differences between the new Posit Assistant and the older Positron Assistant:</p>
<div class="w-full aspect-video">
      <iframe
        src="https://www.youtube.com/embed/Y9P2nlFXKnQ"
        class="w-full h-full"
        
        frameborder="0"
        allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
        allowfullscreen></iframe>
    </div>
<p>You may also be interested in <a href="https://youtu.be/7GI6-4J0AXA" target="_blank" rel="noopener">comparing Posit Assistant and Claude Code</a>.</p>
<p>New in Posit Assistant this release, you can use the Posit AI model provider for Next Edit Suggestions, which propose your likely next change as you edit. You can also access the <strong>Configure Language Model Providers</strong> item in the accounts menu. You can <a href="https://github.com/posit-dev/assistant-feedback" target="_blank" rel="noopener">ask a question, report a bug, or request a new feature</a> for Posit Assistant separately from Positron now, but don&rsquo;t worry too much about where to send your feedback; we will help route it to the right place!</p>
<h2 id="packages-pane-improvements">Packages pane improvements
</h2>
<p>We introduced the <a href="https://positron.posit.co/packages-pane" target="_blank" rel="noopener">Packages</a> pane last release, and this release makes it more informative and flexible. A <strong>Show Help</strong> button and context menu entry on every package take you straight to its documentation in the <a href="https://positron.posit.co/help-pane" target="_blank" rel="noopener">Help pane</a>. You can now combine category filters as an intersection, so you can narrow the list to, for example, packages that are both attached and outdated. A new <strong>Item Size</strong> toggle lets you switch between a compact row view and a richer card view that surfaces package descriptions and other metadata.</p>
<img src="https://opensource.posit.co/blog/2026-06-08_positron-2026-06-release/packages-pane.gif" data-fig-align="center" data-fig-alt="The Positron Packages pane showing the attached category filter, the Item Size toggle switching between compact rows and a card view with package descriptions, and how to update a package." />
<p>For R users, the new <a href="positron://settings/packages.r.installer"><code>packages.r.installer</code></a> setting controls whether installs, updates, and removals use pak, base R, or an automatic choice. The setting that controls the pane has been renamed to <a href="positron://settings/packages.enabled"><code>packages.enabled</code></a>; the previous <code>positron.packages.enable</code> setting is deprecated but still honored.</p>
<h2 id="inline-output-for-quarto">Inline output for Quarto
</h2>
<p><a href="https://positron.posit.co/quarto-inline-output" target="_blank" rel="noopener">Inline output for <code>.qmd</code> documents</a> was one of Positron&rsquo;s most-requested features ever, and it is now out of preview. A new toolbar button in Quarto documents gives you one-click access to running cells, managing inline output, and showing the console.</p>
<img src="https://opensource.posit.co/blog/2026-06-08_positron-2026-06-release/inline-output.gif" data-fig-align="center" data-fig-alt="A Quarto document in Positron showing inline output beneath an executed code cell, with the new toolbar button for managing output." />
<p>Inserting code into a Quarto document from the History pane now automatically wraps it in cell markup when you drop it into a prose region. You can opt into inline output with the <a href="positron://settings/positron.quarto.inlineOutput.enabled"><code>positron.quarto.inlineOutput.enabled</code></a> setting.</p>
<h2 id="faster-startup">Faster startup
</h2>
<p>We continue to invest in performance improvements, and Positron starts up even faster in this release. Positron now caches previously discovered system interpreters, dramatically speeding up startup in new folders and projects; you can control this with the new <a href="positron://settings/interpreters.discoveryCache.enabled"><code>interpreters.discoveryCache.enabled</code></a> setting. On Windows, we fixed multi-minute startup delays by skipping slow <code>PATH</code> discovery by default, governed by the new <a href="positron://settings/positron.r.interpreters.pathDiscoveryMode"><code>positron.r.interpreters.pathDiscoveryMode</code></a> setting. We also fixed an occasional hang at &ldquo;Preparing&rdquo; when starting Positron after updating to a new version.</p>
<h2 id="a-more-customizable-interface">A more customizable interface
</h2>
<p>This release gives you finer control over Positron&rsquo;s chrome so you can tailor the interface to your workflow. Several layout settings that previously weren&rsquo;t always honored now work as expected:</p>
<ul>
<li><a href="positron://settings/workbench.topActionBar.visible"><code>workbench.topActionBar.visible</code></a> reliably hides and shows the top action bar at runtime</li>
<li><a href="positron://settings/workbench.secondarySideBar.defaultVisibility"><code>workbench.secondarySideBar.defaultVisibility</code></a> is respected when you set it explicitly</li>
<li><a href="positron://settings/workbench.secondarySideBar.showLabel"><code>workbench.secondarySideBar.showLabel</code></a> lets Secondary Side Bar items render as compact icons</li>
</ul>
<p>You can also enable <a href="positron://settings/window.commandCenter"><code>window.commandCenter</code></a> to bring the command center into the title bar. Learn more about some of our own team members&rsquo; favorite customizations:</p>
<div class="w-full aspect-video">
      <iframe
        src="https://www.youtube.com/embed/QIYyeuZ_ISY"
        class="w-full h-full"
        
        frameborder="0"
        allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
        allowfullscreen></iframe>
    </div>
<h2 id="r-language-intelligence">R language intelligence
</h2>
<p>Positron&rsquo;s R language support now understands symbols within a file. You can rename a local symbol and have every use of it updated at once, and Go to Definition and Find References now work for local symbols, so you can jump to where a variable is defined (such as via <code>&lt;-</code> or a function parameter) or see everywhere it is used within a function. Renaming symbols across files is not yet supported, but it is coming soon.</p>
<h2 id="whats-coming-next">What&rsquo;s coming next
</h2>
<ul>
<li>We have some exciting milestones planned for our July release! The <a href="https://positron.posit.co/positron-notebook-editor.html" target="_blank" rel="noopener">new notebook editor for <code>.ipynb</code> files</a>, the <a href="https://positron.posit.co/packages-pane.html" target="_blank" rel="noopener">Packages pane</a>, and <a href="https://pos.it/assistant" target="_blank" rel="noopener">Posit Assistant</a> will all come out of preview to general availability. We are thrilled for these features to be ready for your production workflows, and we hope you will give them a try and <a href="https://github.com/posit-dev/positron/discussions" target="_blank" rel="noopener">share your feedback</a>.</li>
<li>We&rsquo;re still prototyping first-class SQL editing and execution in Positron, including support for visualizations with <a href="https://opensource.posit.co/blog/2026-04-20_ggsql_alpha_release/" target="_blank" rel="noopener">ggsql</a>. We&rsquo;ll share more as this takes shape, and in the meantime, <a href="https://github.com/posit-dev/positron/issues/7233" target="_blank" rel="noopener">let us know</a> your thoughts and current pain points!</li>
<li>If you will be at the Databricks Data + AI Summit next week, <a href="https://posit.co/events/databricks-data-ai-summit-2026" target="_blank" rel="noopener">join us</a> to learn about using Positron and Workbench within the Databricks platform.</li>
<li>We are looking forward to posit::conf(2026) in September, where our team will have several sessions on Positron. The <a href="https://posit.co/blog/posit-conf-2026-agenda-breakdown" target="_blank" rel="noopener">conference program</a> was released last week and we are pretty excited about everything happening! <a href="https://conf.posit.co/2026/" target="_blank" rel="noopener">Register now</a> to join us in person in Houston or virtually from anywhere in the world.</li>
</ul>
<div class="callout callout-tip" role="note" aria-label="Tip">
<div class="callout-header">
<span class="callout-title">Tip</span>
</div>
<div class="callout-body">
<p><a href="https://positron.posit.co/download" target="_blank" rel="noopener">Download Positron</a> to try out the new features and improvements in this release!</p>
</div>
</div>
]]></description>
      <enclosure url="https://opensource.posit.co/blog/2026-06-08_positron-2026-06-release/featured.png" length="73388" type="image/png" />
    </item>
    <item>
      <title>Libraries for your Python Polars workflows</title>
      <link>https://opensource.posit.co/blog/2026-06-04_libraries-for-python-polars/</link>
      <pubDate>Thu, 04 Jun 2026 00:00:00 +0000</pubDate>
      <guid>https://opensource.posit.co/blog/2026-06-04_libraries-for-python-polars/</guid>
      <dc:creator>Isabella Velásquez</dc:creator><description><![CDATA[<script src="https://cdn.jsdelivr.net/npm/requirejs@2.3.6/require.min.js" integrity="sha384-c9c+LnTbwQ3aujuU7ULEPVvgLs+Fn6fJUvIGTsuu1ZcCf11fiEubah0ttpca4ntM sha384-6V1/AdqZRWk1KAlWbKBlGhN7VG4iE/yAZcO6NZPMF8od0vukrvr0tg4qY6NSrItx" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js" integrity="sha384-ZvpUoO/+PpLXR1lu4jmpXWu80pZlYUAfxl5NsBMWOEPSjUn/6Z/hRTt8+pR6L4N2" crossorigin="anonymous" data-relocate-top="true"></script>
<script type="application/javascript">define('jquery', [],function() {return window.jQuery;})</script>
<p>We (Emil Hvitfeldt, Jeroen Janssens, Michael Chow, and I) just got back from PyCon US 2026, and there was <strong>so much buzz</strong> around <a href="https://pola.rs/" target="_blank" rel="noopener">Polars</a>. What is Polars? Why should I switch to Polars? <em>How</em> do I switch to Polars?</p>
<p>I mean, just check out the line for the book signing of Jeroen&rsquo;s book, <a href="https://polarsguide.com/" target="_blank" rel="noopener">Python Polars: The Definitive Guide</a>.</p>
<img src="https://opensource.posit.co/blog/2026-06-04_libraries-for-python-polars/images/book-signing.jpeg" data-fig-alt="A long line of conference attendees waiting at Jeroen&#39;s book signing booth in PyCon US&#39; large convention hall" />
<p>If Polars is new to you, it is a library for efficient data manipulation in Python. It&rsquo;s built on Rust, so it&rsquo;s super fast. And a lot of people (including <a href="https://polarsguide.com/praise/" target="_blank" rel="noopener">the creator of Pandas</a>!) like the intuitive way you write Polars code. However, if you work in Python, you might know that different DataFrames have different requirements, so you want to make sure to use libraries that support Polars (I admit, I come from the R world, and this was mind blowing to me).</p>
<p>And, we&rsquo;re happy to say that we (Posit) have excellent Polars support across our Python libraries for every stage of the data science workflow! In this post, we&rsquo;ll walk through four:</p>
<ul>
<li><a href="https://posit-dev.github.io/pointblank/" target="_blank" rel="noopener"><strong>pointblank</strong></a> for data validation and quality checks</li>
<li><a href="https://posit-dev.github.io/great-tables/articles/intro.html" target="_blank" rel="noopener"><strong>Great Tables</strong></a> for creating publication-quality tables</li>
<li><a href="https://plotnine.org/" target="_blank" rel="noopener"><strong>plotnine</strong></a> for ggplot2-style visualizations</li>
<li><a href="https://mlverse.github.io/mall/" target="_blank" rel="noopener"><strong>mall</strong></a> for LLM-powered data analysis</li>
</ul>
<p>Let&rsquo;s check them out!</p>
<h2 id="setup">Setup
</h2>
<p>First, let&rsquo;s install the libraries:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">pip</span> <span class="n">install</span> <span class="n">polars</span> <span class="n">great_tables</span> <span class="n">pointblank</span> <span class="n">plotnine</span> <span class="n">mlverse</span><span class="o">-</span><span class="n">mall</span></span></span></code></pre></div></div>
<p>Now, import what we need:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">polars</span> <span class="k">as</span> <span class="nn">pl</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">great_tables</span> <span class="kn">import</span> <span class="n">GT</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">pointblank</span> <span class="k">as</span> <span class="nn">pb</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">plotnine</span> <span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">ggplot</span><span class="p">,</span> <span class="n">aes</span><span class="p">,</span> <span class="n">geom_point</span><span class="p">,</span> <span class="n">geom_line</span><span class="p">,</span> <span class="n">geom_bar</span><span class="p">,</span> <span class="n">geom_text</span><span class="p">,</span> <span class="n">geom_col</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">labs</span><span class="p">,</span> <span class="n">theme_minimal</span><span class="p">,</span> <span class="n">theme</span><span class="p">,</span> <span class="n">element_text</span><span class="p">,</span> <span class="n">scale_fill_manual</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">scale_y_continuous</span><span class="p">,</span> <span class="n">element_rect</span><span class="p">,</span> <span class="n">element_blank</span><span class="p">,</span> <span class="n">element_line</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">scale_color_manual</span><span class="p">,</span> <span class="n">position_dodge</span><span class="p">,</span> <span class="n">annotate</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">mall</span></span></span></code></pre></div></div>
<h2 id="the-dataset">The dataset
</h2>
<p>We&rsquo;ll demonstrate these tools using <code>sales_data</code>, a sample sales dataset:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">sales_data</span> <span class="o">=</span> <span class="n">pl</span><span class="o">.</span><span class="n">DataFrame</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;date&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;2026-01-15&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;2026-01-16&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;2026-01-17&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;2026-01-18&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;2026-01-19&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;2026-01-20&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;2026-01-21&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">],</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;region&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;North&#34;</span><span class="p">,</span> <span class="s2">&#34;South&#34;</span><span class="p">,</span> <span class="s2">&#34;North&#34;</span><span class="p">,</span> <span class="s2">&#34;West&#34;</span><span class="p">,</span> <span class="s2">&#34;South&#34;</span><span class="p">,</span> <span class="s2">&#34;North&#34;</span><span class="p">,</span> <span class="s2">&#34;West&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;product&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;Widget A&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;Widget B&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;Widget A&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;Widget C&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;Widget B&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;Widget A&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;Widget C&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">],</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;sales&#34;</span><span class="p">:</span> <span class="p">[</span><span class="mi">1200</span><span class="p">,</span> <span class="mi">1800</span><span class="p">,</span> <span class="mi">1500</span><span class="p">,</span> <span class="mi">2100</span><span class="p">,</span> <span class="mi">1650</span><span class="p">,</span> <span class="mi">1900</span><span class="p">,</span> <span class="mi">2300</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;units_sold&#34;</span><span class="p">:</span> <span class="p">[</span><span class="mi">24</span><span class="p">,</span> <span class="mi">36</span><span class="p">,</span> <span class="mi">30</span><span class="p">,</span> <span class="mi">42</span><span class="p">,</span> <span class="mi">33</span><span class="p">,</span> <span class="mi">38</span><span class="p">,</span> <span class="mi">46</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;customer_rating&#34;</span><span class="p">:</span> <span class="p">[</span><span class="mf">4.5</span><span class="p">,</span> <span class="mf">4.8</span><span class="p">,</span> <span class="mf">4.6</span><span class="p">,</span> <span class="mf">4.9</span><span class="p">,</span> <span class="mf">4.7</span><span class="p">,</span> <span class="mf">4.8</span><span class="p">,</span> <span class="mf">4.9</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span><span class="o">.</span><span class="n">with_columns</span><span class="p">(</span><span class="n">pl</span><span class="o">.</span><span class="n">col</span><span class="p">(</span><span class="s2">&#34;date&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">str</span><span class="o">.</span><span class="n">strptime</span><span class="p">(</span><span class="n">pl</span><span class="o">.</span><span class="n">Date</span><span class="p">,</span> <span class="s2">&#34;%Y-%m-</span><span class="si">%d</span><span class="s2">&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">sales_data</span><span class="p">)</span></span></span></code></pre></div></div>
<pre><code>shape: (7, 6)
┌────────────┬────────┬──────────┬───────┬────────────┬─────────────────┐
│ date       ┆ region ┆ product  ┆ sales ┆ units_sold ┆ customer_rating │
│ ---        ┆ ---    ┆ ---      ┆ ---   ┆ ---        ┆ ---             │
│ date       ┆ str    ┆ str      ┆ i64   ┆ i64        ┆ f64             │
╞════════════╪════════╪══════════╪═══════╪════════════╪═════════════════╡
│ 2026-01-15 ┆ North  ┆ Widget A ┆ 1200  ┆ 24         ┆ 4.5             │
│ 2026-01-16 ┆ South  ┆ Widget B ┆ 1800  ┆ 36         ┆ 4.8             │
│ 2026-01-17 ┆ North  ┆ Widget A ┆ 1500  ┆ 30         ┆ 4.6             │
│ 2026-01-18 ┆ West   ┆ Widget C ┆ 2100  ┆ 42         ┆ 4.9             │
│ 2026-01-19 ┆ South  ┆ Widget B ┆ 1650  ┆ 33         ┆ 4.7             │
│ 2026-01-20 ┆ North  ┆ Widget A ┆ 1900  ┆ 38         ┆ 4.8             │
│ 2026-01-21 ┆ West   ┆ Widget C ┆ 2300  ┆ 46         ┆ 4.9             │
└────────────┴────────┴──────────┴───────┴────────────┴─────────────────┘
</code></pre>
<p>We can confirm that it is, indeed, a Polars DataFrame!</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="nb">isinstance</span><span class="p">(</span><span class="n">sales_data</span><span class="p">,</span> <span class="n">pl</span><span class="o">.</span><span class="n">DataFrame</span><span class="p">)</span></span></span></code></pre></div></div>
<pre><code>True
</code></pre>
<h2 id="data-validation-with-pointblank">Data validation with pointblank
</h2>
<p>Let&rsquo;s now validate our data quality using <a href="https://posit-dev.github.io/pointblank/" target="_blank" rel="noopener"><strong>pointblank</strong></a>:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">agent</span> <span class="o">=</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">pb</span><span class="o">.</span><span class="n">Validate</span><span class="p">(</span><span class="n">sales_data</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">.</span><span class="n">col_vals_not_null</span><span class="p">(</span><span class="n">columns</span><span class="o">=</span><span class="s2">&#34;date&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">.</span><span class="n">col_vals_between</span><span class="p">(</span><span class="n">columns</span><span class="o">=</span><span class="s2">&#34;sales&#34;</span><span class="p">,</span> <span class="n">left</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">right</span><span class="o">=</span><span class="mi">10000</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">.</span><span class="n">col_vals_between</span><span class="p">(</span><span class="n">columns</span><span class="o">=</span><span class="s2">&#34;customer_rating&#34;</span><span class="p">,</span> <span class="n">left</span><span class="o">=</span><span class="mf">1.0</span><span class="p">,</span> <span class="n">right</span><span class="o">=</span><span class="mf">5.0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">.</span><span class="n">col_vals_in_set</span><span class="p">(</span><span class="n">columns</span><span class="o">=</span><span class="s2">&#34;region&#34;</span><span class="p">,</span> <span class="nb">set</span><span class="o">=</span><span class="p">[</span><span class="s2">&#34;North&#34;</span><span class="p">,</span> <span class="s2">&#34;South&#34;</span><span class="p">,</span> <span class="s2">&#34;East&#34;</span><span class="p">,</span> <span class="s2">&#34;West&#34;</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">    <span class="o">.</span><span class="n">interrogate</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">agent</span></span></span></code></pre></div></div>
<div id="pb_tbl" style="padding-left:0px;padding-right:0px;padding-top:10px;padding-bottom:10px;overflow-x:auto;overflow-y:auto;width:auto;height:auto;">
<style>
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono&display=swap');
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Sans&display=swap');
#pb_tbl table {
          font-family: 'IBM Plex Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Helvetica Neue', 'Fira Sans', 'Droid Sans', Arial, sans-serif;
          -webkit-font-smoothing: antialiased;
          -moz-osx-font-smoothing: grayscale;
        }
#pb_tbl thead, tbody, tfoot, tr, td, th { border-style: none; }
 tr { background-color: transparent; }
#pb_tbl p { margin: 0; padding: 0; }
 #pb_tbl .gt_table { display: table; border-collapse: collapse; line-height: normal; margin-left: auto; margin-right: auto; color: #333333; font-size: 90%; font-weight: normal; font-style: normal; background-color: #FFFFFF; width: auto; border-top-style: solid; border-top-width: 2px; border-top-color: #A8A8A8; border-right-style: none; border-right-width: 2px; border-right-color: #D3D3D3; border-bottom-style: solid; border-bottom-width: 2px; border-bottom-color: #A8A8A8; border-left-style: none; border-left-width: 2px; border-left-color: #D3D3D3; }
 #pb_tbl .gt_caption { padding-top: 4px; padding-bottom: 4px; }
 #pb_tbl .gt_title { color: #333333; font-size: 125%; font-weight: initial; padding-top: 4px; padding-bottom: 4px; padding-left: 5px; padding-right: 5px; border-bottom-color: #FFFFFF; border-bottom-width: 0; }
 #pb_tbl .gt_subtitle { color: #333333; font-size: 85%; font-weight: initial; padding-top: 3px; padding-bottom: 5px; padding-left: 5px; padding-right: 5px; border-top-color: #FFFFFF; border-top-width: 0; }
 #pb_tbl .gt_heading { background-color: #FFFFFF; text-align: left; border-bottom-color: #FFFFFF; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; }
 #pb_tbl .gt_bottom_border { border-bottom-style: solid; border-bottom-width: 2px; border-bottom-color: #D3D3D3; }
 #pb_tbl .gt_col_headings { border-top-style: solid; border-top-width: 2px; border-top-color: #D3D3D3; border-bottom-style: solid; border-bottom-width: 2px; border-bottom-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; }
 #pb_tbl .gt_col_heading { color: #333333; background-color: #FFFFFF; font-size: 100%; font-weight: normal; text-transform: inherit; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: bottom; padding-top: 5px; padding-bottom: 5px; padding-left: 5px; padding-right: 5px; overflow-x: hidden; }
 #pb_tbl .gt_column_spanner_outer { color: #333333; background-color: #FFFFFF; font-size: 100%; font-weight: normal; text-transform: inherit; padding-top: 0; padding-bottom: 0; padding-left: 4px; padding-right: 4px; }
 #pb_tbl .gt_column_spanner_outer:first-child { padding-left: 0; }
 #pb_tbl .gt_column_spanner_outer:last-child { padding-right: 0; }
 #pb_tbl .gt_column_spanner { border-bottom-style: solid; border-bottom-width: 2px; border-bottom-color: #D3D3D3; vertical-align: bottom; padding-top: 5px; padding-bottom: 5px; overflow-x: hidden; display: inline-block; width: 100%; }
 #pb_tbl .gt_spanner_row { border-bottom-style: hidden; }
 #pb_tbl .gt_group_heading { padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; color: #333333; background-color: #FFFFFF; font-size: 100%; font-weight: initial; text-transform: inherit; border-top-style: solid; border-top-width: 2px; border-top-color: #D3D3D3; border-bottom-style: solid; border-bottom-width: 2px; border-bottom-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; text-align: left; }
 #pb_tbl .gt_empty_group_heading { padding: 0.5px; color: #333333; background-color: #FFFFFF; font-size: 100%; font-weight: initial; border-top-style: solid; border-top-width: 2px; border-top-color: #D3D3D3; border-bottom-style: solid; border-bottom-width: 2px; border-bottom-color: #D3D3D3; vertical-align: middle; }
 #pb_tbl .gt_from_md> :first-child { margin-top: 0; }
 #pb_tbl .gt_from_md> :last-child { margin-bottom: 0; }
 #pb_tbl .gt_row { padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; }
 #pb_tbl .gt_stub { color: #333333; background-color: #FFFFFF; font-size: 100%; font-weight: initial; text-transform: inherit; border-right-style: solid; border-right-width: 2px; border-right-color: #D3D3D3; padding-left: 5px; padding-right: 5px; }
 #pb_tbl .gt_stub_row_group { color: #333333; background-color: #FFFFFF; font-size: 100%; font-weight: initial; text-transform: inherit; border-right-style: solid; border-right-width: 2px; border-right-color: #D3D3D3; padding-left: 5px; padding-right: 5px; vertical-align: top; }
 #pb_tbl .gt_row_group_first td { border-top-width: 2px; }
 #pb_tbl .gt_row_group_first th { border-top-width: 2px; }
 #pb_tbl .gt_striped { color: #333333; background-color: #F4F4F4; }
 #pb_tbl .gt_table_body { border-top-style: solid; border-top-width: 2px; border-top-color: #D3D3D3; border-bottom-style: solid; border-bottom-width: 2px; border-bottom-color: #D3D3D3; }
 #pb_tbl .gt_grand_summary_row { color: #333333; background-color: #FFFFFF; text-transform: inherit; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; }
 #pb_tbl .gt_first_grand_summary_row_bottom { border-top-style: double; border-top-width: 6px; border-top-color: #D3D3D3; }
 #pb_tbl .gt_last_grand_summary_row_top { border-bottom-style: double; border-bottom-width: 6px; border-bottom-color: #D3D3D3; }
 #pb_tbl .gt_sourcenotes { color: #333333; background-color: #FFFFFF; border-bottom-style: none; border-bottom-width: 2px; border-bottom-color: #D3D3D3; border-left-style: none; border-left-width: 2px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 2px; border-right-color: #D3D3D3; }
 #pb_tbl .gt_sourcenote { font-size: 90%; padding-top: 4px; padding-bottom: 4px; padding-left: 5px; padding-right: 5px; text-align: left; }
 #pb_tbl .gt_left { text-align: left; }
 #pb_tbl .gt_center { text-align: center; }
 #pb_tbl .gt_right { text-align: right; font-variant-numeric: tabular-nums; }
 #pb_tbl .gt_font_normal { font-weight: normal; }
 #pb_tbl .gt_font_bold { font-weight: bold; }
 #pb_tbl .gt_font_italic { font-style: italic; }
 #pb_tbl .gt_super { font-size: 65%; }
 #pb_tbl .gt_footnote_marks { font-size: 75%; vertical-align: 0.4em; position: initial; }
 #pb_tbl .gt_asterisk { font-size: 100%; vertical-align: 0; }
</style>
<table style="table-layout: fixed;; width: 0px" class="gt_table" data-quarto-disable-processing="true" data-quarto-bootstrap="false">
<colgroup>
  <col style="width:4px;"/>
  <col style="width:35px;"/>
  <col style="width:190px;"/>
  <col style="width:120px;"/>
  <col style="width:120px;"/>
  <col style="width:50px;"/>
  <col style="width:50px;"/>
  <col style="width:60px;"/>
  <col style="width:60px;"/>
  <col style="width:60px;"/>
  <col style="width:30px;"/>
  <col style="width:30px;"/>
  <col style="width:30px;"/>
  <col style="width:65px;"/>
</colgroup>
<thead>
  <tr class="gt_heading">
    <td colspan="14" class="gt_heading gt_title gt_font_normal" style="color: #444444;font-size: 28px;text-align: left;font-weight: bold; text-align: left;">Pointblank Validation</td>
  </tr>
  <tr class="gt_heading">
    <td colspan="14" class="gt_heading gt_subtitle gt_font_normal gt_bottom_border" style="text-align: left;"><div><span style='text-decoration-style: solid; text-decoration-color: #ADD8E6; text-decoration-line: underline; text-underline-position: under; color: #333333; font-variant-numeric: tabular-nums; padding-left: 4px; margin-right: 5px; padding-right: 2px;'>2026-06-04|17:00:11</span><div style="padding-top: 10px; padding-bottom: 5px;"><span style='background-color: #0075FF; color: #FFFFFF; padding: 0.5em 0.5em; position: inherit; text-transform: uppercase; margin: 5px 10px 5px 0px; border: solid 1px #0075FF; font-weight: bold; padding: 2px 10px 2px 10px; font-size: 10px;'>Polars</span></div></div></td>
  </tr>
<tr class="gt_col_headings">
  <th class="gt_col_heading gt_columns_bottom_border gt_left" rowspan="1" colspan="1" style="color: #666666;font-weight: bold;" scope="col" id="pb_tbl-status_color"></th>
  <th class="gt_col_heading gt_columns_bottom_border gt_right" rowspan="1" colspan="1" style="color: #666666;font-weight: bold;" scope="col" id="pb_tbl-i"></th>
  <th class="gt_col_heading gt_columns_bottom_border gt_left" rowspan="1" colspan="1" style="color: #666666;font-weight: bold;" scope="col" id="pb_tbl-type_upd">STEP</th>
  <th class="gt_col_heading gt_columns_bottom_border gt_left" rowspan="1" colspan="1" style="color: #666666;font-weight: bold;" scope="col" id="pb_tbl-columns_upd">COLUMNS</th>
  <th class="gt_col_heading gt_columns_bottom_border gt_left" rowspan="1" colspan="1" style="color: #666666;font-weight: bold;" scope="col" id="pb_tbl-values_upd">VALUES</th>
  <th class="gt_col_heading gt_columns_bottom_border gt_center" rowspan="1" colspan="1" style="color: #666666;font-weight: bold;" scope="col" id="pb_tbl-tbl">TBL</th>
  <th class="gt_col_heading gt_columns_bottom_border gt_center" rowspan="1" colspan="1" style="color: #666666;font-weight: bold;" scope="col" id="pb_tbl-eval">EVAL</th>
  <th class="gt_col_heading gt_columns_bottom_border gt_right" rowspan="1" colspan="1" style="color: #666666;font-weight: bold;" scope="col" id="pb_tbl-test_units">UNITS</th>
  <th class="gt_col_heading gt_columns_bottom_border gt_right" rowspan="1" colspan="1" style="color: #666666;font-weight: bold;" scope="col" id="pb_tbl-pass">PASS</th>
  <th class="gt_col_heading gt_columns_bottom_border gt_right" rowspan="1" colspan="1" style="color: #666666;font-weight: bold;" scope="col" id="pb_tbl-fail">FAIL</th>
  <th class="gt_col_heading gt_columns_bottom_border gt_center" rowspan="1" colspan="1" style="color: #666666;font-weight: bold;" scope="col" id="pb_tbl-w_upd">W</th>
  <th class="gt_col_heading gt_columns_bottom_border gt_center" rowspan="1" colspan="1" style="color: #666666;font-weight: bold;" scope="col" id="pb_tbl-e_upd">E</th>
  <th class="gt_col_heading gt_columns_bottom_border gt_center" rowspan="1" colspan="1" style="color: #666666;font-weight: bold;" scope="col" id="pb_tbl-c_upd">C</th>
  <th class="gt_col_heading gt_columns_bottom_border gt_center" rowspan="1" colspan="1" style="color: #666666;font-weight: bold;" scope="col" id="pb_tbl-extract_upd">EXT</th>
</tr>
</thead>
<tbody class="gt_table_body">
  <tr>
    <td style="height: 40px; background-color: #4CA64C; color: transparent;font-size: 0px;" class="gt_row gt_left">#4CA64C</td>
    <td style="height: 40px; color: #666666;font-size: 13px;font-weight: bold;" class="gt_row gt_right">1</td>
    <td style="height: 40px; color: black;font-family: IBM Plex Mono;font-size: 11px;" class="gt_row gt_left">
        <div style="margin: 0; padding: 0; display: inline-block; height: 30px; vertical-align: middle; width: 16%;">
            <!--?xml version="1.0" encoding="UTF-8"?--><?xml version="1.0" encoding="UTF-8"?>
<svg width="30px" height="30px" viewBox="0 0 67 67" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <title>col_vals_not_null</title>
    <g id="Icons" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
        <g id="col_vals_not_null" transform="translate(0.000000, 0.551724)">
            <path d="M56.712234,1 C59.1975153,1 61.4475153,2.00735931 63.076195,3.63603897 C64.7048747,5.26471863 65.712234,7.51471863 65.712234,10 L65.712234,10 L65.712234,65 L10.712234,65 C8.22695259,65 5.97695259,63.9926407 4.34827294,62.363961 C2.71959328,60.7352814 1.71223397,58.4852814 1.71223397,56 L1.71223397,56 L1.71223397,10 C1.71223397,7.51471863 2.71959328,5.26471863 4.34827294,3.63603897 C5.97695259,2.00735931 8.22695259,1 10.712234,1 L10.712234,1 Z" id="rectangle" stroke="#000000" stroke-width="2" fill="#FFFFFF"></path>
            <path d="M40.6120805,47.037834 C37.4692348,47.037834 35.0126139,45.9348613 33.712234,44.0140597 C32.4118541,45.9348613 29.9552331,47.037834 26.8123883,47.037834 C22.6574397,47.037834 16.0646712,43.4437723 16.0646712,33.8021619 C16.0646712,29.3401361 17.4715879,18.962166 30.5035862,18.962166 C30.9454018,18.962166 31.3057481,19.3225124 31.3057481,19.7643279 L31.3057481,21.3686518 C31.3057481,21.8104674 30.9454018,22.1708138 30.5035862,22.1708138 C26.6400486,22.1708138 22.4819668,25.8118774 22.4819668,33.8021619 C22.4819668,37.5090277 23.7635456,43.0270243 27.2949384,43.0270243 C29.795428,43.0270243 31.224279,40.4231312 32.0985095,38.2861221 C30.5067194,35.6101596 29.7014243,33.1034035 29.7014243,30.8347892 C29.7014243,25.6238707 31.8603677,23.7751377 33.712234,23.7751377 C35.5641002,23.7751377 37.7230437,25.6238707 37.7230437,30.8347892 C37.7230437,33.1347383 36.9396828,35.5788255 35.3290916,38.2861221 C36.6294715,41.4321009 38.243196,43.0270243 40.1295295,43.0270243 C43.6609223,43.0270243 44.9425012,37.5090277 44.9425012,33.8021619 C44.9425012,25.8118774 40.7844193,22.1708138 36.9208817,22.1708138 C36.4759329,22.1708138 36.1187198,21.8104674 36.1187198,21.3686518 L36.1187198,19.7643279 C36.1187198,19.3225124 36.4759329,18.962166 36.9208817,18.962166 C49.9528801,18.962166 51.3597967,29.3401361 51.3597967,33.8021619 C51.3597967,43.4437723 44.7670282,47.037834 40.6120805,47.037834 Z" id="omega" fill="#000000" fill-rule="nonzero"></path>
            <path d="M33,7.93597705 C33.2761424,7.93597705 33.5,8.15983467 33.5,8.43597705 L33.5,57.564023 C33.5,57.8401653 33.2761424,58.064023 33,58.064023 C32.7238576,58.064023 32.5,57.8401653 32.5,57.564023 L32.5,8.43597705 C32.5,8.15983467 32.7238576,7.93597705 33,7.93597705 Z" id="line_black" fill="#000000" transform="translate(33.000000, 33.000000) rotate(-320.000000) translate(-33.000000, -33.000000) "></path>
            <polygon id="line_white" fill="#FFFFFF" transform="translate(34.899496, 32.153303) rotate(-320.000000) translate(-34.899496, -32.153303) " points="34.3994962 8.54160469 35.3994962 8.54160469 35.3994962 55.7650019 34.3994962 55.7650019"></polygon>
        </g>
    </g>
</svg>
        </div>
        <div style="font-family: 'IBM Plex Mono', monospace, courier; color: black; font-size: 10px; display: inline-block; vertical-align: middle;">
            <div>col_vals_not_null()</div>
        </div>
        </td>
    <td style="height: 40px; color: black;font-family: IBM Plex Mono;font-size: 11px; border-left: 1px dashed #E5E5E5; white-space: nowrap; text-overflow: ellipsis; overflow: hidden;" class="gt_row gt_left">date</td>
    <td style="height: 40px; color: black;font-family: IBM Plex Mono;font-size: 11px; border-left: 1px dashed #E5E5E5; white-space: nowrap; text-overflow: ellipsis; overflow: hidden;" class="gt_row gt_left">&mdash;</td>
    <td style="height: 40px; background-color: #FCFCFC; border-left: 1px solid #D3D3D3;" class="gt_row gt_center"><svg width="25px" height="25px" viewBox="0 0 25 25" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="vertical-align: middle;">
    <g id="unchanged" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
        <g id="unchanged" transform="translate(0.500000, 0.570147)">
            <rect id="Rectangle" x="0.125132506" y="0" width="23.749735" height="23.7894737"></rect>
            <path d="M5.80375046,8.18194736 C3.77191832,8.18194736 2.11875046,9.83495328 2.11875046,11.8669474 C2.11875046,13.8989414 3.77191832,15.5519474 5.80375046,15.5519474 C7.8355826,15.5519474 9.48875046,13.8989414 9.48875046,11.8669474 C9.48875046,9.83495328 7.83552863,8.18194736 5.80375046,8.18194736 Z M5.80375046,14.814915 C4.17821997,14.814915 2.85578285,13.4924778 2.85578285,11.8669474 C2.85578285,10.2414169 4.17821997,8.91897975 5.80375046,8.91897975 C7.42928095,8.91897975 8.75171807,10.2414169 8.75171807,11.8669474 C8.75171807,13.4924778 7.42928095,14.814915 5.80375046,14.814915 Z" id="Shape" fill="#000000" fill-rule="nonzero"></path>
            <path d="M13.9638189,8.699335 C13.9364621,8.70430925 13.9091059,8.71176968 13.8842359,8.71923074 C13.7822704,8.73663967 13.6877654,8.77643115 13.6056956,8.83860518 L10.2433156,11.3852598 C10.0766886,11.5046343 9.97720993,11.6986181 9.97720993,11.9025491 C9.97720993,12.1064807 10.0766886,12.3004639 10.2433156,12.4198383 L13.6056956,14.966493 C13.891697,15.1803725 14.2970729,15.1231721 14.5109517,14.8371707 C14.7248313,14.5511692 14.6676309,14.145794 14.3816294,13.9319145 L12.5313257,12.5392127 L21.8812495,12.5392127 L21.8812495,11.2658854 L12.5313257,11.2658854 L14.3816294,9.87318364 C14.6377872,9.71650453 14.7497006,9.40066014 14.6477351,9.11714553 C14.5482564,8.83363156 14.262255,8.65954352 13.9638189,8.699335 Z" id="arrow" fill="#000000" transform="translate(15.929230, 11.894737) rotate(-180.000000) translate(-15.929230, -11.894737) "></path>
        </g>
    </g>
</svg></td>
    <td style="height: 40px; background-color: #FCFCFC; border-right: 1px solid #D3D3D3;" class="gt_row gt_center"><span style="color:#4CA64C;">&check;</span></td>
    <td style="height: 40px; color: black;font-family: IBM Plex Mono;font-size: 11px;" class="gt_row gt_right">7</td>
    <td style="height: 40px; color: black;font-family: IBM Plex Mono;font-size: 11px; border-left: 1px dashed #E5E5E5;" class="gt_row gt_right">7<br />1.00</td>
    <td style="height: 40px; color: black;font-family: IBM Plex Mono;font-size: 11px; border-left: 1px dashed #E5E5E5;" class="gt_row gt_right">0<br />0.00</td>
    <td style="height: 40px; background-color: #FCFCFC; border-left: 1px solid #D3D3D3;" class="gt_row gt_center">&mdash;</td>
    <td style="height: 40px; background-color: #FCFCFC;" class="gt_row gt_center">&mdash;</td>
    <td style="height: 40px; background-color: #FCFCFC; border-right: 1px solid #D3D3D3;" class="gt_row gt_center">&mdash;</td>
    <td style="height: 40px;" class="gt_row gt_center">—</td>
  </tr>
  <tr>
    <td style="height: 40px; background-color: #4CA64C; color: transparent;font-size: 0px;" class="gt_row gt_left">#4CA64C</td>
    <td style="height: 40px; color: #666666;font-size: 13px;font-weight: bold;" class="gt_row gt_right">2</td>
    <td style="height: 40px; color: black;font-family: IBM Plex Mono;font-size: 11px;" class="gt_row gt_left">
        <div style="margin: 0; padding: 0; display: inline-block; height: 30px; vertical-align: middle; width: 16%;">
            <!--?xml version="1.0" encoding="UTF-8"?--><?xml version="1.0" encoding="UTF-8"?>
<svg width="30px" height="30px" viewBox="0 0 67 67" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <title>col_vals_between</title>
    <g id="Icons" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
        <g id="col_vals_between" transform="translate(0.000000, 0.206897)">
            <path d="M56.712234,1 C59.1975153,1 61.4475153,2.00735931 63.076195,3.63603897 C64.7048747,5.26471863 65.712234,7.51471863 65.712234,10 L65.712234,10 L65.712234,65 L10.712234,65 C8.22695259,65 5.97695259,63.9926407 4.34827294,62.363961 C2.71959328,60.7352814 1.71223397,58.4852814 1.71223397,56 L1.71223397,56 L1.71223397,10 C1.71223397,7.51471863 2.71959328,5.26471863 4.34827294,3.63603897 C5.97695259,2.00735931 8.22695259,1 10.712234,1 L10.712234,1 Z" id="rectangle" stroke="#000000" stroke-width="2" fill="#FFFFFF"></path>
            <path d="M11.993484,21.96875 C10.962234,22.082031 10.188797,22.964844 10.212234,24 L10.212234,42 C10.200515,42.722656 10.579422,43.390625 11.204422,43.753906 C11.825515,44.121094 12.598953,44.121094 13.220047,43.753906 C13.845047,43.390625 14.223953,42.722656 14.212234,42 L14.212234,24 C14.220047,23.457031 14.009109,22.9375 13.626297,22.554688 C13.243484,22.171875 12.723953,21.960938 12.180984,21.96875 C12.118484,21.964844 12.055984,21.964844 11.993484,21.96875 Z M55.993484,21.96875 C54.962234,22.082031 54.188797,22.964844 54.212234,24 L54.212234,42 C54.200515,42.722656 54.579422,43.390625 55.204422,43.753906 C55.825515,44.121094 56.598953,44.121094 57.220047,43.753906 C57.845047,43.390625 58.223953,42.722656 58.212234,42 L58.212234,24 C58.220047,23.457031 58.009109,22.9375 57.626297,22.554688 C57.243484,22.171875 56.723953,21.960938 56.180984,21.96875 C56.118484,21.964844 56.055984,21.964844 55.993484,21.96875 Z M16.212234,22 C15.661453,22 15.212234,22.449219 15.212234,23 C15.212234,23.550781 15.661453,24 16.212234,24 C16.763015,24 17.212234,23.550781 17.212234,23 C17.212234,22.449219 16.763015,22 16.212234,22 Z M20.212234,22 C19.661453,22 19.212234,22.449219 19.212234,23 C19.212234,23.550781 19.661453,24 20.212234,24 C20.763015,24 21.212234,23.550781 21.212234,23 C21.212234,22.449219 20.763015,22 20.212234,22 Z M24.212234,22 C23.661453,22 23.212234,22.449219 23.212234,23 C23.212234,23.550781 23.661453,24 24.212234,24 C24.763015,24 25.212234,23.550781 25.212234,23 C25.212234,22.449219 24.763015,22 24.212234,22 Z M28.212234,22 C27.661453,22 27.212234,22.449219 27.212234,23 C27.212234,23.550781 27.661453,24 28.212234,24 C28.763015,24 29.212234,23.550781 29.212234,23 C29.212234,22.449219 28.763015,22 28.212234,22 Z M32.212234,22 C31.661453,22 31.212234,22.449219 31.212234,23 C31.212234,23.550781 31.661453,24 32.212234,24 C32.763015,24 33.212234,23.550781 33.212234,23 C33.212234,22.449219 32.763015,22 32.212234,22 Z M36.212234,22 C35.661453,22 35.212234,22.449219 35.212234,23 C35.212234,23.550781 35.661453,24 36.212234,24 C36.763015,24 37.212234,23.550781 37.212234,23 C37.212234,22.449219 36.763015,22 36.212234,22 Z M40.212234,22 C39.661453,22 39.212234,22.449219 39.212234,23 C39.212234,23.550781 39.661453,24 40.212234,24 C40.763015,24 41.212234,23.550781 41.212234,23 C41.212234,22.449219 40.763015,22 40.212234,22 Z M44.212234,22 C43.661453,22 43.212234,22.449219 43.212234,23 C43.212234,23.550781 43.661453,24 44.212234,24 C44.763015,24 45.212234,23.550781 45.212234,23 C45.212234,22.449219 44.763015,22 44.212234,22 Z M48.212234,22 C47.661453,22 47.212234,22.449219 47.212234,23 C47.212234,23.550781 47.661453,24 48.212234,24 C48.763015,24 49.212234,23.550781 49.212234,23 C49.212234,22.449219 48.763015,22 48.212234,22 Z M52.212234,22 C51.661453,22 51.212234,22.449219 51.212234,23 C51.212234,23.550781 51.661453,24 52.212234,24 C52.763015,24 53.212234,23.550781 53.212234,23 C53.212234,22.449219 52.763015,22 52.212234,22 Z M21.462234,27.96875 C21.419265,27.976563 21.376297,27.988281 21.337234,28 C21.177078,28.027344 21.02864,28.089844 20.899734,28.1875 L15.618484,32.1875 C15.356765,32.375 15.200515,32.679688 15.200515,33 C15.200515,33.320313 15.356765,33.625 15.618484,33.8125 L20.899734,37.8125 C21.348953,38.148438 21.985672,38.058594 22.321609,37.609375 C22.657547,37.160156 22.567703,36.523438 22.118484,36.1875 L19.212234,34 L49.212234,34 L46.305984,36.1875 C45.856765,36.523438 45.766922,37.160156 46.102859,37.609375 C46.438797,38.058594 47.075515,38.148438 47.524734,37.8125 L52.805984,33.8125 C53.067703,33.625 53.223953,33.320313 53.223953,33 C53.223953,32.679688 53.067703,32.375 52.805984,32.1875 L47.524734,28.1875 C47.30989,28.027344 47.040359,27.960938 46.774734,28 C46.743484,28 46.712234,28 46.680984,28 C46.282547,28.074219 45.96614,28.382813 45.884109,28.78125 C45.802078,29.179688 45.970047,29.585938 46.305984,29.8125 L49.212234,32 L19.212234,32 L22.118484,29.8125 C22.520828,29.566406 22.696609,29.070313 22.536453,28.625 C22.380203,28.179688 21.930984,27.90625 21.462234,27.96875 Z M16.212234,42 C15.661453,42 15.212234,42.449219 15.212234,43 C15.212234,43.550781 15.661453,44 16.212234,44 C16.763015,44 17.212234,43.550781 17.212234,43 C17.212234,42.449219 16.763015,42 16.212234,42 Z M20.212234,42 C19.661453,42 19.212234,42.449219 19.212234,43 C19.212234,43.550781 19.661453,44 20.212234,44 C20.763015,44 21.212234,43.550781 21.212234,43 C21.212234,42.449219 20.763015,42 20.212234,42 Z M24.212234,42 C23.661453,42 23.212234,42.449219 23.212234,43 C23.212234,43.550781 23.661453,44 24.212234,44 C24.763015,44 25.212234,43.550781 25.212234,43 C25.212234,42.449219 24.763015,42 24.212234,42 Z M28.212234,42 C27.661453,42 27.212234,42.449219 27.212234,43 C27.212234,43.550781 27.661453,44 28.212234,44 C28.763015,44 29.212234,43.550781 29.212234,43 C29.212234,42.449219 28.763015,42 28.212234,42 Z M32.212234,42 C31.661453,42 31.212234,42.449219 31.212234,43 C31.212234,43.550781 31.661453,44 32.212234,44 C32.763015,44 33.212234,43.550781 33.212234,43 C33.212234,42.449219 32.763015,42 32.212234,42 Z M36.212234,42 C35.661453,42 35.212234,42.449219 35.212234,43 C35.212234,43.550781 35.661453,44 36.212234,44 C36.763015,44 37.212234,43.550781 37.212234,43 C37.212234,42.449219 36.763015,42 36.212234,42 Z M40.212234,42 C39.661453,42 39.212234,42.449219 39.212234,43 C39.212234,43.550781 39.661453,44 40.212234,44 C40.763015,44 41.212234,43.550781 41.212234,43 C41.212234,42.449219 40.763015,42 40.212234,42 Z M44.212234,42 C43.661453,42 43.212234,42.449219 43.212234,43 C43.212234,43.550781 43.661453,44 44.212234,44 C44.763015,44 45.212234,43.550781 45.212234,43 C45.212234,42.449219 44.763015,42 44.212234,42 Z M48.212234,42 C47.661453,42 47.212234,42.449219 47.212234,43 C47.212234,43.550781 47.661453,44 48.212234,44 C48.763015,44 49.212234,43.550781 49.212234,43 C49.212234,42.449219 48.763015,42 48.212234,42 Z M52.212234,42 C51.661453,42 51.212234,42.449219 51.212234,43 C51.212234,43.550781 51.661453,44 52.212234,44 C52.763015,44 53.212234,43.550781 53.212234,43 C53.212234,42.449219 52.763015,42 52.212234,42 Z" id="inside_range" fill="#000000" fill-rule="nonzero"></path>
        </g>
    </g>
</svg>
        </div>
        <div style="font-family: 'IBM Plex Mono', monospace, courier; color: black; font-size: 10px; display: inline-block; vertical-align: middle;">
            <div>col_vals_between()</div>
        </div>
        </td>
    <td style="height: 40px; color: black;font-family: IBM Plex Mono;font-size: 11px; border-left: 1px dashed #E5E5E5; white-space: nowrap; text-overflow: ellipsis; overflow: hidden;" class="gt_row gt_left">sales</td>
    <td style="height: 40px; color: black;font-family: IBM Plex Mono;font-size: 11px; border-left: 1px dashed #E5E5E5; white-space: nowrap; text-overflow: ellipsis; overflow: hidden;" class="gt_row gt_left">[0, 10000]</td>
    <td style="height: 40px; background-color: #FCFCFC; border-left: 1px solid #D3D3D3;" class="gt_row gt_center"><svg width="25px" height="25px" viewBox="0 0 25 25" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="vertical-align: middle;">
    <g id="unchanged" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
        <g id="unchanged" transform="translate(0.500000, 0.570147)">
            <rect id="Rectangle" x="0.125132506" y="0" width="23.749735" height="23.7894737"></rect>
            <path d="M5.80375046,8.18194736 C3.77191832,8.18194736 2.11875046,9.83495328 2.11875046,11.8669474 C2.11875046,13.8989414 3.77191832,15.5519474 5.80375046,15.5519474 C7.8355826,15.5519474 9.48875046,13.8989414 9.48875046,11.8669474 C9.48875046,9.83495328 7.83552863,8.18194736 5.80375046,8.18194736 Z M5.80375046,14.814915 C4.17821997,14.814915 2.85578285,13.4924778 2.85578285,11.8669474 C2.85578285,10.2414169 4.17821997,8.91897975 5.80375046,8.91897975 C7.42928095,8.91897975 8.75171807,10.2414169 8.75171807,11.8669474 C8.75171807,13.4924778 7.42928095,14.814915 5.80375046,14.814915 Z" id="Shape" fill="#000000" fill-rule="nonzero"></path>
            <path d="M13.9638189,8.699335 C13.9364621,8.70430925 13.9091059,8.71176968 13.8842359,8.71923074 C13.7822704,8.73663967 13.6877654,8.77643115 13.6056956,8.83860518 L10.2433156,11.3852598 C10.0766886,11.5046343 9.97720993,11.6986181 9.97720993,11.9025491 C9.97720993,12.1064807 10.0766886,12.3004639 10.2433156,12.4198383 L13.6056956,14.966493 C13.891697,15.1803725 14.2970729,15.1231721 14.5109517,14.8371707 C14.7248313,14.5511692 14.6676309,14.145794 14.3816294,13.9319145 L12.5313257,12.5392127 L21.8812495,12.5392127 L21.8812495,11.2658854 L12.5313257,11.2658854 L14.3816294,9.87318364 C14.6377872,9.71650453 14.7497006,9.40066014 14.6477351,9.11714553 C14.5482564,8.83363156 14.262255,8.65954352 13.9638189,8.699335 Z" id="arrow" fill="#000000" transform="translate(15.929230, 11.894737) rotate(-180.000000) translate(-15.929230, -11.894737) "></path>
        </g>
    </g>
</svg></td>
    <td style="height: 40px; background-color: #FCFCFC; border-right: 1px solid #D3D3D3;" class="gt_row gt_center"><span style="color:#4CA64C;">&check;</span></td>
    <td style="height: 40px; color: black;font-family: IBM Plex Mono;font-size: 11px;" class="gt_row gt_right">7</td>
    <td style="height: 40px; color: black;font-family: IBM Plex Mono;font-size: 11px; border-left: 1px dashed #E5E5E5;" class="gt_row gt_right">7<br />1.00</td>
    <td style="height: 40px; color: black;font-family: IBM Plex Mono;font-size: 11px; border-left: 1px dashed #E5E5E5;" class="gt_row gt_right">0<br />0.00</td>
    <td style="height: 40px; background-color: #FCFCFC; border-left: 1px solid #D3D3D3;" class="gt_row gt_center">&mdash;</td>
    <td style="height: 40px; background-color: #FCFCFC;" class="gt_row gt_center">&mdash;</td>
    <td style="height: 40px; background-color: #FCFCFC; border-right: 1px solid #D3D3D3;" class="gt_row gt_center">&mdash;</td>
    <td style="height: 40px;" class="gt_row gt_center">—</td>
  </tr>
  <tr>
    <td style="height: 40px; background-color: #4CA64C; color: transparent;font-size: 0px;" class="gt_row gt_left">#4CA64C</td>
    <td style="height: 40px; color: #666666;font-size: 13px;font-weight: bold;" class="gt_row gt_right">3</td>
    <td style="height: 40px; color: black;font-family: IBM Plex Mono;font-size: 11px;" class="gt_row gt_left">
        <div style="margin: 0; padding: 0; display: inline-block; height: 30px; vertical-align: middle; width: 16%;">
            <!--?xml version="1.0" encoding="UTF-8"?--><?xml version="1.0" encoding="UTF-8"?>
<svg width="30px" height="30px" viewBox="0 0 67 67" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <title>col_vals_between</title>
    <g id="Icons" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
        <g id="col_vals_between" transform="translate(0.000000, 0.206897)">
            <path d="M56.712234,1 C59.1975153,1 61.4475153,2.00735931 63.076195,3.63603897 C64.7048747,5.26471863 65.712234,7.51471863 65.712234,10 L65.712234,10 L65.712234,65 L10.712234,65 C8.22695259,65 5.97695259,63.9926407 4.34827294,62.363961 C2.71959328,60.7352814 1.71223397,58.4852814 1.71223397,56 L1.71223397,56 L1.71223397,10 C1.71223397,7.51471863 2.71959328,5.26471863 4.34827294,3.63603897 C5.97695259,2.00735931 8.22695259,1 10.712234,1 L10.712234,1 Z" id="rectangle" stroke="#000000" stroke-width="2" fill="#FFFFFF"></path>
            <path d="M11.993484,21.96875 C10.962234,22.082031 10.188797,22.964844 10.212234,24 L10.212234,42 C10.200515,42.722656 10.579422,43.390625 11.204422,43.753906 C11.825515,44.121094 12.598953,44.121094 13.220047,43.753906 C13.845047,43.390625 14.223953,42.722656 14.212234,42 L14.212234,24 C14.220047,23.457031 14.009109,22.9375 13.626297,22.554688 C13.243484,22.171875 12.723953,21.960938 12.180984,21.96875 C12.118484,21.964844 12.055984,21.964844 11.993484,21.96875 Z M55.993484,21.96875 C54.962234,22.082031 54.188797,22.964844 54.212234,24 L54.212234,42 C54.200515,42.722656 54.579422,43.390625 55.204422,43.753906 C55.825515,44.121094 56.598953,44.121094 57.220047,43.753906 C57.845047,43.390625 58.223953,42.722656 58.212234,42 L58.212234,24 C58.220047,23.457031 58.009109,22.9375 57.626297,22.554688 C57.243484,22.171875 56.723953,21.960938 56.180984,21.96875 C56.118484,21.964844 56.055984,21.964844 55.993484,21.96875 Z M16.212234,22 C15.661453,22 15.212234,22.449219 15.212234,23 C15.212234,23.550781 15.661453,24 16.212234,24 C16.763015,24 17.212234,23.550781 17.212234,23 C17.212234,22.449219 16.763015,22 16.212234,22 Z M20.212234,22 C19.661453,22 19.212234,22.449219 19.212234,23 C19.212234,23.550781 19.661453,24 20.212234,24 C20.763015,24 21.212234,23.550781 21.212234,23 C21.212234,22.449219 20.763015,22 20.212234,22 Z M24.212234,22 C23.661453,22 23.212234,22.449219 23.212234,23 C23.212234,23.550781 23.661453,24 24.212234,24 C24.763015,24 25.212234,23.550781 25.212234,23 C25.212234,22.449219 24.763015,22 24.212234,22 Z M28.212234,22 C27.661453,22 27.212234,22.449219 27.212234,23 C27.212234,23.550781 27.661453,24 28.212234,24 C28.763015,24 29.212234,23.550781 29.212234,23 C29.212234,22.449219 28.763015,22 28.212234,22 Z M32.212234,22 C31.661453,22 31.212234,22.449219 31.212234,23 C31.212234,23.550781 31.661453,24 32.212234,24 C32.763015,24 33.212234,23.550781 33.212234,23 C33.212234,22.449219 32.763015,22 32.212234,22 Z M36.212234,22 C35.661453,22 35.212234,22.449219 35.212234,23 C35.212234,23.550781 35.661453,24 36.212234,24 C36.763015,24 37.212234,23.550781 37.212234,23 C37.212234,22.449219 36.763015,22 36.212234,22 Z M40.212234,22 C39.661453,22 39.212234,22.449219 39.212234,23 C39.212234,23.550781 39.661453,24 40.212234,24 C40.763015,24 41.212234,23.550781 41.212234,23 C41.212234,22.449219 40.763015,22 40.212234,22 Z M44.212234,22 C43.661453,22 43.212234,22.449219 43.212234,23 C43.212234,23.550781 43.661453,24 44.212234,24 C44.763015,24 45.212234,23.550781 45.212234,23 C45.212234,22.449219 44.763015,22 44.212234,22 Z M48.212234,22 C47.661453,22 47.212234,22.449219 47.212234,23 C47.212234,23.550781 47.661453,24 48.212234,24 C48.763015,24 49.212234,23.550781 49.212234,23 C49.212234,22.449219 48.763015,22 48.212234,22 Z M52.212234,22 C51.661453,22 51.212234,22.449219 51.212234,23 C51.212234,23.550781 51.661453,24 52.212234,24 C52.763015,24 53.212234,23.550781 53.212234,23 C53.212234,22.449219 52.763015,22 52.212234,22 Z M21.462234,27.96875 C21.419265,27.976563 21.376297,27.988281 21.337234,28 C21.177078,28.027344 21.02864,28.089844 20.899734,28.1875 L15.618484,32.1875 C15.356765,32.375 15.200515,32.679688 15.200515,33 C15.200515,33.320313 15.356765,33.625 15.618484,33.8125 L20.899734,37.8125 C21.348953,38.148438 21.985672,38.058594 22.321609,37.609375 C22.657547,37.160156 22.567703,36.523438 22.118484,36.1875 L19.212234,34 L49.212234,34 L46.305984,36.1875 C45.856765,36.523438 45.766922,37.160156 46.102859,37.609375 C46.438797,38.058594 47.075515,38.148438 47.524734,37.8125 L52.805984,33.8125 C53.067703,33.625 53.223953,33.320313 53.223953,33 C53.223953,32.679688 53.067703,32.375 52.805984,32.1875 L47.524734,28.1875 C47.30989,28.027344 47.040359,27.960938 46.774734,28 C46.743484,28 46.712234,28 46.680984,28 C46.282547,28.074219 45.96614,28.382813 45.884109,28.78125 C45.802078,29.179688 45.970047,29.585938 46.305984,29.8125 L49.212234,32 L19.212234,32 L22.118484,29.8125 C22.520828,29.566406 22.696609,29.070313 22.536453,28.625 C22.380203,28.179688 21.930984,27.90625 21.462234,27.96875 Z M16.212234,42 C15.661453,42 15.212234,42.449219 15.212234,43 C15.212234,43.550781 15.661453,44 16.212234,44 C16.763015,44 17.212234,43.550781 17.212234,43 C17.212234,42.449219 16.763015,42 16.212234,42 Z M20.212234,42 C19.661453,42 19.212234,42.449219 19.212234,43 C19.212234,43.550781 19.661453,44 20.212234,44 C20.763015,44 21.212234,43.550781 21.212234,43 C21.212234,42.449219 20.763015,42 20.212234,42 Z M24.212234,42 C23.661453,42 23.212234,42.449219 23.212234,43 C23.212234,43.550781 23.661453,44 24.212234,44 C24.763015,44 25.212234,43.550781 25.212234,43 C25.212234,42.449219 24.763015,42 24.212234,42 Z M28.212234,42 C27.661453,42 27.212234,42.449219 27.212234,43 C27.212234,43.550781 27.661453,44 28.212234,44 C28.763015,44 29.212234,43.550781 29.212234,43 C29.212234,42.449219 28.763015,42 28.212234,42 Z M32.212234,42 C31.661453,42 31.212234,42.449219 31.212234,43 C31.212234,43.550781 31.661453,44 32.212234,44 C32.763015,44 33.212234,43.550781 33.212234,43 C33.212234,42.449219 32.763015,42 32.212234,42 Z M36.212234,42 C35.661453,42 35.212234,42.449219 35.212234,43 C35.212234,43.550781 35.661453,44 36.212234,44 C36.763015,44 37.212234,43.550781 37.212234,43 C37.212234,42.449219 36.763015,42 36.212234,42 Z M40.212234,42 C39.661453,42 39.212234,42.449219 39.212234,43 C39.212234,43.550781 39.661453,44 40.212234,44 C40.763015,44 41.212234,43.550781 41.212234,43 C41.212234,42.449219 40.763015,42 40.212234,42 Z M44.212234,42 C43.661453,42 43.212234,42.449219 43.212234,43 C43.212234,43.550781 43.661453,44 44.212234,44 C44.763015,44 45.212234,43.550781 45.212234,43 C45.212234,42.449219 44.763015,42 44.212234,42 Z M48.212234,42 C47.661453,42 47.212234,42.449219 47.212234,43 C47.212234,43.550781 47.661453,44 48.212234,44 C48.763015,44 49.212234,43.550781 49.212234,43 C49.212234,42.449219 48.763015,42 48.212234,42 Z M52.212234,42 C51.661453,42 51.212234,42.449219 51.212234,43 C51.212234,43.550781 51.661453,44 52.212234,44 C52.763015,44 53.212234,43.550781 53.212234,43 C53.212234,42.449219 52.763015,42 52.212234,42 Z" id="inside_range" fill="#000000" fill-rule="nonzero"></path>
        </g>
    </g>
</svg>
        </div>
        <div style="font-family: 'IBM Plex Mono', monospace, courier; color: black; font-size: 10px; display: inline-block; vertical-align: middle;">
            <div>col_vals_between()</div>
        </div>
        </td>
    <td style="height: 40px; color: black;font-family: IBM Plex Mono;font-size: 11px; border-left: 1px dashed #E5E5E5; white-space: nowrap; text-overflow: ellipsis; overflow: hidden;" class="gt_row gt_left">customer_rating</td>
    <td style="height: 40px; color: black;font-family: IBM Plex Mono;font-size: 11px; border-left: 1px dashed #E5E5E5; white-space: nowrap; text-overflow: ellipsis; overflow: hidden;" class="gt_row gt_left">[1.0, 5.0]</td>
    <td style="height: 40px; background-color: #FCFCFC; border-left: 1px solid #D3D3D3;" class="gt_row gt_center"><svg width="25px" height="25px" viewBox="0 0 25 25" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="vertical-align: middle;">
    <g id="unchanged" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
        <g id="unchanged" transform="translate(0.500000, 0.570147)">
            <rect id="Rectangle" x="0.125132506" y="0" width="23.749735" height="23.7894737"></rect>
            <path d="M5.80375046,8.18194736 C3.77191832,8.18194736 2.11875046,9.83495328 2.11875046,11.8669474 C2.11875046,13.8989414 3.77191832,15.5519474 5.80375046,15.5519474 C7.8355826,15.5519474 9.48875046,13.8989414 9.48875046,11.8669474 C9.48875046,9.83495328 7.83552863,8.18194736 5.80375046,8.18194736 Z M5.80375046,14.814915 C4.17821997,14.814915 2.85578285,13.4924778 2.85578285,11.8669474 C2.85578285,10.2414169 4.17821997,8.91897975 5.80375046,8.91897975 C7.42928095,8.91897975 8.75171807,10.2414169 8.75171807,11.8669474 C8.75171807,13.4924778 7.42928095,14.814915 5.80375046,14.814915 Z" id="Shape" fill="#000000" fill-rule="nonzero"></path>
            <path d="M13.9638189,8.699335 C13.9364621,8.70430925 13.9091059,8.71176968 13.8842359,8.71923074 C13.7822704,8.73663967 13.6877654,8.77643115 13.6056956,8.83860518 L10.2433156,11.3852598 C10.0766886,11.5046343 9.97720993,11.6986181 9.97720993,11.9025491 C9.97720993,12.1064807 10.0766886,12.3004639 10.2433156,12.4198383 L13.6056956,14.966493 C13.891697,15.1803725 14.2970729,15.1231721 14.5109517,14.8371707 C14.7248313,14.5511692 14.6676309,14.145794 14.3816294,13.9319145 L12.5313257,12.5392127 L21.8812495,12.5392127 L21.8812495,11.2658854 L12.5313257,11.2658854 L14.3816294,9.87318364 C14.6377872,9.71650453 14.7497006,9.40066014 14.6477351,9.11714553 C14.5482564,8.83363156 14.262255,8.65954352 13.9638189,8.699335 Z" id="arrow" fill="#000000" transform="translate(15.929230, 11.894737) rotate(-180.000000) translate(-15.929230, -11.894737) "></path>
        </g>
    </g>
</svg></td>
    <td style="height: 40px; background-color: #FCFCFC; border-right: 1px solid #D3D3D3;" class="gt_row gt_center"><span style="color:#4CA64C;">&check;</span></td>
    <td style="height: 40px; color: black;font-family: IBM Plex Mono;font-size: 11px;" class="gt_row gt_right">7</td>
    <td style="height: 40px; color: black;font-family: IBM Plex Mono;font-size: 11px; border-left: 1px dashed #E5E5E5;" class="gt_row gt_right">7<br />1.00</td>
    <td style="height: 40px; color: black;font-family: IBM Plex Mono;font-size: 11px; border-left: 1px dashed #E5E5E5;" class="gt_row gt_right">0<br />0.00</td>
    <td style="height: 40px; background-color: #FCFCFC; border-left: 1px solid #D3D3D3;" class="gt_row gt_center">&mdash;</td>
    <td style="height: 40px; background-color: #FCFCFC;" class="gt_row gt_center">&mdash;</td>
    <td style="height: 40px; background-color: #FCFCFC; border-right: 1px solid #D3D3D3;" class="gt_row gt_center">&mdash;</td>
    <td style="height: 40px;" class="gt_row gt_center">—</td>
  </tr>
  <tr>
    <td style="height: 40px; background-color: #4CA64C; color: transparent;font-size: 0px;" class="gt_row gt_left">#4CA64C</td>
    <td style="height: 40px; color: #666666;font-size: 13px;font-weight: bold;" class="gt_row gt_right">4</td>
    <td style="height: 40px; color: black;font-family: IBM Plex Mono;font-size: 11px;" class="gt_row gt_left">
        <div style="margin: 0; padding: 0; display: inline-block; height: 30px; vertical-align: middle; width: 16%;">
            <!--?xml version="1.0" encoding="UTF-8"?--><?xml version="1.0" encoding="UTF-8"?>
<svg width="30px" height="30px" viewBox="0 0 67 67" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <title>col_vals_in_set</title>
    <g id="Icons" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
        <g id="col_vals_in_set" transform="translate(0.000000, 0.172414)">
            <path d="M56.712234,1 C59.1975153,1 61.4475153,2.00735931 63.076195,3.63603897 C64.7048747,5.26471863 65.712234,7.51471863 65.712234,10 L65.712234,10 L65.712234,65 L10.712234,65 C8.22695259,65 5.97695259,63.9926407 4.34827294,62.363961 C2.71959328,60.7352814 1.71223397,58.4852814 1.71223397,56 L1.71223397,56 L1.71223397,10 C1.71223397,7.51471863 2.71959328,5.26471863 4.34827294,3.63603897 C5.97695259,2.00735931 8.22695259,1 10.712234,1 L10.712234,1 Z" id="rectangle" stroke="#000000" stroke-width="2" fill="#FFFFFF"></path>
            <path d="M44.127969,41.1538382 L31.0814568,41.1538382 C29.9510748,41.1536429 28.8827052,40.9256134 27.9079888,40.5136953 C26.4467442,39.8960136 25.19849,38.8599685 24.3189894,37.5577099 C23.8792391,36.906727 23.5314818,36.1899233 23.2936866,35.4252675 C23.2130217,35.16589 23.1460289,34.9005554 23.0913409,34.6307286 L44.1278714,34.6307286 C45.028466,34.6306309 45.7586488,33.9004481 45.7586488,32.9998535 C45.7586488,32.0992589 45.028466,31.3690761 44.1278714,31.3690761 L23.0905596,31.3690761 C23.1990567,30.8337194 23.3597028,30.3180894 23.5675173,29.8264831 C24.185199,28.3652386 25.2212442,27.1169844 26.5236004,26.2374838 C27.1745833,25.7977334 27.891387,25.4499762 28.6560428,25.2122786 C29.4208939,24.9744833 30.2334994,24.8459665 31.0813591,24.8459665 L44.1277737,24.8459665 C45.0283683,24.8459665 45.7586488,24.1157837 45.7586488,23.2151891 C45.7586488,22.3145945 45.0283683,21.5844117 44.1277737,21.5844117 L31.0813591,21.5844117 C29.5096643,21.5844117 28.0039858,21.9038483 26.6373711,22.4820765 C24.5866678,23.3498583 22.8469049,24.7950871 21.6163267,26.616296 C20.3856508,28.4362354 19.665136,30.6413347 19.6658191,33.0000488 C19.6658191,34.5717436 19.9852563,36.0774222 20.5635822,37.4440369 C21.4312663,39.4947402 22.8765927,41.2345031 24.697704,42.4650813 C26.5176434,43.6957572 28.7227427,44.4155883 31.0814568,44.4155883 L44.1278714,44.4155883 C45.028466,44.4155883 45.7586488,43.6854055 45.7586488,42.7848109 C45.7586488,41.8842163 45.0285636,41.1538382 44.127969,41.1538382 Z" id="set_of" fill="#000000" fill-rule="nonzero"></path>
        </g>
    </g>
</svg>
        </div>
        <div style="font-family: 'IBM Plex Mono', monospace, courier; color: black; font-size: 11px; display: inline-block; vertical-align: middle;">
            <div>col_vals_in_set()</div>
        </div>
        </td>
    <td style="height: 40px; color: black;font-family: IBM Plex Mono;font-size: 11px; border-left: 1px dashed #E5E5E5; white-space: nowrap; text-overflow: ellipsis; overflow: hidden;" class="gt_row gt_left">region</td>
    <td style="height: 40px; color: black;font-family: IBM Plex Mono;font-size: 11px; border-left: 1px dashed #E5E5E5; white-space: nowrap; text-overflow: ellipsis; overflow: hidden;" class="gt_row gt_left">North, South, East, West</td>
    <td style="height: 40px; background-color: #FCFCFC; border-left: 1px solid #D3D3D3;" class="gt_row gt_center"><svg width="25px" height="25px" viewBox="0 0 25 25" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="vertical-align: middle;">
    <g id="unchanged" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
        <g id="unchanged" transform="translate(0.500000, 0.570147)">
            <rect id="Rectangle" x="0.125132506" y="0" width="23.749735" height="23.7894737"></rect>
            <path d="M5.80375046,8.18194736 C3.77191832,8.18194736 2.11875046,9.83495328 2.11875046,11.8669474 C2.11875046,13.8989414 3.77191832,15.5519474 5.80375046,15.5519474 C7.8355826,15.5519474 9.48875046,13.8989414 9.48875046,11.8669474 C9.48875046,9.83495328 7.83552863,8.18194736 5.80375046,8.18194736 Z M5.80375046,14.814915 C4.17821997,14.814915 2.85578285,13.4924778 2.85578285,11.8669474 C2.85578285,10.2414169 4.17821997,8.91897975 5.80375046,8.91897975 C7.42928095,8.91897975 8.75171807,10.2414169 8.75171807,11.8669474 C8.75171807,13.4924778 7.42928095,14.814915 5.80375046,14.814915 Z" id="Shape" fill="#000000" fill-rule="nonzero"></path>
            <path d="M13.9638189,8.699335 C13.9364621,8.70430925 13.9091059,8.71176968 13.8842359,8.71923074 C13.7822704,8.73663967 13.6877654,8.77643115 13.6056956,8.83860518 L10.2433156,11.3852598 C10.0766886,11.5046343 9.97720993,11.6986181 9.97720993,11.9025491 C9.97720993,12.1064807 10.0766886,12.3004639 10.2433156,12.4198383 L13.6056956,14.966493 C13.891697,15.1803725 14.2970729,15.1231721 14.5109517,14.8371707 C14.7248313,14.5511692 14.6676309,14.145794 14.3816294,13.9319145 L12.5313257,12.5392127 L21.8812495,12.5392127 L21.8812495,11.2658854 L12.5313257,11.2658854 L14.3816294,9.87318364 C14.6377872,9.71650453 14.7497006,9.40066014 14.6477351,9.11714553 C14.5482564,8.83363156 14.262255,8.65954352 13.9638189,8.699335 Z" id="arrow" fill="#000000" transform="translate(15.929230, 11.894737) rotate(-180.000000) translate(-15.929230, -11.894737) "></path>
        </g>
    </g>
</svg></td>
    <td style="height: 40px; background-color: #FCFCFC; border-right: 1px solid #D3D3D3;" class="gt_row gt_center"><span style="color:#4CA64C;">&check;</span></td>
    <td style="height: 40px; color: black;font-family: IBM Plex Mono;font-size: 11px;" class="gt_row gt_right">7</td>
    <td style="height: 40px; color: black;font-family: IBM Plex Mono;font-size: 11px; border-left: 1px dashed #E5E5E5;" class="gt_row gt_right">7<br />1.00</td>
    <td style="height: 40px; color: black;font-family: IBM Plex Mono;font-size: 11px; border-left: 1px dashed #E5E5E5;" class="gt_row gt_right">0<br />0.00</td>
    <td style="height: 40px; background-color: #FCFCFC; border-left: 1px solid #D3D3D3;" class="gt_row gt_center">&mdash;</td>
    <td style="height: 40px; background-color: #FCFCFC;" class="gt_row gt_center">&mdash;</td>
    <td style="height: 40px; background-color: #FCFCFC; border-right: 1px solid #D3D3D3;" class="gt_row gt_center">&mdash;</td>
    <td style="height: 40px;" class="gt_row gt_center">—</td>
  </tr>
</tbody>
  <tfoot class="gt_sourcenotes">
  <tr>
    <td class="gt_sourcenote" colspan="14" style="text-align: left;"><div style='margin-top: 5px; margin-bottom: 5px;'><span style='background-color: #FFF; color: #444; padding: 0.5em 0.5em; position: inherit; text-transform: uppercase; margin-left: 10px; margin-right: 5px; border: solid 1px #999999; font-variant-numeric: tabular-nums; border-radius: 0; padding: 2px 10px 2px 10px;'>2026-06-04 17:00:11 UTC</span><span style='background-color: #FFF; color: #444; padding: 0.5em 0.5em; position: inherit; margin-right: 5px; border: solid 1px #999999; font-variant-numeric: tabular-nums; border-radius: 0; padding: 2px 10px 2px 10px;'>< 1 s</span><span style='background-color: #FFF; color: #444; padding: 0.5em 0.5em; position: inherit; text-transform: uppercase; margin: 5px 1px 5px -1px; border: solid 1px #999999; font-variant-numeric: tabular-nums; border-radius: 0; padding: 2px 10px 2px 10px;'>2026-06-04 17:00:11 UTC</span></div></td>
  </tr>
</tfoot>
</table>
</div>
<p>In natural language, the steps that the agent performs are:</p>
<ul>
<li>Validate the <code>sales_data</code> DataFrame</li>
<li>Make sure that no values in <code>Date</code> are null</li>
<li>Make sure that the values in <code>sales</code> are between 0 and 10000</li>
<li>Make sure that the values in <code>customer_rating</code> are between 1 and 5</li>
<li>Make sure that the values in <code>region</code> are &ldquo;North&rdquo;, &ldquo;South&rdquo;, &ldquo;East&rdquo;, or &ldquo;West&rdquo;</li>
<li>Now, interrogate!</li>
</ul>
<p>The resulting table lets us know for each step what was expected, how many values passed, the percentage of values that passed, and so on. And, the validation agent works directly with Polars DataFrames, no need to convert to pandas!</p>
<p>Also, about that nifty table that gets output? 👀 <strong>Directly</strong> in this blog post (written in <a href="https://quarto.org/" target="_blank" rel="noopener">Quarto</a>)? <a href="https://posit-dev.github.io/great-tables/" target="_blank" rel="noopener">That is Great Tables</a>! But Great Tables isn&rsquo;t just for pointblank&hellip;</p>
<h2 id="creating-beautiful-tables-with-great-tables">Creating beautiful tables with Great Tables
</h2>
<p>Let&rsquo;s summarize some data:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">daily_summary</span> <span class="o">=</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">sales_data</span><span class="o">.</span><span class="n">group_by</span><span class="p">(</span><span class="s2">&#34;date&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">.</span><span class="n">agg</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="p">[</span>
</span></span><span class="line"><span class="cl">            <span class="n">pl</span><span class="o">.</span><span class="n">col</span><span class="p">(</span><span class="s2">&#34;sales&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">sum</span><span class="p">()</span><span class="o">.</span><span class="n">alias</span><span class="p">(</span><span class="s2">&#34;total_sales&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">pl</span><span class="o">.</span><span class="n">col</span><span class="p">(</span><span class="s2">&#34;units_sold&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">sum</span><span class="p">()</span><span class="o">.</span><span class="n">alias</span><span class="p">(</span><span class="s2">&#34;total_units&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">pl</span><span class="o">.</span><span class="n">col</span><span class="p">(</span><span class="s2">&#34;customer_rating&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">mean</span><span class="p">()</span><span class="o">.</span><span class="n">alias</span><span class="p">(</span><span class="s2">&#34;avg_rating&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">.</span><span class="n">sort</span><span class="p">(</span><span class="s2">&#34;date&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">regional_summary</span> <span class="o">=</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">sales_data</span><span class="o">.</span><span class="n">group_by</span><span class="p">(</span><span class="s2">&#34;region&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">.</span><span class="n">agg</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="p">[</span>
</span></span><span class="line"><span class="cl">            <span class="n">pl</span><span class="o">.</span><span class="n">col</span><span class="p">(</span><span class="s2">&#34;sales&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">sum</span><span class="p">()</span><span class="o">.</span><span class="n">alias</span><span class="p">(</span><span class="s2">&#34;total_sales&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">pl</span><span class="o">.</span><span class="n">col</span><span class="p">(</span><span class="s2">&#34;sales&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">mean</span><span class="p">()</span><span class="o">.</span><span class="n">alias</span><span class="p">(</span><span class="s2">&#34;avg_sales&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">pl</span><span class="o">.</span><span class="n">col</span><span class="p">(</span><span class="s2">&#34;units_sold&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">sum</span><span class="p">()</span><span class="o">.</span><span class="n">alias</span><span class="p">(</span><span class="s2">&#34;total_units&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">pl</span><span class="o">.</span><span class="n">col</span><span class="p">(</span><span class="s2">&#34;sales&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">alias</span><span class="p">(</span><span class="s2">&#34;sales_trend&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">.</span><span class="n">sort</span><span class="p">(</span><span class="s2">&#34;total_sales&#34;</span><span class="p">,</span> <span class="n">descending</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span></span></span></code></pre></div></div>
<p>Let&rsquo;s present our regional summary in a ✨publication-quality table✨ using <strong>Great Tables</strong>:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">great_tables</span> <span class="kn">import</span> <span class="n">loc</span><span class="p">,</span> <span class="n">style</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">GT</span><span class="p">(</span><span class="n">regional_summary</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">.</span><span class="n">tab_header</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">title</span><span class="o">=</span><span class="s2">&#34;Regional Sales Performance&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">subtitle</span><span class="o">=</span><span class="s2">&#34;Week of January 15-21, 2026&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">.</span><span class="n">fmt_currency</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">columns</span><span class="o">=</span><span class="p">[</span><span class="s2">&#34;total_sales&#34;</span><span class="p">,</span> <span class="s2">&#34;avg_sales&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">        <span class="n">currency</span><span class="o">=</span><span class="s2">&#34;USD&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">.</span><span class="n">fmt_number</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">columns</span><span class="o">=</span><span class="s2">&#34;total_units&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">decimals</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">sep_mark</span><span class="o">=</span><span class="s2">&#34;,&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">.</span><span class="n">fmt_nanoplot</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">columns</span><span class="o">=</span><span class="s2">&#34;sales_trend&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">plot_type</span><span class="o">=</span><span class="s2">&#34;line&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">autoscale</span><span class="o">=</span><span class="kc">True</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">.</span><span class="n">cols_label</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">region</span><span class="o">=</span><span class="s2">&#34;Region&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">total_sales</span><span class="o">=</span><span class="s2">&#34;Total Sales&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">avg_sales</span><span class="o">=</span><span class="s2">&#34;Average Sale&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">total_units</span><span class="o">=</span><span class="s2">&#34;Units Sold&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">sales_trend</span><span class="o">=</span><span class="s2">&#34;Trend&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">.</span><span class="n">data_color</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">columns</span><span class="o">=</span><span class="s2">&#34;total_sales&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">palette</span><span class="o">=</span><span class="p">[</span><span class="s2">&#34;#f0f0f0&#34;</span><span class="p">,</span> <span class="s2">&#34;#447099&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">        <span class="n">domain</span><span class="o">=</span><span class="p">[</span><span class="mi">1000</span><span class="p">,</span> <span class="mi">5000</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">.</span><span class="n">tab_style</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">style</span><span class="o">=</span><span class="n">style</span><span class="o">.</span><span class="n">text</span><span class="p">(</span><span class="n">weight</span><span class="o">=</span><span class="s2">&#34;bold&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">locations</span><span class="o">=</span><span class="n">loc</span><span class="o">.</span><span class="n">body</span><span class="p">(</span><span class="n">columns</span><span class="o">=</span><span class="s2">&#34;region&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">.</span><span class="n">tab_style</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">style</span><span class="o">=</span><span class="n">style</span><span class="o">.</span><span class="n">fill</span><span class="p">(</span><span class="n">color</span><span class="o">=</span><span class="s2">&#34;#e8f4f8&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">locations</span><span class="o">=</span><span class="n">loc</span><span class="o">.</span><span class="n">body</span><span class="p">(</span><span class="n">rows</span><span class="o">=</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">.</span><span class="n">tab_source_note</span><span class="p">(</span><span class="s2">&#34;Data validated with pointblank · Trend shows daily sales pattern&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">.</span><span class="n">tab_options</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">table_font_size</span><span class="o">=</span><span class="s2">&#34;14px&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">heading_title_font_size</span><span class="o">=</span><span class="s2">&#34;18px&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">heading_subtitle_font_size</span><span class="o">=</span><span class="s2">&#34;14px&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span></span></span></code></pre></div></div>
<div id="ihvfoecgvb" style="padding-left:0px;padding-right:0px;padding-top:10px;padding-bottom:10px;overflow-x:auto;overflow-y:auto;width:auto;height:auto;">
<style>
#ihvfoecgvb table {
          font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Helvetica Neue', 'Fira Sans', 'Droid Sans', Arial, sans-serif;
          -webkit-font-smoothing: antialiased;
          -moz-osx-font-smoothing: grayscale;
        }
#ihvfoecgvb thead, tbody, tfoot, tr, td, th { border-style: none; }
 tr { background-color: transparent; }
#ihvfoecgvb p { margin: 0; padding: 0; }
 #ihvfoecgvb .gt_table { display: table; border-collapse: collapse; line-height: normal; margin-left: auto; margin-right: auto; color: #333333; font-size: 14px; font-weight: normal; font-style: normal; background-color: #FFFFFF; width: auto; border-top-style: solid; border-top-width: 2px; border-top-color: #A8A8A8; border-right-style: none; border-right-width: 2px; border-right-color: #D3D3D3; border-bottom-style: solid; border-bottom-width: 2px; border-bottom-color: #A8A8A8; border-left-style: none; border-left-width: 2px; border-left-color: #D3D3D3; }
 #ihvfoecgvb .gt_caption { padding-top: 4px; padding-bottom: 4px; }
 #ihvfoecgvb .gt_title { color: #333333; font-size: 18px; font-weight: initial; padding-top: 4px; padding-bottom: 4px; padding-left: 5px; padding-right: 5px; border-bottom-color: #FFFFFF; border-bottom-width: 0; }
 #ihvfoecgvb .gt_subtitle { color: #333333; font-size: 14px; font-weight: initial; padding-top: 3px; padding-bottom: 5px; padding-left: 5px; padding-right: 5px; border-top-color: #FFFFFF; border-top-width: 0; }
 #ihvfoecgvb .gt_heading { background-color: #FFFFFF; text-align: center; border-bottom-color: #FFFFFF; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; }
 #ihvfoecgvb .gt_bottom_border { border-bottom-style: solid; border-bottom-width: 2px; border-bottom-color: #D3D3D3; }
 #ihvfoecgvb .gt_col_headings { border-top-style: solid; border-top-width: 2px; border-top-color: #D3D3D3; border-bottom-style: solid; border-bottom-width: 2px; border-bottom-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; }
 #ihvfoecgvb .gt_col_heading { color: #333333; background-color: #FFFFFF; font-size: 100%; font-weight: normal; text-transform: inherit; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: bottom; padding-top: 5px; padding-bottom: 5px; padding-left: 5px; padding-right: 5px; overflow-x: hidden; }
 #ihvfoecgvb .gt_column_spanner_outer { color: #333333; background-color: #FFFFFF; font-size: 100%; font-weight: normal; text-transform: inherit; padding-top: 0; padding-bottom: 0; padding-left: 4px; padding-right: 4px; }
 #ihvfoecgvb .gt_column_spanner_outer:first-child { padding-left: 0; }
 #ihvfoecgvb .gt_column_spanner_outer:last-child { padding-right: 0; }
 #ihvfoecgvb .gt_column_spanner { border-bottom-style: solid; border-bottom-width: 2px; border-bottom-color: #D3D3D3; vertical-align: bottom; padding-top: 5px; padding-bottom: 5px; overflow-x: hidden; display: inline-block; width: 100%; }
 #ihvfoecgvb .gt_spanner_row { border-bottom-style: hidden; }
 #ihvfoecgvb .gt_group_heading { padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; color: #333333; background-color: #FFFFFF; font-size: 100%; font-weight: initial; text-transform: inherit; border-top-style: solid; border-top-width: 2px; border-top-color: #D3D3D3; border-bottom-style: solid; border-bottom-width: 2px; border-bottom-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; text-align: left; }
 #ihvfoecgvb .gt_empty_group_heading { padding: 0.5px; color: #333333; background-color: #FFFFFF; font-size: 100%; font-weight: initial; border-top-style: solid; border-top-width: 2px; border-top-color: #D3D3D3; border-bottom-style: solid; border-bottom-width: 2px; border-bottom-color: #D3D3D3; vertical-align: middle; }
 #ihvfoecgvb .gt_from_md> :first-child { margin-top: 0; }
 #ihvfoecgvb .gt_from_md> :last-child { margin-bottom: 0; }
 #ihvfoecgvb .gt_row { padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; }
 #ihvfoecgvb .gt_stub { color: #333333; background-color: #FFFFFF; font-size: 100%; font-weight: initial; text-transform: inherit; border-right-style: solid; border-right-width: 2px; border-right-color: #D3D3D3; padding-left: 5px; padding-right: 5px; }
 #ihvfoecgvb .gt_stub_row_group { color: #333333; background-color: #FFFFFF; font-size: 100%; font-weight: initial; text-transform: inherit; border-right-style: solid; border-right-width: 2px; border-right-color: #D3D3D3; padding-left: 5px; padding-right: 5px; vertical-align: top; }
 #ihvfoecgvb .gt_row_group_first td { border-top-width: 2px; }
 #ihvfoecgvb .gt_row_group_first th { border-top-width: 2px; }
 #ihvfoecgvb .gt_striped { color: #333333; background-color: #F4F4F4; }
 #ihvfoecgvb .gt_table_body { border-top-style: solid; border-top-width: 2px; border-top-color: #D3D3D3; border-bottom-style: solid; border-bottom-width: 2px; border-bottom-color: #D3D3D3; }
 #ihvfoecgvb .gt_grand_summary_row { color: #333333; background-color: #FFFFFF; text-transform: inherit; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; }
 #ihvfoecgvb .gt_first_grand_summary_row_bottom { border-top-style: double; border-top-width: 6px; border-top-color: #D3D3D3; }
 #ihvfoecgvb .gt_last_grand_summary_row_top { border-bottom-style: double; border-bottom-width: 6px; border-bottom-color: #D3D3D3; }
 #ihvfoecgvb .gt_sourcenotes { color: #333333; background-color: #FFFFFF; border-bottom-style: none; border-bottom-width: 2px; border-bottom-color: #D3D3D3; border-left-style: none; border-left-width: 2px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 2px; border-right-color: #D3D3D3; }
 #ihvfoecgvb .gt_sourcenote { font-size: 90%; padding-top: 4px; padding-bottom: 4px; padding-left: 5px; padding-right: 5px; text-align: left; }
 #ihvfoecgvb .gt_left { text-align: left; }
 #ihvfoecgvb .gt_center { text-align: center; }
 #ihvfoecgvb .gt_right { text-align: right; font-variant-numeric: tabular-nums; }
 #ihvfoecgvb .gt_font_normal { font-weight: normal; }
 #ihvfoecgvb .gt_font_bold { font-weight: bold; }
 #ihvfoecgvb .gt_font_italic { font-style: italic; }
 #ihvfoecgvb .gt_super { font-size: 65%; }
 #ihvfoecgvb .gt_footnote_marks { font-size: 75%; vertical-align: 0.4em; position: initial; }
 #ihvfoecgvb .gt_asterisk { font-size: 100%; vertical-align: 0; }
</style>
<table class="gt_table" data-quarto-postprocess="true" data-quarto-disable-processing="false" data-quarto-bootstrap="false">
<colgroup>
<col style="width: 20%" />
<col style="width: 20%" />
<col style="width: 20%" />
<col style="width: 20%" />
<col style="width: 20%" />
</colgroup>
<thead>
<tr class="gt_heading">
<th colspan="5" class="gt_heading gt_title gt_font_normal">Regional Sales Performance</th>
</tr>
<tr class="gt_heading">
<th colspan="5" class="gt_heading gt_subtitle gt_font_normal gt_bottom_border">Week of January 15-21, 2026</th>
</tr>
<tr class="gt_col_headings">
<th id="region" class="gt_col_heading gt_columns_bottom_border gt_left" data-quarto-table-cell-role="th" scope="col">Region</th>
<th id="total_sales" class="gt_col_heading gt_columns_bottom_border gt_right" data-quarto-table-cell-role="th" scope="col">Total Sales</th>
<th id="avg_sales" class="gt_col_heading gt_columns_bottom_border gt_right" data-quarto-table-cell-role="th" scope="col">Average Sale</th>
<th id="total_units" class="gt_col_heading gt_columns_bottom_border gt_right" data-quarto-table-cell-role="th" scope="col">Units Sold</th>
<th id="sales_trend" class="gt_col_heading gt_columns_bottom_border gt_center" data-quarto-table-cell-role="th" scope="col">Trend</th>
</tr>
</thead>
<tbody class="gt_table_body">
<tr>
<td class="gt_row gt_left" style="font-weight: bold; background-color: #e8f4f8">North</td>
<td class="gt_row gt_right" style="color: #000000; background-color: #557da2; background-color: #e8f4f8">$4,600.00</td>
<td class="gt_row gt_right" style="background-color: #e8f4f8">$1,533.33</td>
<td class="gt_row gt_right" style="background-color: #e8f4f8">92</td>
<td class="gt_row gt_center" style="background-color: #e8f4f8"><div>
<svg role="img" viewbox="0 0 250 130" style="height: 2em; margin-left: auto; margin-right: auto; font-size: inherit; overflow: visible; vertical-align: middle; position:relative;">
<defs><pattern id="area_pattern" width="8" height="8" patternunits="userSpaceOnUse"><path class="pattern-line" d="M 0,8 l 8,-8 M -1,1 l 4,-4 M 6,10 l 4,-4" stroke="#FF0000" stroke-width="1.5" stroke-linecap="round" shape-rendering="geometricPrecision"></path></pattern></defs><path class="area-closed" d="M 50.0,115.0 125.0,87.72727272727273 200.0,51.36363636363637 200.0,125 50.0,125 Z" stroke="transparent" stroke-width="2" fill="url(#area_pattern)" fill-opacity="0.7"></path><path d="M 50.0,115.0 C 75.0,115.0 100.0,87.72727272727273 125.0,87.72727272727273 C 150.0,87.72727272727273 175.0,51.36363636363637 200.0,51.36363636363637" stroke="#4682B4" stroke-width="8" fill="none"></path><circle cx="50.0" cy="115.0" r="10" stroke="#FFFFFF" stroke-width="4" fill="#FF0000"></circle><circle cx="125.0" cy="87.72727272727273" r="10" stroke="#FFFFFF" stroke-width="4" fill="#FF0000"></circle><circle cx="200.0" cy="51.36363636363637" r="10" stroke="#FFFFFF" stroke-width="4" fill="#FF0000"></circle><g class="y-axis-line"><rect x="0" y="0" width="65" height="130" stroke="transparent" stroke-width="0" fill="transparent"></rect><text x="0" y="19.0" fill="transparent" stroke="transparent" font-size="25">2.30K</text><text x="0" y="126.0" fill="transparent" stroke="transparent" font-size="25">1.20K</text></g><g class="vert-line"><rect x="40.0" y="0" width="20" height="130" stroke="transparent" stroke-width="12" fill="transparent"></rect><text x="60.0" y="20" fill="transparent" stroke="transparent" font-size="30px">1.20K</text></g><g class="vert-line"><rect x="115.0" y="0" width="20" height="130" stroke="transparent" stroke-width="12" fill="transparent"></rect><text x="135.0" y="20" fill="transparent" stroke="transparent" font-size="30px">1.50K</text></g><g class="vert-line"><rect x="190.0" y="0" width="20" height="130" stroke="transparent" stroke-width="12" fill="transparent"></rect><text x="210.0" y="20" fill="transparent" stroke="transparent" font-size="30px">1.90K</text></g>
</svg>
</div></td>
</tr>
<tr>
<td class="gt_row gt_left" style="font-weight: bold">West</td>
<td class="gt_row gt_right" style="color: #000000; background-color: #5e83a6">$4,400.00</td>
<td class="gt_row gt_right">$2,200.00</td>
<td class="gt_row gt_right">88</td>
<td class="gt_row gt_center"><div>
<svg role="img" viewbox="0 0 200 130" style="height: 2em; margin-left: auto; margin-right: auto; font-size: inherit; overflow: visible; vertical-align: middle; position:relative;">
<defs><pattern id="area_pattern" width="8" height="8" patternunits="userSpaceOnUse"><path class="pattern-line" d="M 0,8 l 8,-8 M -1,1 l 4,-4 M 6,10 l 4,-4" stroke="#FF0000" stroke-width="1.5" stroke-linecap="round" shape-rendering="geometricPrecision"></path></pattern></defs><path class="area-closed" d="M 50.0,33.18181818181817 150.0,15.0 150.0,125 50.0,125 Z" stroke="transparent" stroke-width="2" fill="url(#area_pattern)" fill-opacity="0.7"></path><path d="M 50.0,33.18181818181817 C 75.0,33.18181818181817 125.0,15.0 150.0,15.0" stroke="#4682B4" stroke-width="8" fill="none"></path><circle cx="50.0" cy="33.18181818181817" r="10" stroke="#FFFFFF" stroke-width="4" fill="#FF0000"></circle><circle cx="150.0" cy="15.0" r="10" stroke="#FFFFFF" stroke-width="4" fill="#FF0000"></circle><g class="y-axis-line"><rect x="0" y="0" width="65" height="130" stroke="transparent" stroke-width="0" fill="transparent"></rect><text x="0" y="19.0" fill="transparent" stroke="transparent" font-size="25">2.30K</text><text x="0" y="126.0" fill="transparent" stroke="transparent" font-size="25">1.20K</text></g><g class="vert-line"><rect x="40.0" y="0" width="20" height="130" stroke="transparent" stroke-width="12" fill="transparent"></rect><text x="60.0" y="20" fill="transparent" stroke="transparent" font-size="30px">2.10K</text></g><g class="vert-line"><rect x="140.0" y="0" width="20" height="130" stroke="transparent" stroke-width="12" fill="transparent"></rect><text x="160.0" y="20" fill="transparent" stroke="transparent" font-size="30px">2.30K</text></g>
</svg>
</div></td>
</tr>
<tr>
<td class="gt_row gt_left" style="font-weight: bold">South</td>
<td class="gt_row gt_right" style="color: #000000; background-color: #87a2bb">$3,450.00</td>
<td class="gt_row gt_right">$1,725.00</td>
<td class="gt_row gt_right">69</td>
<td class="gt_row gt_center"><div>
<svg role="img" viewbox="0 0 200 130" style="height: 2em; margin-left: auto; margin-right: auto; font-size: inherit; overflow: visible; vertical-align: middle; position:relative;">
<defs><pattern id="area_pattern" width="8" height="8" patternunits="userSpaceOnUse"><path class="pattern-line" d="M 0,8 l 8,-8 M -1,1 l 4,-4 M 6,10 l 4,-4" stroke="#FF0000" stroke-width="1.5" stroke-linecap="round" shape-rendering="geometricPrecision"></path></pattern></defs><path class="area-closed" d="M 50.0,60.45454545454546 150.0,74.09090909090908 150.0,125 50.0,125 Z" stroke="transparent" stroke-width="2" fill="url(#area_pattern)" fill-opacity="0.7"></path><path d="M 50.0,60.45454545454546 C 75.0,60.45454545454546 125.0,74.09090909090908 150.0,74.09090909090908" stroke="#4682B4" stroke-width="8" fill="none"></path><circle cx="50.0" cy="60.45454545454546" r="10" stroke="#FFFFFF" stroke-width="4" fill="#FF0000"></circle><circle cx="150.0" cy="74.09090909090908" r="10" stroke="#FFFFFF" stroke-width="4" fill="#FF0000"></circle><g class="y-axis-line"><rect x="0" y="0" width="65" height="130" stroke="transparent" stroke-width="0" fill="transparent"></rect><text x="0" y="19.0" fill="transparent" stroke="transparent" font-size="25">2.30K</text><text x="0" y="126.0" fill="transparent" stroke="transparent" font-size="25">1.20K</text></g><g class="vert-line"><rect x="40.0" y="0" width="20" height="130" stroke="transparent" stroke-width="12" fill="transparent"></rect><text x="60.0" y="20" fill="transparent" stroke="transparent" font-size="30px">1.80K</text></g><g class="vert-line"><rect x="140.0" y="0" width="20" height="130" stroke="transparent" stroke-width="12" fill="transparent"></rect><text x="160.0" y="20" fill="transparent" stroke="transparent" font-size="30px">1.65K</text></g>
</svg>
</div></td>
</tr>
</tbody><tfoot class="gt_sourcenotes">
<tr>
<td colspan="5" class="gt_sourcenote">Data validated with pointblank · Trend shows daily sales pattern</td>
</tr>
</tfoot>
&#10;</table>
</div>
<p>In this example, Great Tables:</p>
<ul>
<li>Adds a title using <code>tab_header</code></li>
<li>Formats currency using <code>fmt_currency</code></li>
<li>Formats numbers using <code>fmt_number</code></li>
<li>Creates a nanoplot (a miniature line chart) using <code>fmt_nanoplot</code></li>
<li>Labels the columns using <code>cols_label</code></li>
<li>Edit color, styling, and font sizes with <code>data_color</code>, <code>tab_style</code>, and <code>tab_options</code></li>
<li>Add a source note using <code>tab_source_note</code></li>
</ul>
<p>All with full Polars support! In fact, Great Tables is the <a href="https://docs.pola.rs/user-guide/misc/styling/" target="_blank" rel="noopener">default way to style Polars DataFrames</a>, using <code>df.style</code>.</p>
<h2 id="visualizations-with-plotnine">Visualizations with plotnine
</h2>
<p>For visualizations, <a href="https://plotnine.org/" target="_blank" rel="noopener"><strong>plotnine</strong></a> brings the <a href="https://en.wikipedia.org/wiki/Wilkinson%27s_Grammar_of_Graphics" target="_blank" rel="noopener">grammar of graphics</a> to Python. Let&rsquo;s create some nice-looking plots:</p>
<h3 id="daily-sales-trend">Daily sales trend
</h3>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">ggplot</span><span class="p">(</span><span class="n">daily_summary</span><span class="p">,</span> <span class="n">aes</span><span class="p">(</span><span class="n">x</span><span class="o">=</span><span class="s2">&#34;date&#34;</span><span class="p">,</span> <span class="n">y</span><span class="o">=</span><span class="s2">&#34;total_sales&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="o">+</span> <span class="n">geom_line</span><span class="p">(</span><span class="n">color</span><span class="o">=</span><span class="s2">&#34;#447099&#34;</span><span class="p">,</span> <span class="n">size</span><span class="o">=</span><span class="mf">1.5</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">+</span> <span class="n">geom_point</span><span class="p">(</span><span class="n">color</span><span class="o">=</span><span class="s2">&#34;#447099&#34;</span><span class="p">,</span> <span class="n">size</span><span class="o">=</span><span class="mi">4</span><span class="p">,</span> <span class="n">fill</span><span class="o">=</span><span class="s2">&#34;white&#34;</span><span class="p">,</span> <span class="n">stroke</span><span class="o">=</span><span class="mf">1.5</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">+</span> <span class="n">geom_text</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">aes</span><span class="p">(</span><span class="n">label</span><span class="o">=</span><span class="s2">&#34;total_sales&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">va</span><span class="o">=</span><span class="s2">&#34;bottom&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">nudge_y</span><span class="o">=</span><span class="mi">100</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">size</span><span class="o">=</span><span class="mi">9</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">color</span><span class="o">=</span><span class="s2">&#34;#333333&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">format_string</span><span class="o">=</span><span class="s2">&#34;$</span><span class="si">{:,.0f}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">+</span> <span class="n">scale_y_continuous</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">labels</span><span class="o">=</span><span class="k">lambda</span> <span class="n">l</span><span class="p">:</span> <span class="p">[</span><span class="sa">f</span><span class="s2">&#34;$</span><span class="si">{</span><span class="n">x</span><span class="si">:</span><span class="s2">,.0f</span><span class="si">}</span><span class="s2">&#34;</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">l</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">        <span class="n">limits</span><span class="o">=</span><span class="p">(</span><span class="mi">1000</span><span class="p">,</span> <span class="mi">2600</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">expand</span><span class="o">=</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">+</span> <span class="n">labs</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">title</span><span class="o">=</span><span class="s2">&#34;Daily Sales Trend&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">subtitle</span><span class="o">=</span><span class="s2">&#34;Week of January 15-21, 2026 • Total revenue trending upward&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">x</span><span class="o">=</span><span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">y</span><span class="o">=</span><span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">+</span> <span class="n">theme_minimal</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="o">+</span> <span class="n">theme</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">plot_title</span><span class="o">=</span><span class="n">element_text</span><span class="p">(</span><span class="n">size</span><span class="o">=</span><span class="mi">16</span><span class="p">,</span> <span class="n">weight</span><span class="o">=</span><span class="s2">&#34;bold&#34;</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s2">&#34;#2c3e50&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">plot_subtitle</span><span class="o">=</span><span class="n">element_text</span><span class="p">(</span><span class="n">size</span><span class="o">=</span><span class="mi">11</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s2">&#34;#7f8c8d&#34;</span><span class="p">,</span> <span class="n">margin</span><span class="o">=</span><span class="p">{</span><span class="s2">&#34;b&#34;</span><span class="p">:</span> <span class="mi">15</span><span class="p">}),</span>
</span></span><span class="line"><span class="cl">        <span class="n">axis_title_y</span><span class="o">=</span><span class="n">element_blank</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">        <span class="n">axis_text_y</span><span class="o">=</span><span class="n">element_text</span><span class="p">(</span><span class="n">size</span><span class="o">=</span><span class="mi">10</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s2">&#34;#666666&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">axis_text_x</span><span class="o">=</span><span class="n">element_text</span><span class="p">(</span><span class="n">size</span><span class="o">=</span><span class="mi">10</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s2">&#34;#666666&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">panel_grid_major_x</span><span class="o">=</span><span class="n">element_blank</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">        <span class="n">panel_grid_minor</span><span class="o">=</span><span class="n">element_blank</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">        <span class="n">panel_grid_major_y</span><span class="o">=</span><span class="n">element_line</span><span class="p">(</span><span class="n">color</span><span class="o">=</span><span class="s2">&#34;#e0e0e0&#34;</span><span class="p">,</span> <span class="n">size</span><span class="o">=</span><span class="mf">0.5</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">plot_background</span><span class="o">=</span><span class="n">element_rect</span><span class="p">(</span><span class="n">fill</span><span class="o">=</span><span class="s2">&#34;white&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">panel_background</span><span class="o">=</span><span class="n">element_rect</span><span class="p">(</span><span class="n">fill</span><span class="o">=</span><span class="s2">&#34;white&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">figure_size</span><span class="o">=</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="mi">6</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span></span></span></code></pre></div></div>
<img src="https://opensource.posit.co/blog/2026-06-04_libraries-for-python-polars/index_files/figure-markdown_strict/cell-9-output-1.png" data-fig-alt="Line chart showing daily sales from January 15-21, 2026. Sales increase from around $1,900 to $2,300 over the week, with values labeled on each data point." width="768" height="576" />
<h3 id="sales-by-region-and-product">Sales by region and product
</h3>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># Custom color palette with Posit-inspired colors</span>
</span></span><span class="line"><span class="cl"><span class="n">product_colors</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;Widget A&#34;</span><span class="p">:</span> <span class="s2">&#34;#447099&#34;</span><span class="p">,</span>  <span class="c1"># Blue</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;Widget B&#34;</span><span class="p">:</span> <span class="s2">&#34;#72994e&#34;</span><span class="p">,</span>  <span class="c1"># Green</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;Widget C&#34;</span><span class="p">:</span> <span class="s2">&#34;#c65d47&#34;</span><span class="p">,</span>  <span class="c1"># Rust</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">ggplot</span><span class="p">(</span><span class="n">sales_data</span><span class="p">,</span> <span class="n">aes</span><span class="p">(</span><span class="n">x</span><span class="o">=</span><span class="s2">&#34;region&#34;</span><span class="p">,</span> <span class="n">y</span><span class="o">=</span><span class="s2">&#34;sales&#34;</span><span class="p">,</span> <span class="n">fill</span><span class="o">=</span><span class="s2">&#34;product&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="o">+</span> <span class="n">geom_col</span><span class="p">(</span><span class="n">position</span><span class="o">=</span><span class="n">position_dodge</span><span class="p">(</span><span class="n">width</span><span class="o">=</span><span class="mf">0.8</span><span class="p">),</span> <span class="n">width</span><span class="o">=</span><span class="mf">0.7</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">+</span> <span class="n">scale_fill_manual</span><span class="p">(</span><span class="n">values</span><span class="o">=</span><span class="n">product_colors</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">+</span> <span class="n">scale_y_continuous</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">labels</span><span class="o">=</span><span class="k">lambda</span> <span class="n">l</span><span class="p">:</span> <span class="p">[</span><span class="sa">f</span><span class="s2">&#34;$</span><span class="si">{</span><span class="n">x</span><span class="si">:</span><span class="s2">,.0f</span><span class="si">}</span><span class="s2">&#34;</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">l</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">        <span class="n">limits</span><span class="o">=</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">2500</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">breaks</span><span class="o">=</span><span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">2501</span><span class="p">,</span> <span class="mi">500</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">expand</span><span class="o">=</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">+</span> <span class="n">labs</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">title</span><span class="o">=</span><span class="s2">&#34;Sales Performance by Region&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">subtitle</span><span class="o">=</span><span class="s2">&#34;Product comparison across geographic markets&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">x</span><span class="o">=</span><span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">y</span><span class="o">=</span><span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">fill</span><span class="o">=</span><span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">+</span> <span class="n">theme_minimal</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="o">+</span> <span class="n">theme</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">plot_title</span><span class="o">=</span><span class="n">element_text</span><span class="p">(</span><span class="n">size</span><span class="o">=</span><span class="mi">16</span><span class="p">,</span> <span class="n">weight</span><span class="o">=</span><span class="s2">&#34;bold&#34;</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s2">&#34;#2c3e50&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">plot_subtitle</span><span class="o">=</span><span class="n">element_text</span><span class="p">(</span><span class="n">size</span><span class="o">=</span><span class="mi">11</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s2">&#34;#7f8c8d&#34;</span><span class="p">,</span> <span class="n">margin</span><span class="o">=</span><span class="p">{</span><span class="s2">&#34;b&#34;</span><span class="p">:</span> <span class="mi">15</span><span class="p">}),</span>
</span></span><span class="line"><span class="cl">        <span class="n">axis_title</span><span class="o">=</span><span class="n">element_blank</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">        <span class="n">axis_text_y</span><span class="o">=</span><span class="n">element_text</span><span class="p">(</span><span class="n">size</span><span class="o">=</span><span class="mi">10</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s2">&#34;#666666&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">axis_text_x</span><span class="o">=</span><span class="n">element_text</span><span class="p">(</span><span class="n">size</span><span class="o">=</span><span class="mi">11</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s2">&#34;#333333&#34;</span><span class="p">,</span> <span class="n">weight</span><span class="o">=</span><span class="s2">&#34;bold&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">legend_position</span><span class="o">=</span><span class="s2">&#34;top&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">legend_title</span><span class="o">=</span><span class="n">element_blank</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">        <span class="n">legend_text</span><span class="o">=</span><span class="n">element_text</span><span class="p">(</span><span class="n">size</span><span class="o">=</span><span class="mi">10</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">legend_box_margin</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">panel_grid_major_x</span><span class="o">=</span><span class="n">element_blank</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">        <span class="n">panel_grid_minor</span><span class="o">=</span><span class="n">element_blank</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">        <span class="n">panel_grid_major_y</span><span class="o">=</span><span class="n">element_line</span><span class="p">(</span><span class="n">color</span><span class="o">=</span><span class="s2">&#34;#e0e0e0&#34;</span><span class="p">,</span> <span class="n">size</span><span class="o">=</span><span class="mf">0.5</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">plot_background</span><span class="o">=</span><span class="n">element_rect</span><span class="p">(</span><span class="n">fill</span><span class="o">=</span><span class="s2">&#34;white&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">panel_background</span><span class="o">=</span><span class="n">element_rect</span><span class="p">(</span><span class="n">fill</span><span class="o">=</span><span class="s2">&#34;white&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">figure_size</span><span class="o">=</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="mi">6</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span></span></span></code></pre></div></div>
<img src="https://opensource.posit.co/blog/2026-06-04_libraries-for-python-polars/index_files/figure-markdown_strict/cell-10-output-1.png" data-fig-alt="Grouped bar chart showing sales performance by region (North, South, West) and product (Widget A, B, C). Bars are color-coded by product with Widget A in blue, Widget B in green, and Widget C in rust. West region shows highest sales, particularly for Widget C at approximately $2,300." width="768" height="576" />
<p>Plotnine works seamlessly with Polars DataFrames, no conversion needed! These visualizations can include:</p>
<ul>
<li>Values displayed directly on points for easy reading with <code>geom_text</code></li>
<li>Currency labels, appropriate limits, and controlled breaks with <code>scale_y_continuous</code></li>
<li>Larger, bolder titles with subtle subtitle styling with <code>theme</code></li>
<li>Pure white backgrounds with subtle gray gridlines with <code>theme_minimal</code> (my favorite built-in theme)</li>
</ul>
<p>Again, with full Polars support. There&rsquo;s more about it in the <a href="https://docs.pola.rs/user-guide/misc/visualization/" target="_blank" rel="noopener">Polars documentation for visualization</a>.</p>
<h2 id="ai-powered-insights-with-mall">AI-powered insights with mall
</h2>
<p>Finally, let&rsquo;s use <a href="https://mlverse.github.io/mall/" target="_blank" rel="noopener"><strong>mall</strong></a> to add LLM-powered analysis to our workflow. I used <a href="https://ollama.com/" target="_blank" rel="noopener">Ollama</a> <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> with a local model, but mall works with OpenAI, Anthropic, and other providers through the <a href="https://github.com/cpsievert/chatlas" target="_blank" rel="noopener">chatlas</a> package.</p>
<p>Mall extends Polars DataFrames with an <code>.llm</code> accessor that provides natural language operations. We can use mall to add natural language descriptions to our sales data, rating the performance of each row as &ldquo;low&rdquo;, &ldquo;medium&rdquo;, or &ldquo;high&rdquo;:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">sales_data</span><span class="o">.</span><span class="n">llm</span><span class="o">.</span><span class="n">use</span><span class="p">(</span><span class="s2">&#34;ollama&#34;</span><span class="p">,</span> <span class="s2">&#34;llama3.2&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">sales_with_performance</span> <span class="o">=</span> <span class="n">sales_data</span><span class="o">.</span><span class="n">llm</span><span class="o">.</span><span class="n">classify</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;sales&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">[</span><span class="s2">&#34;high&#34;</span><span class="p">,</span> <span class="s2">&#34;medium&#34;</span><span class="p">,</span> <span class="s2">&#34;low&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="n">pred_name</span><span class="o">=</span><span class="s2">&#34;performance&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">sales_with_performance</span><span class="o">.</span><span class="n">select</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="p">[</span><span class="s2">&#34;date&#34;</span><span class="p">,</span> <span class="s2">&#34;region&#34;</span><span class="p">,</span> <span class="s2">&#34;product&#34;</span><span class="p">,</span> <span class="s2">&#34;sales&#34;</span><span class="p">,</span> <span class="s2">&#34;performance&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">))</span></span></span></code></pre></div></div>
<pre><code>shape: (7, 5)
┌────────────┬────────┬──────────┬───────┬─────────────┐
│ date       ┆ region ┆ product  ┆ sales ┆ performance │
│ ---        ┆ ---    ┆ ---      ┆ ---   ┆ ---         │
│ date       ┆ str    ┆ str      ┆ i64   ┆ str         │
╞════════════╪════════╪══════════╪═══════╪═════════════╡
│ 2026-01-15 ┆ North  ┆ Widget A ┆ 1200  ┆ low         │
│ 2026-01-16 ┆ South  ┆ Widget B ┆ 1800  ┆ low         │
│ 2026-01-17 ┆ North  ┆ Widget A ┆ 1500  ┆ low         │
│ 2026-01-18 ┆ West   ┆ Widget C ┆ 2100  ┆ low         │
│ 2026-01-19 ┆ South  ┆ Widget B ┆ 1650  ┆ low         │
│ 2026-01-20 ┆ North  ┆ Widget A ┆ 1900  ┆ low         │
│ 2026-01-21 ┆ West   ┆ Widget C ┆ 2300  ┆ low         │
└────────────┴────────┴──────────┴───────┴─────────────┘
</code></pre>
<p>Or we can generate custom descriptions for each product:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># mall&#39;s custom() processes a single text column, so first combine the</span>
</span></span><span class="line"><span class="cl"><span class="c1"># relevant fields into one column for the LLM to read.</span>
</span></span><span class="line"><span class="cl"><span class="n">sales_with_description</span> <span class="o">=</span> <span class="n">sales_data</span><span class="o">.</span><span class="n">with_columns</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">pl</span><span class="o">.</span><span class="n">format</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;Product: </span><span class="si">{}</span><span class="s2">, Region: </span><span class="si">{}</span><span class="s2">, Sales: </span><span class="si">{}</span><span class="s2">, Customer rating: </span><span class="si">{}</span><span class="s2">&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">pl</span><span class="o">.</span><span class="n">col</span><span class="p">(</span><span class="s2">&#34;product&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">pl</span><span class="o">.</span><span class="n">col</span><span class="p">(</span><span class="s2">&#34;region&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">pl</span><span class="o">.</span><span class="n">col</span><span class="p">(</span><span class="s2">&#34;sales&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">pl</span><span class="o">.</span><span class="n">col</span><span class="p">(</span><span class="s2">&#34;customer_rating&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span><span class="o">.</span><span class="n">alias</span><span class="p">(</span><span class="s2">&#34;product_info&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span><span class="o">.</span><span class="n">llm</span><span class="o">.</span><span class="n">custom</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;product_info&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">pred_name</span><span class="o">=</span><span class="s2">&#34;description&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">prompt</span><span class="o">=</span><span class="s2">&#34;Based on this product, region, sales amount, and customer rating, create a brief insight about the product&#39;s performance in 15 words or less&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">with</span> <span class="n">pl</span><span class="o">.</span><span class="n">Config</span><span class="p">(</span><span class="n">fmt_str_lengths</span><span class="o">=</span><span class="mi">200</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="n">sales_with_description</span><span class="o">.</span><span class="n">select</span><span class="p">([</span><span class="s2">&#34;product&#34;</span><span class="p">,</span> <span class="s2">&#34;description&#34;</span><span class="p">]))</span></span></span></code></pre></div></div>
<pre><code>shape: (7, 2)
┌──────────┬───────────────────────────────────────────────────────────────────────────────────────┐
│ product  ┆ description                                                                           │
│ ---      ┆ ---                                                                                   │
│ str      ┆ str                                                                                   │
╞══════════╪═══════════════════════════════════════════════════════════════════════════════════════╡
│ Widget A ┆ Widget A performs well in North region with strong customer satisfaction and moderate │
│          ┆ sales growth.                                                                         │
│ Widget B ┆ Widget B performs well in the South region with high customer satisfaction and sales. │
│ Widget A ┆ Widget A performs well in the North region with high sales and customer satisfaction  │
│          ┆ ratings.                                                                              │
│ Widget C ┆ Widget C excels with high sales and exceptional customer satisfaction in Western      │
│          ┆ region markets.                                                                       │
│ Widget B ┆ Widget B performs well with strong sales and high customer satisfaction ratings       │
│          ┆ overall.                                                                              │
│ Widget A ┆ Widget A excels with high sales (1900) and excellent customer reviews (4.8/5 in North │
│          ┆ region).                                                                              │
│ Widget C ┆ Widget C performs well with high sales and exceptional customer satisfaction in       │
│          ┆ Western region.                                                                       │
└──────────┴───────────────────────────────────────────────────────────────────────────────────────┘
</code></pre>
<p>Mall has a bunch of other powerful operations you can use:</p>
<ul>
<li><code>.llm.classify</code> &mdash; Categorize data into predefined labels</li>
<li><code>.llm.sentiment</code> &mdash; Analyze sentiment (positive/negative/neutral)</li>
<li><code>.llm.summarize</code> &mdash; Condense text columns to key points</li>
<li><code>.llm.extract</code> &mdash; Pull specific information from text</li>
<li><code>.llm.translate</code> &mdash; Convert text to another language</li>
<li><code>.llm.verify</code> &mdash; Check if statements are supported by data</li>
</ul>
<p>And not surprisingly, mall keeps everything in Polars format, which means fast, AI-enhanced data operations that fit naturally into your Polars pipelines.</p>
<h2 id="wrapping-up">Wrapping up
</h2>
<p>The Python data ecosystem has embraced Polars, and so has Posit! These four libraries show how we can build complete data workflows without ever leaving the Polars DataFrame format:</p>
<ul>
<li><strong>pointblank</strong> &mdash; Ensure your data quality before analysis begins</li>
<li><strong>Great Tables</strong> &mdash; Create publication-ready tables with rich formatting options</li>
<li><strong>plotnine</strong> &mdash; Build beautiful, reproducible visualizations with the grammar of graphics</li>
<li><strong>mall</strong> &mdash; Integrate LLM capabilities directly into your data pipelines</li>
</ul>
<p>All of these libraries work seamlessly with Polars, so you can stay in the fast, efficient world of Polars from start to finish. Hope you check them out!</p>
<h2 id="learn-more">Learn more
</h2>
<ul>
<li><a href="https://posit-dev.github.io/pointblank/" target="_blank" rel="noopener">pointblank documentation</a></li>
<li><a href="https://posit-dev.github.io/great-tables/" target="_blank" rel="noopener">Great Tables documentation</a></li>
<li><a href="https://plotnine.org/" target="_blank" rel="noopener">plotnine documentation</a></li>
<li><a href="https://mlverse.github.io/mall/" target="_blank" rel="noopener">mall documentation</a></li>
</ul>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>For instructions, please review the <a href="https://posit.co/blog/setting-up-local-llms-for-r-and-python" target="_blank" rel="noopener">Setting up local LLMs for R and Python</a> blog post.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></description>
      <enclosure url="https://opensource.posit.co/blog/2026-06-04_libraries-for-python-polars/images/featured-image.jpg" length="77502" type="image/jpeg" />
    </item>
    <item>
      <title>posit::glimpse() Newsletter – June 2026</title>
      <link>https://opensource.posit.co/blog/2026-06-03_2026-06-glimpse/</link>
      <pubDate>Wed, 03 Jun 2026 00:00:00 +0000</pubDate>
      <guid>https://opensource.posit.co/blog/2026-06-03_2026-06-glimpse/</guid>
      <dc:creator>Isabella Velásquez</dc:creator><description><![CDATA[<blockquote>
<p>Welcome to our newsletter, posit::glimpse()!</p>
<p>If you&rsquo;re currently reading this on our blog, consider subscribing to Product Updates - Open Source on our <a href="https://posit.co/about/subscription-management" target="_blank" rel="noopener">subscription page</a> to receive this newsletter directly in your inbox.</p>
</blockquote>
<p>posit::glimpse() is our roundup of the most important open-source news for Posit’s community! Check out the amazing updates we have in store.</p>
<h2 id="new-home-for-posit-open-source">New home for Posit Open Source
</h2>
<p>For over 15 years, Posit has been building wonderful open source software. To make it easier than ever for you to navigate our tools, documentation, and content, we’ve brought everything together into one central hub.</p>
<p>Head over to <a href="http://opensource.posit.co" target="_blank" rel="noopener">opensource.posit.co</a>, take a look around, and let us know what you think! And if you’re on LinkedIn, give our new <a href="https://www.linkedin.com/showcase/posit-open-source/" target="_blank" rel="noopener">Posit Open Source LinkedIn showcase page</a> a follow.</p>
<center><img src="https://opensource.posit.co/blog/2026-06-03_2026-06-glimpse/images/osw-packages.gif" alt="OSW Packages"></center>
<h2 id="key-product-updates-and-new-releases">Key product updates and new releases
</h2>
<h3 id="bringing-opentelemetry-to-r-in-production">Bringing OpenTelemetry to R in production
</h3>
<p>Posit has now instrumented eight widely-used R packages (Shiny, plumber2, mirai, httr2, ellmer, knitr, testthat, and DBI) with OpenTelemetry support. This change brings production-grade observability to R applications, enabling traces, logs, and metrics with only environment variable configuration (no code changes required).</p>
<ul>
<li>Learn more in the <a href="https://opensource.posit.co/blog/2026-05-07_opentelemetry/" target="_blank" rel="noopener">Bringing OpenTelemetry to R in production</a> blog post.</li>
</ul>
<h3 id="mirai-270-and-mori-020">mirai 2.7.0 and mori 0.2.0
</h3>
<p>mirai 2.7.0 and mori 0.2.0 are now on CRAN, extending R’s async capabilities for production use. mirai adds bounded queues and <code>try_mirai()</code> for memory-safe async, while mori 0.2.0 graduates from experimental status, providing cross-platform shared memory for R processes without data duplication.</p>
<ul>
<li>Read more in the <a href="https://opensource.posit.co/blog/2026-05-12_production-async-r/" target="_blank" rel="noopener">A memory model for production async R: mirai 2.7.0 and mori 0.2.0</a> blog post.</li>
</ul>
<h3 id="quarto-2-design-and-features">Quarto 2 design and features
</h3>
<p>Quarto 2 introduces a custom Markdown parser designed to provide actionable syntax errors, preserve source locations throughout the processing pipeline, and maintain syntax stability across the project’s lifetime. The Quarto team wrote a fascinating article on their thought process behind this implementation.</p>
<ul>
<li>Check out the <a href="https://opensource.posit.co/blog/2026-05-07_quarto-2-parsing/" target="_blank" rel="noopener">Quarto 2: Parsing and source maps</a> post here.</li>
</ul>
<h3 id="new-plotnine-skill">New Plotnine skill
</h3>
<p>A new plotnine skill is now available for AI coding agents, enabling them to generate consistent and runnable plotnine code. The skill follows the Agent Skills standard and works across any compatible agent including Claude Code and Codex.</p>
<ul>
<li>Learn more in the <a href="https://opensource.posit.co/blog/2026-05-05_plotnine-skill-announcement/" target="_blank" rel="noopener">A Plotnine Skill for AI Coding Agents</a> blog post.</li>
</ul>
<h3 id="ten-great-things-we-added-to-great-docs">Ten great things we added to Great Docs
</h3>
<p>Great Docs v0.10.0 marks the tenth release of the Python documentation tool. Learn about its transformation from a simple documentation generator into a comprehensive documentation platform via ten key features added since launch, including LLM integration, cross-reference validation, flexible theming, and enhanced API reference generation.</p>
<ul>
<li>Read the <a href="https://opensource.posit.co/blog/2026-05-13_great-docs-ten-things/" target="_blank" rel="noopener">Ten great things we added to Great Docs</a> blog post.</li>
</ul>
<h3 id="whats-new-in-shiny">What’s new in Shiny
</h3>
<p>bslib 0.11.0 and py-shiny 1.6.0 introduce toolbar components: compact UI elements for fitting buttons, selects, and other controls into card headers/footers, input labels, and text input submit areas. These space-efficient components are particularly valuable for dashboards and AI chat interfaces where screen real estate is at a premium.</p>
<ul>
<li>Get a walkthrough of toolbars in R and Python in the <a href="https://opensource.posit.co/blog/2026-05-26_introducing-toolbars/" target="_blank" rel="noopener">Introducing Toolbars</a> blog post!</li>
</ul>
<h3 id="whats-new-in-positron">What’s new in Positron
</h3>
<p>Positron’s May release includes a lot of highly requested features:</p>
<ul>
<li>Inline output for Quarto</li>
<li>Packages pane</li>
<li>Positron Notebook Editor now in beta</li>
</ul>
<p>For more, check out the <a href="https://opensource.posit.co/blog/2026-05-11_positron-2026-05-release/" target="_blank" rel="noopener">Positron May Release Highlights</a> post and <a href="https://posit.co/positron-updates-signup" target="_blank" rel="noopener">subscribe to Positron emails</a>.</p>
<h2 id="community">Community
</h2>
<h3 id="may-event-roundup">May Event Roundup
</h3>
<p>We were at so many events this month, we can barely keep track! Here’s a quick wrap-up of where the team spent May:</p>
<ul>
<li>Charlotte, François, and Sara talked Quarto, larger than memory data, and AI at R/Medicine 2026</li>
<li>Nick showcased Positron at R Exchange</li>
<li>Wes joined industry leaders to discuss the future of artificial intelligence at AI Council</li>
<li>Emil, Isabella, Jeroen, and Michael reconnected with the Python community at PyCon US</li>
</ul>
<p>And we’re just getting started! <a href="https://opensource.posit.co/events/" target="_blank" rel="noopener">See where we’ll be next</a>, and come say hi.</p>
<h2 id="learning-and-discussion">Learning and discussion
</h2>
<h3 id="in-defense-of-yaml">In Defense of YAML
</h3>
<p>Love YAML? Hate YAML? Rich Iannone and Tomasz Kalinowski dive into the nuances of this finicky configuration format, comparing it to TOML and exploring how the YAML 1.2 specification enables a fast, fully compliant, and simple user experience. Their discussion highlights how the new <a href="https://posit-dev.github.io/py-yaml12/" target="_blank" rel="noopener">py-yaml12</a> package delivers a modern, spec-compliant YAML experience for today’s developers.</p>
<ul>
<li>Read <a href="https://opensource.posit.co/blog/2026-05-21_in-defense-of-yaml/" target="_blank" rel="noopener">In Defense of YAML</a> blog post.</li>
</ul>
<h3 id="agentic-engineering-and-the-lost-art-of-verification">Agentic Engineering and the Lost Art of Verification
</h3>
<p>“I almost don’t read code now,” says Wes McKinney (creator of Pandas). He talks about the new workflows that allow him to review a million lines of AI-generated code in Show Us Your Agent Skills, a new live session by Vanishing Gradients.</p>
<ul>
<li>Listen to the <a href="https://hugobowne.substack.com/p/agentic-engineering-and-the-lost" target="_blank" rel="noopener">podcast</a> and read the <a href="https://hugobowne.substack.com/p/the-agentic-software-factory" target="_blank" rel="noopener">The Agentic Software Factory</a> blog post.</li>
</ul>
<h3 id="comparing-posit-assistant-and-positron-assistant">Comparing Posit Assistant and Positron Assistant
</h3>
<p>Wondering what the difference is between Posit Assistant and Positron Assistant? Sara and Simon dive deep into this topic in their YouTube video:</p>
<div class="w-full aspect-video">
      <iframe
        src="https://www.youtube.com/embed/Y9P2nlFXKnQ"
        class="w-full h-full"
        
        frameborder="0"
        allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
        allowfullscreen></iframe>
    </div>
<p>Sara and Simon have more to share in the AI newsletter! Check out the recent posts that include <a href="https://opensource.posit.co/blog/2026-05-08_ai-newsletter/" target="_blank" rel="noopener">Posit Assistant’s data cleaning mode</a> and <a href="https://opensource.posit.co/blog/2026-05-22_ai-newsletter/" target="_blank" rel="noopener">Gemma 4 as a budget-focused model</a>.</p>
<h3 id="great-docs-on-talk-python">Great Docs on Talk Python
</h3>
<p>Rich and Michael recently joined Michael Kennedy on the Talk Python podcast, where they discussed Great Docs:</p>
<div class="w-full aspect-video">
      <iframe
        src="https://www.youtube.com/embed/rj2hY2Bsi30"
        class="w-full h-full"
        
        frameborder="0"
        allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
        allowfullscreen></iframe>
    </div>
<h3 id="pycascades-2026-videos-are-up">PyCascades 2026 videos are up!
</h3>
<p>Rodrigo Silva Ferreira gave a great talk on “To Notebook or Not to Notebook: Multilingual Workflows for Data Analysis”:</p>
<div class="w-full aspect-video">
      <iframe
        src="https://www.youtube.com/embed/Q4WuuANQRy8"
        class="w-full h-full"
        
        frameborder="0"
        allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
        allowfullscreen></iframe>
    </div>
<h3 id="showcases-from-the-community">Showcases from the community
</h3>









  
  
    
  

  
  
    
  





  


<div class="grid gap-12 items-start mt-12 md:grid-cols-2 ">
  
  
    
    
    
      <div class="prose max-w-none "><p><a href="https://www.linkedin.com/posts/nithinmkp_nithin-m-activity-7459067164256395264-5QC5?utm_source=share&amp;utm_medium=member_desktop&amp;rcm=ACoAAB0DXA0BRYdwbGNKW2-OfIAa3MsVywURURg" target="_blank" rel="noopener">Nithin M</a> created a new website in Quarto! It has beautiful layouts, listings, and aesthetics.</p>
<p><a href="https://nithinmkp.github.io/" target="_blank" rel="noopener">See it here</a>.</p>
</div>
    
  
    
    
    
      <div class="prose max-w-none "><div class="not-prose"><figure>
      <img class="h-auto max-w-full rounded-lg"
        src="https://opensource.posit.co/blog/2026-06-03_2026-06-glimpse/images/image1.png"
        alt="Screenshot of Nithin&rsquo;s Quarto website." 
        loading="lazy"
      >
    </figure></div></div>
    
  
</div>










  
  
    
  

  
  
    
  





  


<div class="grid gap-12 items-start mt-12 md:grid-cols-2 ">
  
  
    
    
    
      <div class="prose max-w-none "><div class="not-prose"><figure>
      <img class="h-auto max-w-full rounded-lg"
        src="https://opensource.posit.co/blog/2026-06-03_2026-06-glimpse/images/image2.png"
        alt="" 
        loading="lazy"
      >
    </figure></div></div>
    
  
    
    
    
      <div class="prose max-w-none "><a href="https://www.linkedin.com/posts/nicolas-foss_rstats-datascience-publichealth-share-7461287476876283904-NVTP/?rcm=ACoAABUe3WEBw4M0V14n5v6VMG53LQM8ulIAraY" target="_blank" rel="noopener">Nicolas Foss</a> shared his {mirai} + {mori} stack for massive public health tables, resulting in a &ldquo;massive 25% to 50% reduction in runtimes across our heaviest processing steps&rdquo;.</div>
    
  
</div>










  
  
    
  

  
  
    
  





  


<div class="grid gap-12 items-start mt-12 md:grid-cols-2 ">
  
  
    
    
    
      <div class="prose max-w-none "><div class="not-prose"><figure>
      <img class="h-auto max-w-full rounded-lg"
        src="https://opensource.posit.co/blog/2026-06-03_2026-06-glimpse/images/image3.png"
        alt="Posit logo and Carfax logo saying 35B vehicle records, 150,000 data sources, 12000&#43; hours saved." 
        loading="lazy"
      >
    </figure></div></div>
    
  
    
    
    
      <div class="prose max-w-none "><p>When your database spans 35 billion vehicle records, manual data workflows stop being an inconvenience and start being a real ceiling on what your team can do. We&rsquo;re proud to spotlight the CARFAX team, who used Posit Team to reclaim 12,000 hours and set a new bar for scalable data work across their organization.</p>
<p><a href="https://posit.co/about/customer-stories/carfax" target="_blank" rel="noopener">Read their story here</a>.</p>
</div>
    
  
</div>

<h2 id="whats-next">What’s next
</h2>
<p>Do you love shorts (not the type you wear, but the vertical video kind)? We’re publishing shorts over at <a href="https://www.youtube.com/channel/UC3xfbCMLCw1Hh4dWop3XtHg/" target="_blank" rel="noopener">YouTube</a>, <a href="https://www.instagram.com/posit.pbc/" target="_blank" rel="noopener">Instagram</a>, <a href="https://www.tiktok.com/@posit_pbc" target="_blank" rel="noopener">TikTok</a>, and our new <a href="https://www.linkedin.com/showcase/posit-open-source" target="_blank" rel="noopener">Posit Open Source LinkedIn showcase page</a>. I&rsquo;d love to hear what short you&rsquo;d like to see next!</p>
<p>We continue to have an excellent line up on the <a href="https://pos.it/dsh" target="_blank" rel="noopener">Data Science Hangout</a> and <a href="https://pos.it/dslab" target="_blank" rel="noopener">Data Science Lab</a>.</p>
<ul>
<li>On June 9, we’re doing a Community Showcase with Kylie Ainslie and Daniel Chen! Let’s learn about how others are using open-source tools in really cool ways.</li>
<li>Jamie Shive, Director of Data Science at the National Hockey League, is the featured leader of the Hangout on June 11 (which could coincide with the Stanley Cup finals!). Let’s talk about everything about hockey and beyond.</li>
</ul>
<p>I’m a real person, and I would love to know how to make the Glimpse newsletter better! Find me on <a href="https://www.linkedin.com/in/ivelasq/" target="_blank" rel="noopener">LinkedIn</a> and <a href="https://bsky.app/profile/ivelasq3.bsky.social" target="_blank" rel="noopener">Bluesky</a>, or email me at isabella [dot] velasquez [at] posit.co.</p>
]]></description>
      <enclosure url="https://opensource.posit.co/blog/2026-06-03_2026-06-glimpse/thumbnail.jpg" length="68574" type="image/jpeg" />
    </item>
    <item>
      <title>Introducing Toolbars: Supercharge your Cards and Inputs</title>
      <link>https://opensource.posit.co/blog/2026-05-26_introducing-toolbars/</link>
      <pubDate>Tue, 26 May 2026 00:00:00 +0000</pubDate>
      <guid>https://opensource.posit.co/blog/2026-05-26_introducing-toolbars/</guid>
      <dc:creator>Liz Nelson</dc:creator><description><![CDATA[<p>You&rsquo;re building a dashboard. It&rsquo;s looking pretty good. But every time you talk to your users, someone comes up with another must-have filter or button. Maybe you&rsquo;re even adding an AI chat feature for the first time and realizing that you&rsquo;ve got less space than it takes to write &ldquo;this text box is smaller than I thought&rdquo; to fit all those buttons and selects.</p>
<p>Good news! Toolbars are here to save you.</p>
<p>Their small size and low-profile aesthetic is perfect for keeping your favorite filters and buttons compact and legible. They&rsquo;re modular and they work in some of your favorite existing components.
Seeing is believing, so let&rsquo;s jump in!</p>
<p>We&rsquo;re going to provide some examples of classic spots where you might want a toolbar.</p>
<div class="callout callout-tip" role="note" aria-label="Tip">
<div class="callout-header">
<span class="callout-title">Short on time?</span>
</div>
<div class="callout-body">
<p>Jump straight to the functionality you&rsquo;re most interested in:</p>
<ul>
<li><a href="#toolbars-in-card-headers--footers">Toolbars in Card Headers &amp; Footers</a></li>
<li><a href="#attaching-controls-to-inputs">Toolbars in Input Labels</a></li>
<li><a href="#complex-text-inputs-with-toolbars">Complex Text Inputs with Toolbars</a></li>
</ul>
</div>
</div>
<div class="callout callout-note" role="note" aria-label="Note">
<div class="callout-header">
<span class="callout-title">Getting toolbars</span>
</div>
<div class="callout-body">
<p>Toolbars ship in <strong>bslib 0.11.0</strong> (R) and <strong>py-shiny 1.6.0</strong> (Python).</p>
<div class="panel-tabset" data-tabset-group="language">
<ul id="tabset-1" class="panel-tabset-tabby">
<li><a data-tabby-default href="#tabset-1-1">R</a></li>
<li><a href="#tabset-1-2">Python</a></li>
</ul>
<div id="tabset-1-1">
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="nf">install.packages</span><span class="p">(</span><span class="s">&#34;bslib&#34;</span><span class="p">)</span></span></span></code></pre></div></div>
</div>
<div id="tabset-1-2">
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">pip install --upgrade shiny</span></span></code></pre></div></div>
</div>
</div>
</div>
</div>
<h2 id="our-example">Our Example
</h2>
<p>We&rsquo;re going to build a version of this app piece by piece:</p>
<script src="https://fast.wistia.com/player.js" async></script>
<script src="https://fast.wistia.com/embed/4o5yqcn2tc.js" async type="module"></script>
<style>wistia-player[media-id='4o5yqcn2tc']:not(:defined) { background: center / contain no-repeat url('https://fast.wistia.com/embed/medias/4o5yqcn2tc/swatch'); display: block; filter: blur(5px); padding-top:67.81%; }</style>
<p><wistia-player media-id="4o5yqcn2tc" aspect="1.4746543778801844"></wistia-player></p>
<h2 id="setup-code">Setup Code
</h2>
<p>Let&rsquo;s get you set up with a basic app with some cards. We&rsquo;ll use this canvas as a starting point for our toolbar components.</p>
<div class="panel-tabset" data-tabset-group="language">
<ul id="tabset-2" class="panel-tabset-tabby">
<li><a data-tabby-default href="#tabset-2-1">R</a></li>
<li><a href="#tabset-2-2">Python</a></li>
</ul>
<div id="tabset-2-1">
<div class="code-block code-with-filename" role="group" aria-labelledby="code-filename-2">
  <div class="code-with-filename-label" id="code-filename-2"><span class="font-mono text-sm">app_1.R</span></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="nf">library</span><span class="p">(</span><span class="n">shiny</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nf">library</span><span class="p">(</span><span class="n">bslib</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">ui</span> <span class="o">&lt;-</span> <span class="nf">page_fluid</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">  <span class="nf">h2</span><span class="p">(</span><span class="s">&#34;Example App&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">  <span class="nf">layout_columns</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="nf">card</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="nf">card_header</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;Toolbar in header &amp; label&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="nf">card_body</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="nf">p</span><span class="p">(</span><span class="s">&#34;Placeholder text.&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="nf">card</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="nf">card_header</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;Toolbar in header, footer, label&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="nf">card_body</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="nf">p</span><span class="p">(</span><span class="s">&#34;More placeholder text.&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="nf">card_footer</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="nf">p</span><span class="p">(</span><span class="s">&#34;Footer content here.&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="n">col_widths</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="m">6</span><span class="p">,</span> <span class="m">6</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">),</span>
</span></span><span class="line"><span class="cl">  <span class="nf">card</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="nf">card_header</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="s">&#34;Toolbar in Text Input Submit Area: Message Composer&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="nf">card_body</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="nf">layout_columns</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="nf">p</span><span class="p">(</span><span class="s">&#34;Placeholder for an input&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="nf">div</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">          <span class="nf">p</span><span class="p">(</span><span class="s">&#34;Placeholder for outputs&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">col_widths</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="m">6</span><span class="p">,</span> <span class="m">6</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">server</span> <span class="o">&lt;-</span> <span class="kr">function</span><span class="p">(</span><span class="n">input</span><span class="p">,</span> <span class="n">output</span><span class="p">,</span> <span class="n">session</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># Placeholder</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">shinyApp</span><span class="p">(</span><span class="n">ui</span><span class="p">,</span> <span class="n">server</span><span class="p">)</span></span></span></code></pre></div></div>
</div>
<div id="tabset-2-2">
<div class="code-block code-with-filename" role="group" aria-labelledby="code-filename-3">
  <div class="code-with-filename-label" id="code-filename-3"><span class="font-mono text-sm">app_1.py</span></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">faicons</span> <span class="kn">import</span> <span class="n">icon_svg</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">shiny</span> <span class="kn">import</span> <span class="n">App</span><span class="p">,</span> <span class="n">Inputs</span><span class="p">,</span> <span class="n">Outputs</span><span class="p">,</span> <span class="n">Session</span><span class="p">,</span> <span class="n">reactive</span><span class="p">,</span> <span class="n">render</span><span class="p">,</span> <span class="n">ui</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">app_ui</span> <span class="o">=</span> <span class="n">ui</span><span class="o">.</span><span class="n">page_fluid</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">ui</span><span class="o">.</span><span class="n">h2</span><span class="p">(</span><span class="s2">&#34;Example App&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="n">ui</span><span class="o">.</span><span class="n">layout_columns</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">card</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">card_header</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;Toolbar in header &amp; label&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">card_body</span><span class="p">(</span><span class="n">ui</span><span class="o">.</span><span class="n">p</span><span class="p">(</span><span class="s2">&#34;Placeholder text.&#34;</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">card</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">card_header</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;Toolbar in header, footer, label&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">card_body</span><span class="p">(</span><span class="n">ui</span><span class="o">.</span><span class="n">p</span><span class="p">(</span><span class="s2">&#34;More placeholder text.&#34;</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">card_footer</span><span class="p">(</span><span class="n">ui</span><span class="o">.</span><span class="n">p</span><span class="p">(</span><span class="s2">&#34;Footer content here.&#34;</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">col_widths</span><span class="o">=</span><span class="p">[</span><span class="mi">6</span><span class="p">,</span> <span class="mi">6</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="n">ui</span><span class="o">.</span><span class="n">card</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">card_header</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;Toolbar in Text Input Submit Area: Message Composer&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">card_body</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">layout_columns</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="n">ui</span><span class="o">.</span><span class="n">p</span><span class="p">(</span><span class="s2">&#34;Placeholder for an input&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                <span class="n">ui</span><span class="o">.</span><span class="n">div</span><span class="p">(</span><span class="n">ui</span><span class="o">.</span><span class="n">p</span><span class="p">(</span><span class="s2">&#34;Placeholder for outputs&#34;</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">                <span class="n">col_widths</span><span class="o">=</span><span class="p">[</span><span class="mi">6</span><span class="p">,</span> <span class="mi">6</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">            <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">server</span><span class="p">(</span><span class="nb">input</span><span class="p">:</span> <span class="n">Inputs</span><span class="p">,</span> <span class="n">output</span><span class="p">:</span> <span class="n">Outputs</span><span class="p">,</span> <span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">pass</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">app</span> <span class="o">=</span> <span class="n">App</span><span class="p">(</span><span class="n">app_ui</span><span class="p">,</span> <span class="n">server</span><span class="p">)</span></span></span></code></pre></div></div>
</div>
</div>
<p>A toolbar on its own is pretty simple. All you have to do is:</p>
<div class="panel-tabset" data-tabset-group="language">
<ul id="tabset-3" class="panel-tabset-tabby">
<li><a data-tabby-default href="#tabset-3-1">R</a></li>
<li><a href="#tabset-3-2">Python</a></li>
</ul>
<div id="tabset-3-1">
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="nf">toolbar</span><span class="p">(</span><span class="n">align</span> <span class="o">=</span> <span class="s">&#34;right&#34;</span><span class="p">)</span></span></span></code></pre></div></div>
</div>
<div id="tabset-3-2">
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">ui</span><span class="o">.</span><span class="n">toolbar</span><span class="p">(</span><span class="n">align</span><span class="o">=</span><span class="s2">&#34;right&#34;</span><span class="p">)</span></span></span></code></pre></div></div>
</div>
</div>
<p>That&rsquo;s just an empty container. The real fun is when we start adding our toolbar inputs and connecting them to card contents.</p>
<h2 id="toolbars-in-card-headers--footers">Toolbars in Card Headers &amp; Footers
</h2>
<p>Toolbars are great for card headers and footers because they provide a clear way to associate a set of controls with a given subset of the app. For example, you can see below that the header toolbar provides controls that clearly correspond to the contents of the card.</p>
<figure>
<img src="https://opensource.posit.co/blog/2026-05-26_introducing-toolbars/card1.png" alt="Sales Data card with a header toolbar holding a region select toolbar dropdown and two toolbar buttons (swap and save), above a sales table." />
<figcaption aria-hidden="true">Sales Data card with a header toolbar holding a region select toolbar dropdown and two toolbar buttons (swap and save), above a sales table.</figcaption>
</figure>
<div class="panel-tabset" data-tabset-group="language">
<ul id="tabset-4" class="panel-tabset-tabby">
<li><a data-tabby-default href="#tabset-4-1">R</a></li>
<li><a href="#tabset-4-2">Python</a></li>
</ul>
<div id="tabset-4-1">
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="nf">card_header</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">  <span class="s">&#34;Sales Data&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nf">toolbar</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">align</span> <span class="o">=</span> <span class="s">&#34;right&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nf">toolbar_input_select</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="n">id</span> <span class="o">=</span> <span class="s">&#34;region_filter&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Region&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">choices</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="s">&#34;All&#34;</span><span class="p">,</span> <span class="s">&#34;North&#34;</span><span class="p">,</span> <span class="s">&#34;South&#34;</span><span class="p">,</span> <span class="s">&#34;East&#34;</span><span class="p">,</span> <span class="s">&#34;West&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="n">icon</span> <span class="o">=</span> <span class="nf">icon</span><span class="p">(</span><span class="s">&#34;filter&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="nf">toolbar_divider</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">    <span class="nf">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="n">id</span> <span class="o">=</span> <span class="s">&#34;save_btn&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Save&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">icon</span> <span class="o">=</span> <span class="nf">icon</span><span class="p">(</span><span class="s">&#34;floppy-disk&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span></span></span></code></pre></div></div>
<details>
<summary>
Complete app code
</summary>
<div class="code-block code-with-filename" role="group" aria-labelledby="code-filename-7">
  <div class="code-with-filename-label" id="code-filename-7"><span class="font-mono text-sm">app_2.R</span></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="nf">library</span><span class="p">(</span><span class="n">shiny</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nf">library</span><span class="p">(</span><span class="n">bslib</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">ui</span> <span class="o">&lt;-</span> <span class="nf">page_fluid</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">  <span class="nf">h2</span><span class="p">(</span><span class="s">&#34;Example App&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">  <span class="nf">layout_columns</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="nf">card</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="nf">card_header</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;Sales Data&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nf">toolbar</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">          <span class="n">align</span> <span class="o">=</span> <span class="s">&#34;right&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nf">toolbar_input_select</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">id</span> <span class="o">=</span> <span class="s">&#34;region_filter&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Region&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">choices</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="s">&#34;All&#34;</span><span class="p">,</span> <span class="s">&#34;North&#34;</span><span class="p">,</span> <span class="s">&#34;South&#34;</span><span class="p">,</span> <span class="s">&#34;East&#34;</span><span class="p">,</span> <span class="s">&#34;West&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">icon</span> <span class="o">=</span> <span class="nf">icon</span><span class="p">(</span><span class="s">&#34;filter&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">          <span class="p">),</span>
</span></span><span class="line"><span class="cl">          <span class="nf">toolbar_divider</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">          <span class="nf">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">id</span> <span class="o">=</span> <span class="s">&#34;save_btn&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Save&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">icon</span> <span class="o">=</span> <span class="nf">icon</span><span class="p">(</span><span class="s">&#34;floppy-disk&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">          <span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="nf">card_body</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="nf">tableOutput</span><span class="p">(</span><span class="s">&#34;sales_table&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="nf">card</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="nf">card_header</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;Toolbar in header, footer, label&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="nf">card_body</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="nf">p</span><span class="p">(</span><span class="s">&#34;More placeholder text.&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="nf">card_footer</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="nf">p</span><span class="p">(</span><span class="s">&#34;Footer content here.&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="n">col_widths</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="m">6</span><span class="p">,</span> <span class="m">6</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">),</span>
</span></span><span class="line"><span class="cl">  <span class="nf">card</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="nf">card_header</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="s">&#34;Toolbar in Text Input Submit Area: Message Composer&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="nf">card_body</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="nf">layout_columns</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="nf">p</span><span class="p">(</span><span class="s">&#34;Placeholder for an input&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="nf">div</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">          <span class="nf">p</span><span class="p">(</span><span class="s">&#34;Placeholder for outputs&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">col_widths</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="m">6</span><span class="p">,</span> <span class="m">6</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">server</span> <span class="o">&lt;-</span> <span class="kr">function</span><span class="p">(</span><span class="n">input</span><span class="p">,</span> <span class="n">output</span><span class="p">,</span> <span class="n">session</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># Handle save button click</span>
</span></span><span class="line"><span class="cl">  <span class="nf">observeEvent</span><span class="p">(</span><span class="n">input</span><span class="o">$</span><span class="n">save_btn</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Change icon to checkmark and show notification</span>
</span></span><span class="line"><span class="cl">    <span class="nf">update_toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="s">&#34;save_btn&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">icon</span> <span class="o">=</span> <span class="nf">icon</span><span class="p">(</span><span class="s">&#34;check&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Saved&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">session</span> <span class="o">=</span> <span class="n">session</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nf">showNotification</span><span class="p">(</span><span class="s">&#34;Saving&#34;</span><span class="p">,</span> <span class="n">type</span> <span class="o">=</span> <span class="s">&#34;message&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># Sample sales data</span>
</span></span><span class="line"><span class="cl">  <span class="n">sales_data</span> <span class="o">&lt;-</span> <span class="nf">reactive</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="nf">data.frame</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="n">Product</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="s">&#34;Widget A&#34;</span><span class="p">,</span> <span class="s">&#34;Widget B&#34;</span><span class="p">,</span> <span class="s">&#34;Widget C&#34;</span><span class="p">,</span> <span class="s">&#34;Widget D&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="n">Region</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="s">&#34;North&#34;</span><span class="p">,</span> <span class="s">&#34;South&#34;</span><span class="p">,</span> <span class="s">&#34;East&#34;</span><span class="p">,</span> <span class="s">&#34;West&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="n">Sales</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="m">1200</span><span class="p">,</span> <span class="m">850</span><span class="p">,</span> <span class="m">1450</span><span class="p">,</span> <span class="m">920</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="n">stringsAsFactors</span> <span class="o">=</span> <span class="kc">FALSE</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># Filtered sales data based on region filter</span>
</span></span><span class="line"><span class="cl">  <span class="n">filtered_data</span> <span class="o">&lt;-</span> <span class="nf">reactive</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="n">data</span> <span class="o">&lt;-</span> <span class="nf">sales_data</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="p">(</span><span class="n">input</span><span class="o">$</span><span class="n">region_filter</span> <span class="o">!=</span> <span class="s">&#34;All&#34;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">data</span> <span class="o">&lt;-</span> <span class="n">data[data</span><span class="o">$</span><span class="n">Region</span> <span class="o">==</span> <span class="n">input</span><span class="o">$</span><span class="n">region_filter</span><span class="p">,</span> <span class="n">]</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">data</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># Render the table</span>
</span></span><span class="line"><span class="cl">  <span class="n">output</span><span class="o">$</span><span class="n">sales_table</span> <span class="o">&lt;-</span> <span class="nf">renderTable</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="n">data</span> <span class="o">&lt;-</span> <span class="nf">filtered_data</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="n">data</span><span class="o">$</span><span class="n">Sales</span> <span class="o">&lt;-</span> <span class="nf">paste0</span><span class="p">(</span><span class="s">&#34;$&#34;</span><span class="p">,</span> <span class="nf">format</span><span class="p">(</span><span class="n">data</span><span class="o">$</span><span class="n">Sales</span><span class="p">,</span> <span class="n">big.mark</span> <span class="o">=</span> <span class="s">&#34;,&#34;</span><span class="p">,</span> <span class="n">trim</span> <span class="o">=</span> <span class="kc">TRUE</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="n">data</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">shinyApp</span><span class="p">(</span><span class="n">ui</span><span class="p">,</span> <span class="n">server</span><span class="p">)</span></span></span></code></pre></div></div>
</details>
</div>
<div id="tabset-4-2">
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">ui</span><span class="o">.</span><span class="n">card_header</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;Sales Data&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">ui</span><span class="o">.</span><span class="n">toolbar</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_input_select</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="nb">id</span><span class="o">=</span><span class="s2">&#34;region_filter&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Region&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">choices</span><span class="o">=</span><span class="p">[</span><span class="s2">&#34;All&#34;</span><span class="p">,</span> <span class="s2">&#34;North&#34;</span><span class="p">,</span> <span class="s2">&#34;South&#34;</span><span class="p">,</span> <span class="s2">&#34;East&#34;</span><span class="p">,</span> <span class="s2">&#34;West&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">            <span class="n">icon</span><span class="o">=</span><span class="n">icon_svg</span><span class="p">(</span><span class="s2">&#34;filter&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_divider</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="nb">id</span><span class="o">=</span><span class="s2">&#34;save_btn&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Save&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">icon</span><span class="o">=</span><span class="n">icon_svg</span><span class="p">(</span><span class="s2">&#34;floppy-disk&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">align</span><span class="o">=</span><span class="s2">&#34;right&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl"><span class="p">),</span></span></span></code></pre></div></div>
<details>
<summary>
Complete app code
</summary>
<div class="code-block code-with-filename" role="group" aria-labelledby="code-filename-9">
  <div class="code-with-filename-label" id="code-filename-9"><span class="font-mono text-sm">app_2.py</span></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">pandas</span> <span class="k">as</span> <span class="nn">pd</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">faicons</span> <span class="kn">import</span> <span class="n">icon_svg</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">shiny</span> <span class="kn">import</span> <span class="n">App</span><span class="p">,</span> <span class="n">Inputs</span><span class="p">,</span> <span class="n">Outputs</span><span class="p">,</span> <span class="n">Session</span><span class="p">,</span> <span class="n">reactive</span><span class="p">,</span> <span class="n">render</span><span class="p">,</span> <span class="n">ui</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">app_ui</span> <span class="o">=</span> <span class="n">ui</span><span class="o">.</span><span class="n">page_fluid</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">ui</span><span class="o">.</span><span class="n">h2</span><span class="p">(</span><span class="s2">&#34;Example App&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="n">ui</span><span class="o">.</span><span class="n">layout_columns</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">card</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">card_header</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;Sales Data&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="n">ui</span><span class="o">.</span><span class="n">toolbar</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                    <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_input_select</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                        <span class="nb">id</span><span class="o">=</span><span class="s2">&#34;region_filter&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Region&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">choices</span><span class="o">=</span><span class="p">[</span><span class="s2">&#34;All&#34;</span><span class="p">,</span> <span class="s2">&#34;North&#34;</span><span class="p">,</span> <span class="s2">&#34;South&#34;</span><span class="p">,</span> <span class="s2">&#34;East&#34;</span><span class="p">,</span> <span class="s2">&#34;West&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">                        <span class="n">icon</span><span class="o">=</span><span class="n">icon_svg</span><span class="p">(</span><span class="s2">&#34;filter&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                    <span class="p">),</span>
</span></span><span class="line"><span class="cl">                    <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_divider</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">                    <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                        <span class="nb">id</span><span class="o">=</span><span class="s2">&#34;save_btn&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Save&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">icon</span><span class="o">=</span><span class="n">icon_svg</span><span class="p">(</span><span class="s2">&#34;floppy-disk&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                    <span class="p">),</span>
</span></span><span class="line"><span class="cl">                    <span class="n">align</span><span class="o">=</span><span class="s2">&#34;right&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">card_body</span><span class="p">(</span><span class="n">ui</span><span class="o">.</span><span class="n">output_table</span><span class="p">(</span><span class="s2">&#34;sales_table&#34;</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">card</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">card_header</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;Toolbar in header, footer, label&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">card_body</span><span class="p">(</span><span class="n">ui</span><span class="o">.</span><span class="n">p</span><span class="p">(</span><span class="s2">&#34;More placeholder text.&#34;</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">card_footer</span><span class="p">(</span><span class="n">ui</span><span class="o">.</span><span class="n">p</span><span class="p">(</span><span class="s2">&#34;Footer content here.&#34;</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">col_widths</span><span class="o">=</span><span class="p">[</span><span class="mi">6</span><span class="p">,</span> <span class="mi">6</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="n">ui</span><span class="o">.</span><span class="n">card</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">card_header</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;Toolbar in Text Input Submit Area: Message Composer&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">card_body</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">layout_columns</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="n">ui</span><span class="o">.</span><span class="n">p</span><span class="p">(</span><span class="s2">&#34;Placeholder for an input&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                <span class="n">ui</span><span class="o">.</span><span class="n">div</span><span class="p">(</span><span class="n">ui</span><span class="o">.</span><span class="n">p</span><span class="p">(</span><span class="s2">&#34;Placeholder for outputs&#34;</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">                <span class="n">col_widths</span><span class="o">=</span><span class="p">[</span><span class="mi">6</span><span class="p">,</span> <span class="mi">6</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">            <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">server</span><span class="p">(</span><span class="nb">input</span><span class="p">:</span> <span class="n">Inputs</span><span class="p">,</span> <span class="n">output</span><span class="p">:</span> <span class="n">Outputs</span><span class="p">,</span> <span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.effect</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.event</span><span class="p">(</span><span class="nb">input</span><span class="o">.</span><span class="n">save_btn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">update</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">update_toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;save_btn&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">icon</span><span class="o">=</span><span class="n">icon_svg</span><span class="p">(</span><span class="s2">&#34;check&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Saved&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">notification_show</span><span class="p">(</span><span class="s2">&#34;Saving&#34;</span><span class="p">,</span> <span class="nb">type</span><span class="o">=</span><span class="s2">&#34;message&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Sample sales data</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.calc</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">sales_data</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">pd</span><span class="o">.</span><span class="n">DataFrame</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;Product&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;Widget A&#34;</span><span class="p">,</span> <span class="s2">&#34;Widget B&#34;</span><span class="p">,</span> <span class="s2">&#34;Widget C&#34;</span><span class="p">,</span> <span class="s2">&#34;Widget D&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;Region&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;North&#34;</span><span class="p">,</span> <span class="s2">&#34;South&#34;</span><span class="p">,</span> <span class="s2">&#34;East&#34;</span><span class="p">,</span> <span class="s2">&#34;West&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;Sales&#34;</span><span class="p">:</span> <span class="p">[</span><span class="mi">1200</span><span class="p">,</span> <span class="mi">850</span><span class="p">,</span> <span class="mi">1450</span><span class="p">,</span> <span class="mi">920</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Filtered sales data based on region filter</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.calc</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">filtered_data</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="n">data</span> <span class="o">=</span> <span class="n">sales_data</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="nb">input</span><span class="o">.</span><span class="n">region_filter</span><span class="p">()</span> <span class="o">!=</span> <span class="s2">&#34;All&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">data</span> <span class="o">=</span> <span class="n">data</span><span class="p">[</span><span class="n">data</span><span class="p">[</span><span class="s2">&#34;Region&#34;</span><span class="p">]</span> <span class="o">==</span> <span class="nb">input</span><span class="o">.</span><span class="n">region_filter</span><span class="p">()]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">data</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Render the table</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@render.table</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">sales_table</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">filtered_data</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">            <span class="o">.</span><span class="n">style</span><span class="o">.</span><span class="n">hide</span><span class="p">(</span><span class="n">axis</span><span class="o">=</span><span class="s2">&#34;index&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="o">.</span><span class="n">format</span><span class="p">({</span><span class="s2">&#34;Sales&#34;</span><span class="p">:</span> <span class="s2">&#34;$</span><span class="si">{:,.0f}</span><span class="s2">&#34;</span><span class="p">})</span>
</span></span><span class="line"><span class="cl">            <span class="o">.</span><span class="n">set_table_styles</span><span class="p">([</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span><span class="s2">&#34;selector&#34;</span><span class="p">:</span> <span class="s2">&#34;th&#34;</span><span class="p">,</span> <span class="s2">&#34;props&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                    <span class="p">(</span><span class="s2">&#34;border-bottom&#34;</span><span class="p">,</span> <span class="s2">&#34;1px solid black&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                    <span class="p">(</span><span class="s2">&#34;padding&#34;</span><span class="p">,</span> <span class="s2">&#34;8px 6px&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                    <span class="p">(</span><span class="s2">&#34;text-align&#34;</span><span class="p">,</span> <span class="s2">&#34;left&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                <span class="p">]},</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span><span class="s2">&#34;selector&#34;</span><span class="p">:</span> <span class="s2">&#34;td&#34;</span><span class="p">,</span> <span class="s2">&#34;props&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                    <span class="p">(</span><span class="s2">&#34;padding&#34;</span><span class="p">,</span> <span class="s2">&#34;6px&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                <span class="p">]},</span>
</span></span><span class="line"><span class="cl">            <span class="p">])</span>
</span></span><span class="line"><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">app</span> <span class="o">=</span> <span class="n">App</span><span class="p">(</span><span class="n">app_ui</span><span class="p">,</span> <span class="n">server</span><span class="p">)</span></span></span></code></pre></div></div>
</details>
</div>
</div>
<h3 id="toolbars-in-card-footers">Toolbars in Card Footers
</h3>
<p>We can also add toolbars in card footers. Footers are a great spot for secondary actions like sharing, exporting, or navigating to related content.</p>
<div class="panel-tabset" data-tabset-group="language">
<ul id="tabset-5" class="panel-tabset-tabby">
<li><a data-tabby-default href="#tabset-5-1">R</a></li>
<li><a href="#tabset-5-2">Python</a></li>
</ul>
<div id="tabset-5-1">
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="nf">card_footer</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">  <span class="nf">toolbar</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">align</span> <span class="o">=</span> <span class="s">&#34;right&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nf">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="n">id</span> <span class="o">=</span> <span class="s">&#34;share_btn&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Share&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">icon</span> <span class="o">=</span> <span class="nf">icon</span><span class="p">(</span><span class="s">&#34;share-nodes&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="n">show_label</span> <span class="o">=</span> <span class="kc">TRUE</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="nf">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="n">id</span> <span class="o">=</span> <span class="s">&#34;export_btn&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Export&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">icon</span> <span class="o">=</span> <span class="nf">icon</span><span class="p">(</span><span class="s">&#34;download&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span></span></span></code></pre></div></div>
<details>
<summary>
Complete app code
</summary>
<div class="code-block code-with-filename" role="group" aria-labelledby="code-filename-11">
  <div class="code-with-filename-label" id="code-filename-11"><span class="font-mono text-sm">app_3.R</span></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="nf">library</span><span class="p">(</span><span class="n">shiny</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nf">library</span><span class="p">(</span><span class="n">bslib</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">ui</span> <span class="o">&lt;-</span> <span class="nf">page_fluid</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">  <span class="nf">h2</span><span class="p">(</span><span class="s">&#34;Example App&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">  <span class="nf">layout_columns</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="nf">card</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="nf">card_header</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;Sales Data&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nf">toolbar</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">          <span class="n">align</span> <span class="o">=</span> <span class="s">&#34;right&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nf">toolbar_input_select</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">id</span> <span class="o">=</span> <span class="s">&#34;region_filter&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Region&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">choices</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="s">&#34;All&#34;</span><span class="p">,</span> <span class="s">&#34;North&#34;</span><span class="p">,</span> <span class="s">&#34;South&#34;</span><span class="p">,</span> <span class="s">&#34;East&#34;</span><span class="p">,</span> <span class="s">&#34;West&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">icon</span> <span class="o">=</span> <span class="nf">icon</span><span class="p">(</span><span class="s">&#34;filter&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">          <span class="p">),</span>
</span></span><span class="line"><span class="cl">          <span class="nf">toolbar_divider</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">          <span class="nf">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">id</span> <span class="o">=</span> <span class="s">&#34;save_btn&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Save&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">icon</span> <span class="o">=</span> <span class="nf">icon</span><span class="p">(</span><span class="s">&#34;floppy-disk&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">          <span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="nf">card_body</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="nf">tableOutput</span><span class="p">(</span><span class="s">&#34;sales_table&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="nf">card</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="nf">card_header</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;Toolbar in header, footer, label&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="nf">card_body</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="nf">p</span><span class="p">(</span><span class="s">&#34;More placeholder text.&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="nf">card_footer</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="nf">toolbar</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">          <span class="n">align</span> <span class="o">=</span> <span class="s">&#34;right&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nf">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">id</span> <span class="o">=</span> <span class="s">&#34;share_btn&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Share&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">icon</span> <span class="o">=</span> <span class="nf">icon</span><span class="p">(</span><span class="s">&#34;share-nodes&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">show_label</span> <span class="o">=</span> <span class="kc">TRUE</span>
</span></span><span class="line"><span class="cl">          <span class="p">),</span>
</span></span><span class="line"><span class="cl">          <span class="nf">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">id</span> <span class="o">=</span> <span class="s">&#34;export_btn&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Export&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">icon</span> <span class="o">=</span> <span class="nf">icon</span><span class="p">(</span><span class="s">&#34;download&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">          <span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="n">col_widths</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="m">6</span><span class="p">,</span> <span class="m">6</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">),</span>
</span></span><span class="line"><span class="cl">  <span class="nf">card</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="nf">card_header</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="s">&#34;Toolbar in Text Input Submit Area: Message Composer&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="nf">card_body</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="nf">layout_columns</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="nf">p</span><span class="p">(</span><span class="s">&#34;Placeholder for an input&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="nf">div</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">          <span class="nf">p</span><span class="p">(</span><span class="s">&#34;Placeholder for outputs&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">col_widths</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="m">6</span><span class="p">,</span> <span class="m">6</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">server</span> <span class="o">&lt;-</span> <span class="kr">function</span><span class="p">(</span><span class="n">input</span><span class="p">,</span> <span class="n">output</span><span class="p">,</span> <span class="n">session</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># Sample sales data</span>
</span></span><span class="line"><span class="cl">  <span class="n">sales_data</span> <span class="o">&lt;-</span> <span class="nf">reactive</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="nf">data.frame</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="n">Product</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="s">&#34;Widget A&#34;</span><span class="p">,</span> <span class="s">&#34;Widget B&#34;</span><span class="p">,</span> <span class="s">&#34;Widget C&#34;</span><span class="p">,</span> <span class="s">&#34;Widget D&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="n">Region</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="s">&#34;North&#34;</span><span class="p">,</span> <span class="s">&#34;South&#34;</span><span class="p">,</span> <span class="s">&#34;East&#34;</span><span class="p">,</span> <span class="s">&#34;West&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="n">Sales</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="m">1200</span><span class="p">,</span> <span class="m">850</span><span class="p">,</span> <span class="m">1450</span><span class="p">,</span> <span class="m">920</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="n">stringsAsFactors</span> <span class="o">=</span> <span class="kc">FALSE</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># Filtered sales data based on region filter</span>
</span></span><span class="line"><span class="cl">  <span class="n">filtered_data</span> <span class="o">&lt;-</span> <span class="nf">reactive</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="n">data</span> <span class="o">&lt;-</span> <span class="nf">sales_data</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="p">(</span><span class="n">input</span><span class="o">$</span><span class="n">region_filter</span> <span class="o">!=</span> <span class="s">&#34;All&#34;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">data</span> <span class="o">&lt;-</span> <span class="n">data[data</span><span class="o">$</span><span class="n">Region</span> <span class="o">==</span> <span class="n">input</span><span class="o">$</span><span class="n">region_filter</span><span class="p">,</span> <span class="n">]</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">data</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># Render the table</span>
</span></span><span class="line"><span class="cl">  <span class="n">output</span><span class="o">$</span><span class="n">sales_table</span> <span class="o">&lt;-</span> <span class="nf">renderTable</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="n">data</span> <span class="o">&lt;-</span> <span class="nf">filtered_data</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="n">data</span><span class="o">$</span><span class="n">Sales</span> <span class="o">&lt;-</span> <span class="nf">paste0</span><span class="p">(</span><span class="s">&#34;$&#34;</span><span class="p">,</span> <span class="nf">format</span><span class="p">(</span><span class="n">data</span><span class="o">$</span><span class="n">Sales</span><span class="p">,</span> <span class="n">big.mark</span> <span class="o">=</span> <span class="s">&#34;,&#34;</span><span class="p">,</span> <span class="n">trim</span> <span class="o">=</span> <span class="kc">TRUE</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="n">data</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">shinyApp</span><span class="p">(</span><span class="n">ui</span><span class="p">,</span> <span class="n">server</span><span class="p">)</span></span></span></code></pre></div></div>
</details>
</div>
<div id="tabset-5-2">
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">ui</span><span class="o">.</span><span class="n">card_footer</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">ui</span><span class="o">.</span><span class="n">toolbar</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="nb">id</span><span class="o">=</span><span class="s2">&#34;share_btn&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Share&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">icon</span><span class="o">=</span><span class="n">icon_svg</span><span class="p">(</span><span class="s2">&#34;share-nodes&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">show_label</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="nb">id</span><span class="o">=</span><span class="s2">&#34;export_btn&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Export&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">icon</span><span class="o">=</span><span class="n">icon_svg</span><span class="p">(</span><span class="s2">&#34;download&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">align</span><span class="o">=</span><span class="s2">&#34;right&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl"><span class="p">),</span></span></span></code></pre></div></div>
<details>
<summary>
Complete app code
</summary>
<div class="code-block code-with-filename" role="group" aria-labelledby="code-filename-13">
  <div class="code-with-filename-label" id="code-filename-13"><span class="font-mono text-sm">app_3.py</span></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">pandas</span> <span class="k">as</span> <span class="nn">pd</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">faicons</span> <span class="kn">import</span> <span class="n">icon_svg</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">shiny</span> <span class="kn">import</span> <span class="n">App</span><span class="p">,</span> <span class="n">Inputs</span><span class="p">,</span> <span class="n">Outputs</span><span class="p">,</span> <span class="n">Session</span><span class="p">,</span> <span class="n">reactive</span><span class="p">,</span> <span class="n">render</span><span class="p">,</span> <span class="n">ui</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">app_ui</span> <span class="o">=</span> <span class="n">ui</span><span class="o">.</span><span class="n">page_fluid</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">ui</span><span class="o">.</span><span class="n">h2</span><span class="p">(</span><span class="s2">&#34;Example App&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="n">ui</span><span class="o">.</span><span class="n">layout_columns</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">card</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">card_header</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;Sales Data&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="n">ui</span><span class="o">.</span><span class="n">toolbar</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                    <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_input_select</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                        <span class="nb">id</span><span class="o">=</span><span class="s2">&#34;region_filter&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Region&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">choices</span><span class="o">=</span><span class="p">[</span><span class="s2">&#34;All&#34;</span><span class="p">,</span> <span class="s2">&#34;North&#34;</span><span class="p">,</span> <span class="s2">&#34;South&#34;</span><span class="p">,</span> <span class="s2">&#34;East&#34;</span><span class="p">,</span> <span class="s2">&#34;West&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">                        <span class="n">icon</span><span class="o">=</span><span class="n">icon_svg</span><span class="p">(</span><span class="s2">&#34;filter&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                    <span class="p">),</span>
</span></span><span class="line"><span class="cl">                    <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_divider</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">                    <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                        <span class="nb">id</span><span class="o">=</span><span class="s2">&#34;save_btn&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Save&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">icon</span><span class="o">=</span><span class="n">icon_svg</span><span class="p">(</span><span class="s2">&#34;floppy-disk&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                    <span class="p">),</span>
</span></span><span class="line"><span class="cl">                    <span class="n">align</span><span class="o">=</span><span class="s2">&#34;right&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">card_body</span><span class="p">(</span><span class="n">ui</span><span class="o">.</span><span class="n">output_table</span><span class="p">(</span><span class="s2">&#34;sales_table&#34;</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">card</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">card_header</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;Toolbar in header, footer, label&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">card_body</span><span class="p">(</span><span class="n">ui</span><span class="o">.</span><span class="n">p</span><span class="p">(</span><span class="s2">&#34;More placeholder text.&#34;</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">card_footer</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="n">ui</span><span class="o">.</span><span class="n">toolbar</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                    <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                        <span class="nb">id</span><span class="o">=</span><span class="s2">&#34;share_btn&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Share&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">icon</span><span class="o">=</span><span class="n">icon_svg</span><span class="p">(</span><span class="s2">&#34;share-nodes&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                        <span class="n">show_label</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="p">),</span>
</span></span><span class="line"><span class="cl">                    <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                        <span class="nb">id</span><span class="o">=</span><span class="s2">&#34;export_btn&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Export&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">icon</span><span class="o">=</span><span class="n">icon_svg</span><span class="p">(</span><span class="s2">&#34;download&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                    <span class="p">),</span>
</span></span><span class="line"><span class="cl">                    <span class="n">align</span><span class="o">=</span><span class="s2">&#34;right&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">col_widths</span><span class="o">=</span><span class="p">[</span><span class="mi">6</span><span class="p">,</span> <span class="mi">6</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="n">ui</span><span class="o">.</span><span class="n">card</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">card_header</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;Toolbar in Text Input Submit Area: Message Composer&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">card_body</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">layout_columns</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="n">ui</span><span class="o">.</span><span class="n">p</span><span class="p">(</span><span class="s2">&#34;Placeholder for an input&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                <span class="n">ui</span><span class="o">.</span><span class="n">div</span><span class="p">(</span><span class="n">ui</span><span class="o">.</span><span class="n">p</span><span class="p">(</span><span class="s2">&#34;Placeholder for outputs&#34;</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">                <span class="n">col_widths</span><span class="o">=</span><span class="p">[</span><span class="mi">6</span><span class="p">,</span> <span class="mi">6</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">            <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">server</span><span class="p">(</span><span class="nb">input</span><span class="p">:</span> <span class="n">Inputs</span><span class="p">,</span> <span class="n">output</span><span class="p">:</span> <span class="n">Outputs</span><span class="p">,</span> <span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Sample sales data</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.calc</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">sales_data</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">pd</span><span class="o">.</span><span class="n">DataFrame</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;Product&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;Widget A&#34;</span><span class="p">,</span> <span class="s2">&#34;Widget B&#34;</span><span class="p">,</span> <span class="s2">&#34;Widget C&#34;</span><span class="p">,</span> <span class="s2">&#34;Widget D&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;Region&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;North&#34;</span><span class="p">,</span> <span class="s2">&#34;South&#34;</span><span class="p">,</span> <span class="s2">&#34;East&#34;</span><span class="p">,</span> <span class="s2">&#34;West&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;Sales&#34;</span><span class="p">:</span> <span class="p">[</span><span class="mi">1200</span><span class="p">,</span> <span class="mi">850</span><span class="p">,</span> <span class="mi">1450</span><span class="p">,</span> <span class="mi">920</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Filtered sales data based on region filter</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.calc</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">filtered_data</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="n">data</span> <span class="o">=</span> <span class="n">sales_data</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="nb">input</span><span class="o">.</span><span class="n">region_filter</span><span class="p">()</span> <span class="o">!=</span> <span class="s2">&#34;All&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">data</span> <span class="o">=</span> <span class="n">data</span><span class="p">[</span><span class="n">data</span><span class="p">[</span><span class="s2">&#34;Region&#34;</span><span class="p">]</span> <span class="o">==</span> <span class="nb">input</span><span class="o">.</span><span class="n">region_filter</span><span class="p">()]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">data</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Render the table</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@render.table</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">sales_table</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">filtered_data</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">            <span class="o">.</span><span class="n">style</span><span class="o">.</span><span class="n">hide</span><span class="p">(</span><span class="n">axis</span><span class="o">=</span><span class="s2">&#34;index&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="o">.</span><span class="n">format</span><span class="p">({</span><span class="s2">&#34;Sales&#34;</span><span class="p">:</span> <span class="s2">&#34;$</span><span class="si">{:,.0f}</span><span class="s2">&#34;</span><span class="p">})</span>
</span></span><span class="line"><span class="cl">            <span class="o">.</span><span class="n">set_table_styles</span><span class="p">([</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span><span class="s2">&#34;selector&#34;</span><span class="p">:</span> <span class="s2">&#34;th&#34;</span><span class="p">,</span> <span class="s2">&#34;props&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                    <span class="p">(</span><span class="s2">&#34;border-bottom&#34;</span><span class="p">,</span> <span class="s2">&#34;1px solid black&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                    <span class="p">(</span><span class="s2">&#34;padding&#34;</span><span class="p">,</span> <span class="s2">&#34;8px 6px&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                    <span class="p">(</span><span class="s2">&#34;text-align&#34;</span><span class="p">,</span> <span class="s2">&#34;left&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                <span class="p">]},</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span><span class="s2">&#34;selector&#34;</span><span class="p">:</span> <span class="s2">&#34;td&#34;</span><span class="p">,</span> <span class="s2">&#34;props&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                    <span class="p">(</span><span class="s2">&#34;padding&#34;</span><span class="p">,</span> <span class="s2">&#34;6px&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                <span class="p">]},</span>
</span></span><span class="line"><span class="cl">            <span class="p">])</span>
</span></span><span class="line"><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">app</span> <span class="o">=</span> <span class="n">App</span><span class="p">(</span><span class="n">app_ui</span><span class="p">,</span> <span class="n">server</span><span class="p">)</span></span></span></code></pre></div></div>
</details>
</div>
</div>
<p>Notice in these examples we use both toolbar buttons and toolbar select inputs. Both of these inputs also have update functions.</p>
<p>For example you might want to update an icon on submit or change out the list of choices depending on user input. We&rsquo;ll talk more about that in the next section.</p>
<h3 id="updating-toolbar-inputs">Updating Toolbar Inputs
</h3>
<p>Toolbar input buttons and selects can be updated dynamically, just like regular Shiny inputs. This is useful for doing things like changing icons after an action (ex. to show a checkmark after saving) or updating available choices based on app state.</p>
<div class="panel-tabset" data-tabset-group="language">
<ul id="tabset-6" class="panel-tabset-tabby">
<li><a data-tabby-default href="#tabset-6-1">R</a></li>
<li><a href="#tabset-6-2">Python</a></li>
</ul>
<div id="tabset-6-1">
<h3 id="a-simple-example-of-updating-a-button">A simple example of updating a button
</h3>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="nf">observeEvent</span><span class="p">(</span><span class="n">input</span><span class="o">$</span><span class="n">save_btn</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># Change icon to checkmark and update label</span>
</span></span><span class="line"><span class="cl">  <span class="nf">update_toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#34;save_btn&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">icon</span> <span class="o">=</span> <span class="nf">icon</span><span class="p">(</span><span class="s">&#34;check&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Saved&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">session</span> <span class="o">=</span> <span class="n">session</span>
</span></span><span class="line"><span class="cl">  <span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="nf">showNotification</span><span class="p">(</span><span class="s">&#34;Changes saved!&#34;</span><span class="p">,</span> <span class="n">type</span> <span class="o">=</span> <span class="s">&#34;message&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">})</span></span></span></code></pre></div></div>
<p>See the complete app code below for more complex examples, including <code>update_toolbar_input_select()</code></p>
<details>
<summary>
Complete app code
</summary>
<div class="code-block code-with-filename" role="group" aria-labelledby="code-filename-15">
  <div class="code-with-filename-label" id="code-filename-15"><span class="font-mono text-sm">app_4.R</span></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="nf">library</span><span class="p">(</span><span class="n">shiny</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nf">library</span><span class="p">(</span><span class="n">bslib</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">ui</span> <span class="o">&lt;-</span> <span class="nf">page_fluid</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">  <span class="nf">h2</span><span class="p">(</span><span class="s">&#34;Example App&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">  <span class="nf">layout_columns</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="nf">card</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="nf">card_header</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;Sales Data&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nf">toolbar</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">          <span class="n">align</span> <span class="o">=</span> <span class="s">&#34;right&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nf">toolbar_input_select</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">id</span> <span class="o">=</span> <span class="s">&#34;data_filter&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Region&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">choices</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="s">&#34;All&#34;</span><span class="p">,</span> <span class="s">&#34;North&#34;</span><span class="p">,</span> <span class="s">&#34;South&#34;</span><span class="p">,</span> <span class="s">&#34;East&#34;</span><span class="p">,</span> <span class="s">&#34;West&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">icon</span> <span class="o">=</span> <span class="nf">icon</span><span class="p">(</span><span class="s">&#34;filter&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">tooltip</span> <span class="o">=</span> <span class="s">&#34;Region&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="p">),</span>
</span></span><span class="line"><span class="cl">          <span class="nf">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">id</span> <span class="o">=</span> <span class="s">&#34;toggle_filter&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Switch to Product&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">icon</span> <span class="o">=</span> <span class="nf">icon</span><span class="p">(</span><span class="s">&#34;right-left&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">tooltip</span> <span class="o">=</span> <span class="s">&#34;Switch to filtering by Product&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="p">),</span>
</span></span><span class="line"><span class="cl">          <span class="nf">toolbar_divider</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">          <span class="nf">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">id</span> <span class="o">=</span> <span class="s">&#34;save_btn&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Save&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">icon</span> <span class="o">=</span> <span class="nf">icon</span><span class="p">(</span><span class="s">&#34;floppy-disk&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">          <span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="nf">card_body</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="nf">tableOutput</span><span class="p">(</span><span class="s">&#34;sales_table&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="nf">card</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="nf">card_header</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;Toolbar in header, footer, label&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="nf">card_body</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="nf">p</span><span class="p">(</span><span class="s">&#34;More placeholder text.&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="nf">card_footer</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="nf">toolbar</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">          <span class="n">align</span> <span class="o">=</span> <span class="s">&#34;right&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nf">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">id</span> <span class="o">=</span> <span class="s">&#34;share_btn&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Share&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">icon</span> <span class="o">=</span> <span class="nf">icon</span><span class="p">(</span><span class="s">&#34;share-nodes&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">show_label</span> <span class="o">=</span> <span class="kc">TRUE</span>
</span></span><span class="line"><span class="cl">          <span class="p">),</span>
</span></span><span class="line"><span class="cl">          <span class="nf">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">id</span> <span class="o">=</span> <span class="s">&#34;export_btn&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Export&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">icon</span> <span class="o">=</span> <span class="nf">icon</span><span class="p">(</span><span class="s">&#34;download&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">          <span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="n">col_widths</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="m">6</span><span class="p">,</span> <span class="m">6</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">),</span>
</span></span><span class="line"><span class="cl">  <span class="nf">card</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="nf">card_header</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="s">&#34;Toolbar in Text Input Submit Area: Message Composer&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="nf">card_body</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="nf">layout_columns</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="nf">p</span><span class="p">(</span><span class="s">&#34;Placeholder for an input&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="nf">div</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">          <span class="nf">p</span><span class="p">(</span><span class="s">&#34;Placeholder for outputs&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">col_widths</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="m">6</span><span class="p">,</span> <span class="m">6</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">server</span> <span class="o">&lt;-</span> <span class="kr">function</span><span class="p">(</span><span class="n">input</span><span class="p">,</span> <span class="n">output</span><span class="p">,</span> <span class="n">session</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># Track current filter mode</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">filter_mode</span> <span class="o">&lt;-</span> <span class="nf">reactiveVal</span><span class="p">(</span><span class="s">&#34;Region&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># Handle save button click</span>
</span></span><span class="line"><span class="cl">  <span class="nf">observeEvent</span><span class="p">(</span><span class="n">input</span><span class="o">$</span><span class="n">save_btn</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">update_toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="s">&#34;save_btn&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">icon</span> <span class="o">=</span> <span class="nf">icon</span><span class="p">(</span><span class="s">&#34;check&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Saved&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nf">showNotification</span><span class="p">(</span><span class="s">&#34;Saving&#34;</span><span class="p">,</span> <span class="n">type</span> <span class="o">=</span> <span class="s">&#34;message&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># Handle toggle filter button click</span>
</span></span><span class="line"><span class="cl">  <span class="nf">observeEvent</span><span class="p">(</span><span class="n">input</span><span class="o">$</span><span class="n">toggle_filter</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="p">(</span><span class="nf">filter_mode</span><span class="p">()</span> <span class="o">==</span> <span class="s">&#34;Region&#34;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nf">filter_mode</span><span class="p">(</span><span class="s">&#34;Product&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="nf">update_toolbar_input_select</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;data_filter&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Product&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">choices</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="s">&#34;All&#34;</span><span class="p">,</span> <span class="s">&#34;Widget A&#34;</span><span class="p">,</span> <span class="s">&#34;Widget B&#34;</span><span class="p">,</span> <span class="s">&#34;Widget C&#34;</span><span class="p">,</span> <span class="s">&#34;Widget D&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">selected</span> <span class="o">=</span> <span class="s">&#34;All&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="nf">update_toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;toggle_filter&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Switch to Region&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="nf">update_tooltip</span><span class="p">(</span><span class="s">&#34;toggle_filter_tooltip&#34;</span><span class="p">,</span> <span class="s">&#34;Switch to filtering by Region&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="nf">update_tooltip</span><span class="p">(</span><span class="s">&#34;data_filter_tooltip&#34;</span><span class="p">,</span> <span class="s">&#34;Product&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="kr">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nf">filter_mode</span><span class="p">(</span><span class="s">&#34;Region&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="nf">update_toolbar_input_select</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;data_filter&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Region&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">choices</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="s">&#34;All&#34;</span><span class="p">,</span> <span class="s">&#34;North&#34;</span><span class="p">,</span> <span class="s">&#34;South&#34;</span><span class="p">,</span> <span class="s">&#34;East&#34;</span><span class="p">,</span> <span class="s">&#34;West&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">selected</span> <span class="o">=</span> <span class="s">&#34;All&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="nf">update_toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;toggle_filter&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Switch to Product&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="nf">update_tooltip</span><span class="p">(</span><span class="s">&#34;toggle_filter_tooltip&#34;</span><span class="p">,</span> <span class="s">&#34;Switch to filtering by Product&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="nf">update_tooltip</span><span class="p">(</span><span class="s">&#34;data_filter_tooltip&#34;</span><span class="p">,</span> <span class="s">&#34;Region&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># Sample sales data</span>
</span></span><span class="line"><span class="cl">  <span class="n">sales_data</span> <span class="o">&lt;-</span> <span class="nf">reactive</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="nf">data.frame</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="n">Product</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="s">&#34;Widget A&#34;</span><span class="p">,</span> <span class="s">&#34;Widget B&#34;</span><span class="p">,</span> <span class="s">&#34;Widget C&#34;</span><span class="p">,</span> <span class="s">&#34;Widget D&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="n">Region</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="s">&#34;North&#34;</span><span class="p">,</span> <span class="s">&#34;South&#34;</span><span class="p">,</span> <span class="s">&#34;East&#34;</span><span class="p">,</span> <span class="s">&#34;West&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="n">Sales</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="m">1200</span><span class="p">,</span> <span class="m">850</span><span class="p">,</span> <span class="m">1450</span><span class="p">,</span> <span class="m">920</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="n">stringsAsFactors</span> <span class="o">=</span> <span class="kc">FALSE</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># Filtered sales data based on current filter</span>
</span></span><span class="line"><span class="cl">  <span class="n">filtered_data</span> <span class="o">&lt;-</span> <span class="nf">reactive</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="n">data</span> <span class="o">&lt;-</span> <span class="nf">sales_data</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="p">(</span><span class="n">input</span><span class="o">$</span><span class="n">data_filter</span> <span class="o">!=</span> <span class="s">&#34;All&#34;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="kr">if</span> <span class="p">(</span><span class="nf">filter_mode</span><span class="p">()</span> <span class="o">==</span> <span class="s">&#34;Region&#34;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">data</span> <span class="o">&lt;-</span> <span class="n">data[data</span><span class="o">$</span><span class="n">Region</span> <span class="o">==</span> <span class="n">input</span><span class="o">$</span><span class="n">data_filter</span><span class="p">,</span> <span class="n">]</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span> <span class="kr">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">data</span> <span class="o">&lt;-</span> <span class="n">data[data</span><span class="o">$</span><span class="n">Product</span> <span class="o">==</span> <span class="n">input</span><span class="o">$</span><span class="n">data_filter</span><span class="p">,</span> <span class="n">]</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">data</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># Render the table</span>
</span></span><span class="line"><span class="cl">  <span class="n">output</span><span class="o">$</span><span class="n">sales_table</span> <span class="o">&lt;-</span> <span class="nf">renderTable</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="n">data</span> <span class="o">&lt;-</span> <span class="nf">filtered_data</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="p">(</span><span class="nf">nrow</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="o">&gt;</span> <span class="m">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">data</span><span class="o">$</span><span class="n">Sales</span> <span class="o">&lt;-</span> <span class="nf">paste0</span><span class="p">(</span><span class="s">&#34;$&#34;</span><span class="p">,</span> <span class="nf">format</span><span class="p">(</span><span class="n">data</span><span class="o">$</span><span class="n">Sales</span><span class="p">,</span> <span class="n">big.mark</span> <span class="o">=</span> <span class="s">&#34;,&#34;</span><span class="p">,</span> <span class="n">trim</span> <span class="o">=</span> <span class="kc">TRUE</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">data</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">shinyApp</span><span class="p">(</span><span class="n">ui</span><span class="p">,</span> <span class="n">server</span><span class="p">)</span></span></span></code></pre></div></div>
</details>
</div>
<div id="tabset-6-2">
<h3 id="a-simple-example-of-updating-a-button-1">A simple example of updating a button
</h3>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="nd">@reactive.effect</span>
</span></span><span class="line"><span class="cl"><span class="nd">@reactive.event</span><span class="p">(</span><span class="nb">input</span><span class="o">.</span><span class="n">save_btn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">update</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">ui</span><span class="o">.</span><span class="n">update_toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;save_btn&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">icon</span><span class="o">=</span><span class="n">icon_svg</span><span class="p">(</span><span class="s2">&#34;check&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Saved&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">ui</span><span class="o">.</span><span class="n">notification_show</span><span class="p">(</span><span class="s2">&#34;Changes saved!&#34;</span><span class="p">,</span> <span class="nb">type</span><span class="o">=</span><span class="s2">&#34;message&#34;</span><span class="p">)</span></span></span></code></pre></div></div>
<p>See the complete app code below for more complex examples, including <code>ui.update_toolbar_input_select()</code></p>
<details>
<summary>
Complete app code
</summary>
<div class="code-block code-with-filename" role="group" aria-labelledby="code-filename-17">
  <div class="code-with-filename-label" id="code-filename-17"><span class="font-mono text-sm">app_4.py</span></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">pandas</span> <span class="k">as</span> <span class="nn">pd</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">faicons</span> <span class="kn">import</span> <span class="n">icon_svg</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">shiny</span> <span class="kn">import</span> <span class="n">App</span><span class="p">,</span> <span class="n">Inputs</span><span class="p">,</span> <span class="n">Outputs</span><span class="p">,</span> <span class="n">Session</span><span class="p">,</span> <span class="n">reactive</span><span class="p">,</span> <span class="n">render</span><span class="p">,</span> <span class="n">ui</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">app_ui</span> <span class="o">=</span> <span class="n">ui</span><span class="o">.</span><span class="n">page_fluid</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">ui</span><span class="o">.</span><span class="n">h2</span><span class="p">(</span><span class="s2">&#34;Example App&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="n">ui</span><span class="o">.</span><span class="n">layout_columns</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">card</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">card_header</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;Sales Data&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="n">ui</span><span class="o">.</span><span class="n">toolbar</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                    <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_input_select</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                        <span class="nb">id</span><span class="o">=</span><span class="s2">&#34;data_filter&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Region&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">choices</span><span class="o">=</span><span class="p">[</span><span class="s2">&#34;All&#34;</span><span class="p">,</span> <span class="s2">&#34;North&#34;</span><span class="p">,</span> <span class="s2">&#34;South&#34;</span><span class="p">,</span> <span class="s2">&#34;East&#34;</span><span class="p">,</span> <span class="s2">&#34;West&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">                        <span class="n">icon</span><span class="o">=</span><span class="n">icon_svg</span><span class="p">(</span><span class="s2">&#34;filter&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                        <span class="n">tooltip</span><span class="o">=</span><span class="s2">&#34;Region&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="p">),</span>
</span></span><span class="line"><span class="cl">                    <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                        <span class="nb">id</span><span class="o">=</span><span class="s2">&#34;toggle_filter&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Switch to Product&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">icon</span><span class="o">=</span><span class="n">icon_svg</span><span class="p">(</span><span class="s2">&#34;right-left&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                        <span class="n">tooltip</span><span class="o">=</span><span class="s2">&#34;Switch to filtering by Product&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="p">),</span>
</span></span><span class="line"><span class="cl">                    <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_divider</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">                    <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                        <span class="nb">id</span><span class="o">=</span><span class="s2">&#34;save_btn&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Save&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">icon</span><span class="o">=</span><span class="n">icon_svg</span><span class="p">(</span><span class="s2">&#34;floppy-disk&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                    <span class="p">),</span>
</span></span><span class="line"><span class="cl">                    <span class="n">align</span><span class="o">=</span><span class="s2">&#34;right&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">card_body</span><span class="p">(</span><span class="n">ui</span><span class="o">.</span><span class="n">output_table</span><span class="p">(</span><span class="s2">&#34;sales_table&#34;</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">card</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">card_header</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;Toolbar in header, footer, label&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">card_body</span><span class="p">(</span><span class="n">ui</span><span class="o">.</span><span class="n">p</span><span class="p">(</span><span class="s2">&#34;More placeholder text.&#34;</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">card_footer</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="n">ui</span><span class="o">.</span><span class="n">toolbar</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                    <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                        <span class="nb">id</span><span class="o">=</span><span class="s2">&#34;share_btn&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Share&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">icon</span><span class="o">=</span><span class="n">icon_svg</span><span class="p">(</span><span class="s2">&#34;share-nodes&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                        <span class="n">show_label</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="p">),</span>
</span></span><span class="line"><span class="cl">                    <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                        <span class="nb">id</span><span class="o">=</span><span class="s2">&#34;export_btn&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Export&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">icon</span><span class="o">=</span><span class="n">icon_svg</span><span class="p">(</span><span class="s2">&#34;download&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                    <span class="p">),</span>
</span></span><span class="line"><span class="cl">                    <span class="n">align</span><span class="o">=</span><span class="s2">&#34;right&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">col_widths</span><span class="o">=</span><span class="p">[</span><span class="mi">6</span><span class="p">,</span> <span class="mi">6</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="n">ui</span><span class="o">.</span><span class="n">card</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">card_header</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;Toolbar in Text Input Submit Area: Message Composer&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">card_body</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">layout_columns</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="n">ui</span><span class="o">.</span><span class="n">p</span><span class="p">(</span><span class="s2">&#34;Placeholder for an input&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                <span class="n">ui</span><span class="o">.</span><span class="n">div</span><span class="p">(</span><span class="n">ui</span><span class="o">.</span><span class="n">p</span><span class="p">(</span><span class="s2">&#34;Placeholder for outputs&#34;</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">                <span class="n">col_widths</span><span class="o">=</span><span class="p">[</span><span class="mi">6</span><span class="p">,</span> <span class="mi">6</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">            <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">server</span><span class="p">(</span><span class="nb">input</span><span class="p">:</span> <span class="n">Inputs</span><span class="p">,</span> <span class="n">output</span><span class="p">:</span> <span class="n">Outputs</span><span class="p">,</span> <span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Track current filter mode</span>
</span></span><span class="line"><span class="cl">    <span class="n">filter_mode</span> <span class="o">=</span> <span class="n">reactive</span><span class="o">.</span><span class="n">value</span><span class="p">(</span><span class="s2">&#34;Region&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.effect</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.event</span><span class="p">(</span><span class="nb">input</span><span class="o">.</span><span class="n">save_btn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">_</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">update_toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;save_btn&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">icon</span><span class="o">=</span><span class="n">icon_svg</span><span class="p">(</span><span class="s2">&#34;check&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Saved&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">notification_show</span><span class="p">(</span><span class="s2">&#34;Saving&#34;</span><span class="p">,</span> <span class="nb">type</span><span class="o">=</span><span class="s2">&#34;message&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Handle toggle filter button click</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.effect</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.event</span><span class="p">(</span><span class="nb">input</span><span class="o">.</span><span class="n">toggle_filter</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">_</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">filter_mode</span><span class="p">()</span> <span class="o">==</span> <span class="s2">&#34;Region&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">filter_mode</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="s2">&#34;Product&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">update_toolbar_input_select</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;data_filter&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Product&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="n">choices</span><span class="o">=</span><span class="p">[</span><span class="s2">&#34;All&#34;</span><span class="p">,</span> <span class="s2">&#34;Widget A&#34;</span><span class="p">,</span> <span class="s2">&#34;Widget B&#34;</span><span class="p">,</span> <span class="s2">&#34;Widget C&#34;</span><span class="p">,</span> <span class="s2">&#34;Widget D&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">                <span class="n">selected</span><span class="o">=</span><span class="s2">&#34;All&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">update_toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;toggle_filter&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Switch to Region&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">update_tooltip</span><span class="p">(</span><span class="s2">&#34;toggle_filter_tooltip&#34;</span><span class="p">,</span> <span class="s2">&#34;Switch to filtering by Region&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">update_tooltip</span><span class="p">(</span><span class="s2">&#34;data_filter_tooltip&#34;</span><span class="p">,</span> <span class="s2">&#34;Product&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">filter_mode</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="s2">&#34;Region&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">update_toolbar_input_select</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;data_filter&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Region&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="n">choices</span><span class="o">=</span><span class="p">[</span><span class="s2">&#34;All&#34;</span><span class="p">,</span> <span class="s2">&#34;North&#34;</span><span class="p">,</span> <span class="s2">&#34;South&#34;</span><span class="p">,</span> <span class="s2">&#34;East&#34;</span><span class="p">,</span> <span class="s2">&#34;West&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">                <span class="n">selected</span><span class="o">=</span><span class="s2">&#34;All&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">update_toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;toggle_filter&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Switch to Product&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">update_tooltip</span><span class="p">(</span><span class="s2">&#34;toggle_filter_tooltip&#34;</span><span class="p">,</span> <span class="s2">&#34;Switch to filtering by Product&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">update_tooltip</span><span class="p">(</span><span class="s2">&#34;data_filter_tooltip&#34;</span><span class="p">,</span> <span class="s2">&#34;Region&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Sample sales data</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.calc</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">sales_data</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">pd</span><span class="o">.</span><span class="n">DataFrame</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;Product&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;Widget A&#34;</span><span class="p">,</span> <span class="s2">&#34;Widget B&#34;</span><span class="p">,</span> <span class="s2">&#34;Widget C&#34;</span><span class="p">,</span> <span class="s2">&#34;Widget D&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;Region&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;North&#34;</span><span class="p">,</span> <span class="s2">&#34;South&#34;</span><span class="p">,</span> <span class="s2">&#34;East&#34;</span><span class="p">,</span> <span class="s2">&#34;West&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;Sales&#34;</span><span class="p">:</span> <span class="p">[</span><span class="mi">1200</span><span class="p">,</span> <span class="mi">850</span><span class="p">,</span> <span class="mi">1450</span><span class="p">,</span> <span class="mi">920</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Filtered sales data based on current filter</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.calc</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">filtered_data</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="n">data</span> <span class="o">=</span> <span class="n">sales_data</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="nb">input</span><span class="o">.</span><span class="n">data_filter</span><span class="p">()</span> <span class="o">!=</span> <span class="s2">&#34;All&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="n">filter_mode</span><span class="p">()</span> <span class="o">==</span> <span class="s2">&#34;Region&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="n">data</span> <span class="o">=</span> <span class="n">data</span><span class="p">[</span><span class="n">data</span><span class="p">[</span><span class="s2">&#34;Region&#34;</span><span class="p">]</span> <span class="o">==</span> <span class="nb">input</span><span class="o">.</span><span class="n">data_filter</span><span class="p">()]</span>
</span></span><span class="line"><span class="cl">            <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="n">data</span> <span class="o">=</span> <span class="n">data</span><span class="p">[</span><span class="n">data</span><span class="p">[</span><span class="s2">&#34;Product&#34;</span><span class="p">]</span> <span class="o">==</span> <span class="nb">input</span><span class="o">.</span><span class="n">data_filter</span><span class="p">()]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">data</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Render the table</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@render.table</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">sales_table</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">filtered_data</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">            <span class="o">.</span><span class="n">style</span><span class="o">.</span><span class="n">hide</span><span class="p">(</span><span class="n">axis</span><span class="o">=</span><span class="s2">&#34;index&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="o">.</span><span class="n">format</span><span class="p">({</span><span class="s2">&#34;Sales&#34;</span><span class="p">:</span> <span class="s2">&#34;$</span><span class="si">{:,.0f}</span><span class="s2">&#34;</span><span class="p">})</span>
</span></span><span class="line"><span class="cl">            <span class="o">.</span><span class="n">set_table_styles</span><span class="p">([</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span><span class="s2">&#34;selector&#34;</span><span class="p">:</span> <span class="s2">&#34;th&#34;</span><span class="p">,</span> <span class="s2">&#34;props&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                    <span class="p">(</span><span class="s2">&#34;border-bottom&#34;</span><span class="p">,</span> <span class="s2">&#34;1px solid black&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                    <span class="p">(</span><span class="s2">&#34;padding&#34;</span><span class="p">,</span> <span class="s2">&#34;8px 6px&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                    <span class="p">(</span><span class="s2">&#34;text-align&#34;</span><span class="p">,</span> <span class="s2">&#34;left&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                <span class="p">]},</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span><span class="s2">&#34;selector&#34;</span><span class="p">:</span> <span class="s2">&#34;td&#34;</span><span class="p">,</span> <span class="s2">&#34;props&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                    <span class="p">(</span><span class="s2">&#34;padding&#34;</span><span class="p">,</span> <span class="s2">&#34;6px&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                <span class="p">]},</span>
</span></span><span class="line"><span class="cl">            <span class="p">])</span>
</span></span><span class="line"><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">app</span> <span class="o">=</span> <span class="n">App</span><span class="p">(</span><span class="n">app_ui</span><span class="p">,</span> <span class="n">server</span><span class="p">)</span></span></span></code></pre></div></div>
</details>
</div>
</div>
<h2 id="attaching-controls-to-inputs">Attaching Controls to Inputs
</h2>
<p>Toolbars aren&rsquo;t just for headers and footers. We can also put them in input labels to attach small controls directly to inputs.
This pattern works well for adding quick actions like preset values or reset buttons next to numeric inputs.</p>
<figure>
<img src="https://opensource.posit.co/blog/2026-05-26_introducing-toolbars/card2.png" alt="Quantity input with an inline toolbar in its label offering preset buttons (10, 50, 100) and a reset button." />
<figcaption aria-hidden="true">Quantity input with an inline toolbar in its label offering preset buttons (10, 50, 100) and a reset button.</figcaption>
</figure>
<p>Here&rsquo;s an example of a quantity input with toolbar buttons for setting preset values and resetting:</p>
<div class="panel-tabset" data-tabset-group="language">
<ul id="tabset-7" class="panel-tabset-tabby">
<li><a data-tabby-default href="#tabset-7-1">R</a></li>
<li><a href="#tabset-7-2">Python</a></li>
</ul>
<div id="tabset-7-1">
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="nf">numericInput</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">  <span class="s">&#34;quantity&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="n">label</span> <span class="o">=</span> <span class="nf">toolbar</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#34;Quantity:&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nf">toolbar_spacer</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">    <span class="nf">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="n">id</span> <span class="o">=</span> <span class="s">&#34;btn_preset_10&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;10&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">show_label</span> <span class="o">=</span> <span class="kc">TRUE</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">tooltip</span> <span class="o">=</span> <span class="s">&#34;Set to 10&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="nf">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="n">id</span> <span class="o">=</span> <span class="s">&#34;btn_preset_50&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;50&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">show_label</span> <span class="o">=</span> <span class="kc">TRUE</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">tooltip</span> <span class="o">=</span> <span class="s">&#34;Set to 50&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="nf">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="n">id</span> <span class="o">=</span> <span class="s">&#34;btn_preset_100&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;100&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">show_label</span> <span class="o">=</span> <span class="kc">TRUE</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">tooltip</span> <span class="o">=</span> <span class="s">&#34;Set to 100&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="nf">toolbar_divider</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">    <span class="nf">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="n">id</span> <span class="o">=</span> <span class="s">&#34;btn_reset&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Reset&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">icon</span> <span class="o">=</span> <span class="nf">icon</span><span class="p">(</span><span class="s">&#34;rotate-left&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="n">tooltip</span> <span class="o">=</span> <span class="s">&#34;Reset to 1&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="n">align</span> <span class="o">=</span> <span class="s">&#34;right&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">),</span>
</span></span><span class="line"><span class="cl">  <span class="n">value</span> <span class="o">=</span> <span class="m">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="n">min</span> <span class="o">=</span> <span class="m">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="n">max</span> <span class="o">=</span> <span class="m">1000</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span></span></span></code></pre></div></div>
<details>
<summary>
Complete app code
</summary>
<div class="code-block code-with-filename" role="group" aria-labelledby="code-filename-19">
  <div class="code-with-filename-label" id="code-filename-19"><span class="font-mono text-sm">app_5.R</span></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="nf">library</span><span class="p">(</span><span class="n">shiny</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nf">library</span><span class="p">(</span><span class="n">bslib</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">ui</span> <span class="o">&lt;-</span> <span class="nf">page_fluid</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">  <span class="nf">h2</span><span class="p">(</span><span class="s">&#34;Example App&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">  <span class="nf">layout_columns</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="nf">card</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="nf">card_header</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;Sales Data&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nf">toolbar</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">          <span class="n">align</span> <span class="o">=</span> <span class="s">&#34;right&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nf">toolbar_input_select</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">id</span> <span class="o">=</span> <span class="s">&#34;data_filter&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Region&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">choices</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="s">&#34;All&#34;</span><span class="p">,</span> <span class="s">&#34;North&#34;</span><span class="p">,</span> <span class="s">&#34;South&#34;</span><span class="p">,</span> <span class="s">&#34;East&#34;</span><span class="p">,</span> <span class="s">&#34;West&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">icon</span> <span class="o">=</span> <span class="nf">icon</span><span class="p">(</span><span class="s">&#34;filter&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">tooltip</span> <span class="o">=</span> <span class="s">&#34;Region&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="p">),</span>
</span></span><span class="line"><span class="cl">          <span class="nf">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">id</span> <span class="o">=</span> <span class="s">&#34;toggle_filter&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Switch to Product&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">icon</span> <span class="o">=</span> <span class="nf">icon</span><span class="p">(</span><span class="s">&#34;right-left&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">tooltip</span> <span class="o">=</span> <span class="s">&#34;Switch to filtering by Product&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="p">),</span>
</span></span><span class="line"><span class="cl">          <span class="nf">toolbar_divider</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">          <span class="nf">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">id</span> <span class="o">=</span> <span class="s">&#34;save_btn&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Save&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">icon</span> <span class="o">=</span> <span class="nf">icon</span><span class="p">(</span><span class="s">&#34;floppy-disk&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">          <span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="nf">card_body</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="nf">tableOutput</span><span class="p">(</span><span class="s">&#34;sales_table&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="nf">card</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="nf">card_header</span><span class="p">(</span><span class="s">&#34;Toolbar in Input Label&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="nf">card_body</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="nf">numericInput</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">          <span class="s">&#34;quantity&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="n">label</span> <span class="o">=</span> <span class="nf">toolbar</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="s">&#34;Quantity:&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nf">toolbar_spacer</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">            <span class="nf">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">              <span class="n">id</span> <span class="o">=</span> <span class="s">&#34;btn_preset_10&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;10&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="n">show_label</span> <span class="o">=</span> <span class="kc">TRUE</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="n">tooltip</span> <span class="o">=</span> <span class="s">&#34;Set to 10&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="nf">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">              <span class="n">id</span> <span class="o">=</span> <span class="s">&#34;btn_preset_50&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;50&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="n">show_label</span> <span class="o">=</span> <span class="kc">TRUE</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="n">tooltip</span> <span class="o">=</span> <span class="s">&#34;Set to 50&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="nf">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">              <span class="n">id</span> <span class="o">=</span> <span class="s">&#34;btn_preset_100&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;100&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="n">show_label</span> <span class="o">=</span> <span class="kc">TRUE</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="n">tooltip</span> <span class="o">=</span> <span class="s">&#34;Set to 100&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="nf">toolbar_divider</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">            <span class="nf">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">              <span class="n">id</span> <span class="o">=</span> <span class="s">&#34;btn_reset&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Reset&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="n">icon</span> <span class="o">=</span> <span class="nf">icon</span><span class="p">(</span><span class="s">&#34;rotate-left&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">              <span class="n">tooltip</span> <span class="o">=</span> <span class="s">&#34;Reset to 1&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">align</span> <span class="o">=</span> <span class="s">&#34;right&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="p">),</span>
</span></span><span class="line"><span class="cl">          <span class="n">value</span> <span class="o">=</span> <span class="m">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="n">min</span> <span class="o">=</span> <span class="m">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="n">max</span> <span class="o">=</span> <span class="m">1000</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="nf">textOutput</span><span class="p">(</span><span class="s">&#34;quantity_output&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="n">col_widths</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="m">6</span><span class="p">,</span> <span class="m">6</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">),</span>
</span></span><span class="line"><span class="cl">  <span class="nf">card</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="nf">card_header</span><span class="p">(</span><span class="s">&#34;Toolbar in Text Input Submit Area: Message Composer&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="nf">card_body</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="nf">layout_columns</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="nf">p</span><span class="p">(</span><span class="s">&#34;Placeholder for an input&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="nf">div</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">          <span class="nf">p</span><span class="p">(</span><span class="s">&#34;Placeholder for outputs&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">col_widths</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="m">6</span><span class="p">,</span> <span class="m">6</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">server</span> <span class="o">&lt;-</span> <span class="kr">function</span><span class="p">(</span><span class="n">input</span><span class="p">,</span> <span class="n">output</span><span class="p">,</span> <span class="n">session</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># Track current filter mode</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">filter_mode</span> <span class="o">&lt;-</span> <span class="nf">reactiveVal</span><span class="p">(</span><span class="s">&#34;Region&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># Handle save button click</span>
</span></span><span class="line"><span class="cl">  <span class="nf">observeEvent</span><span class="p">(</span><span class="n">input</span><span class="o">$</span><span class="n">save_btn</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">update_toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="s">&#34;save_btn&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">icon</span> <span class="o">=</span> <span class="nf">icon</span><span class="p">(</span><span class="s">&#34;check&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Saved&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nf">showNotification</span><span class="p">(</span><span class="s">&#34;Saving&#34;</span><span class="p">,</span> <span class="n">type</span> <span class="o">=</span> <span class="s">&#34;message&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># Handle toggle filter button click</span>
</span></span><span class="line"><span class="cl">  <span class="nf">observeEvent</span><span class="p">(</span><span class="n">input</span><span class="o">$</span><span class="n">toggle_filter</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="p">(</span><span class="nf">filter_mode</span><span class="p">()</span> <span class="o">==</span> <span class="s">&#34;Region&#34;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nf">filter_mode</span><span class="p">(</span><span class="s">&#34;Product&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="nf">update_toolbar_input_select</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;data_filter&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Product&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">choices</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="s">&#34;All&#34;</span><span class="p">,</span> <span class="s">&#34;Widget A&#34;</span><span class="p">,</span> <span class="s">&#34;Widget B&#34;</span><span class="p">,</span> <span class="s">&#34;Widget C&#34;</span><span class="p">,</span> <span class="s">&#34;Widget D&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">selected</span> <span class="o">=</span> <span class="s">&#34;All&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="nf">update_toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;toggle_filter&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Switch to Region&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="nf">update_tooltip</span><span class="p">(</span><span class="s">&#34;toggle_filter_tooltip&#34;</span><span class="p">,</span> <span class="s">&#34;Switch to filtering by Region&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="nf">update_tooltip</span><span class="p">(</span><span class="s">&#34;data_filter_tooltip&#34;</span><span class="p">,</span> <span class="s">&#34;Product&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="kr">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nf">filter_mode</span><span class="p">(</span><span class="s">&#34;Region&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="nf">update_toolbar_input_select</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;data_filter&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Region&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">choices</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="s">&#34;All&#34;</span><span class="p">,</span> <span class="s">&#34;North&#34;</span><span class="p">,</span> <span class="s">&#34;South&#34;</span><span class="p">,</span> <span class="s">&#34;East&#34;</span><span class="p">,</span> <span class="s">&#34;West&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">selected</span> <span class="o">=</span> <span class="s">&#34;All&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="nf">update_toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;toggle_filter&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Switch to Product&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="nf">update_tooltip</span><span class="p">(</span><span class="s">&#34;toggle_filter_tooltip&#34;</span><span class="p">,</span> <span class="s">&#34;Switch to filtering by Product&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="nf">update_tooltip</span><span class="p">(</span><span class="s">&#34;data_filter_tooltip&#34;</span><span class="p">,</span> <span class="s">&#34;Region&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># Handle numeric input preset buttons</span>
</span></span><span class="line"><span class="cl">  <span class="nf">observeEvent</span><span class="p">(</span><span class="n">input</span><span class="o">$</span><span class="n">btn_preset_10</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">updateNumericInput</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="s">&#34;quantity&#34;</span><span class="p">,</span> <span class="n">value</span> <span class="o">=</span> <span class="m">10</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nf">observeEvent</span><span class="p">(</span><span class="n">input</span><span class="o">$</span><span class="n">btn_preset_50</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">updateNumericInput</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="s">&#34;quantity&#34;</span><span class="p">,</span> <span class="n">value</span> <span class="o">=</span> <span class="m">50</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nf">observeEvent</span><span class="p">(</span><span class="n">input</span><span class="o">$</span><span class="n">btn_preset_100</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">updateNumericInput</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="s">&#34;quantity&#34;</span><span class="p">,</span> <span class="n">value</span> <span class="o">=</span> <span class="m">100</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nf">observeEvent</span><span class="p">(</span><span class="n">input</span><span class="o">$</span><span class="n">btn_reset</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">updateNumericInput</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="s">&#34;quantity&#34;</span><span class="p">,</span> <span class="n">value</span> <span class="o">=</span> <span class="m">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">output</span><span class="o">$</span><span class="n">quantity_output</span> <span class="o">&lt;-</span> <span class="nf">renderText</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="nf">paste</span><span class="p">(</span><span class="s">&#34;Current quantity:&#34;</span><span class="p">,</span> <span class="n">input</span><span class="o">$</span><span class="n">quantity</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># Sample sales data</span>
</span></span><span class="line"><span class="cl">  <span class="n">sales_data</span> <span class="o">&lt;-</span> <span class="nf">reactive</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="nf">data.frame</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="n">Product</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="s">&#34;Widget A&#34;</span><span class="p">,</span> <span class="s">&#34;Widget B&#34;</span><span class="p">,</span> <span class="s">&#34;Widget C&#34;</span><span class="p">,</span> <span class="s">&#34;Widget D&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="n">Region</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="s">&#34;North&#34;</span><span class="p">,</span> <span class="s">&#34;South&#34;</span><span class="p">,</span> <span class="s">&#34;East&#34;</span><span class="p">,</span> <span class="s">&#34;West&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="n">Sales</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="m">1200</span><span class="p">,</span> <span class="m">850</span><span class="p">,</span> <span class="m">1450</span><span class="p">,</span> <span class="m">920</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="n">stringsAsFactors</span> <span class="o">=</span> <span class="kc">FALSE</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># Filtered sales data based on current filter</span>
</span></span><span class="line"><span class="cl">  <span class="n">filtered_data</span> <span class="o">&lt;-</span> <span class="nf">reactive</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="n">data</span> <span class="o">&lt;-</span> <span class="nf">sales_data</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="p">(</span><span class="n">input</span><span class="o">$</span><span class="n">data_filter</span> <span class="o">!=</span> <span class="s">&#34;All&#34;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="kr">if</span> <span class="p">(</span><span class="nf">filter_mode</span><span class="p">()</span> <span class="o">==</span> <span class="s">&#34;Region&#34;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">data</span> <span class="o">&lt;-</span> <span class="n">data[data</span><span class="o">$</span><span class="n">Region</span> <span class="o">==</span> <span class="n">input</span><span class="o">$</span><span class="n">data_filter</span><span class="p">,</span> <span class="n">]</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span> <span class="kr">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">data</span> <span class="o">&lt;-</span> <span class="n">data[data</span><span class="o">$</span><span class="n">Product</span> <span class="o">==</span> <span class="n">input</span><span class="o">$</span><span class="n">data_filter</span><span class="p">,</span> <span class="n">]</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">data</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># Render the table</span>
</span></span><span class="line"><span class="cl">  <span class="n">output</span><span class="o">$</span><span class="n">sales_table</span> <span class="o">&lt;-</span> <span class="nf">renderTable</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="n">data</span> <span class="o">&lt;-</span> <span class="nf">filtered_data</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="p">(</span><span class="nf">nrow</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="o">&gt;</span> <span class="m">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">data</span><span class="o">$</span><span class="n">Sales</span> <span class="o">&lt;-</span> <span class="nf">paste0</span><span class="p">(</span><span class="s">&#34;$&#34;</span><span class="p">,</span> <span class="nf">format</span><span class="p">(</span><span class="n">data</span><span class="o">$</span><span class="n">Sales</span><span class="p">,</span> <span class="n">big.mark</span> <span class="o">=</span> <span class="s">&#34;,&#34;</span><span class="p">,</span> <span class="n">trim</span> <span class="o">=</span> <span class="kc">TRUE</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">data</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">shinyApp</span><span class="p">(</span><span class="n">ui</span><span class="p">,</span> <span class="n">server</span><span class="p">)</span></span></span></code></pre></div></div>
</details>
</div>
<div id="tabset-7-2">
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">ui</span><span class="o">.</span><span class="n">input_numeric</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;quantity&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">label</span><span class="o">=</span><span class="n">ui</span><span class="o">.</span><span class="n">toolbar</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;Quantity:&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_spacer</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;btn_preset_10&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">label</span><span class="o">=</span><span class="s2">&#34;10&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">show_label</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">tooltip</span><span class="o">=</span><span class="s2">&#34;Set to 10&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;btn_preset_50&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">label</span><span class="o">=</span><span class="s2">&#34;50&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">show_label</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">tooltip</span><span class="o">=</span><span class="s2">&#34;Set to 50&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;btn_preset_100&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">label</span><span class="o">=</span><span class="s2">&#34;100&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">show_label</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">tooltip</span><span class="o">=</span><span class="s2">&#34;Set to 100&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_divider</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;btn_reset&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Reset&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">icon</span><span class="o">=</span><span class="n">icon_svg</span><span class="p">(</span><span class="s2">&#34;rotate-left&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">tooltip</span><span class="o">=</span><span class="s2">&#34;Reset to 1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">align</span><span class="o">=</span><span class="s2">&#34;right&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="n">value</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nb">min</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nb">max</span><span class="o">=</span><span class="mi">1000</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span></span></span></code></pre></div></div>
<details>
<summary>
Complete app code
</summary>
<div class="code-block code-with-filename" role="group" aria-labelledby="code-filename-21">
  <div class="code-with-filename-label" id="code-filename-21"><span class="font-mono text-sm">app_5.py</span></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">pandas</span> <span class="k">as</span> <span class="nn">pd</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">faicons</span> <span class="kn">import</span> <span class="n">icon_svg</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">shiny</span> <span class="kn">import</span> <span class="n">App</span><span class="p">,</span> <span class="n">Inputs</span><span class="p">,</span> <span class="n">Outputs</span><span class="p">,</span> <span class="n">Session</span><span class="p">,</span> <span class="n">reactive</span><span class="p">,</span> <span class="n">render</span><span class="p">,</span> <span class="n">ui</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">app_ui</span> <span class="o">=</span> <span class="n">ui</span><span class="o">.</span><span class="n">page_fluid</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">ui</span><span class="o">.</span><span class="n">h2</span><span class="p">(</span><span class="s2">&#34;Example App&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="n">ui</span><span class="o">.</span><span class="n">layout_columns</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">card</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">card_header</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;Sales Data&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="n">ui</span><span class="o">.</span><span class="n">toolbar</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                    <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_input_select</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                        <span class="nb">id</span><span class="o">=</span><span class="s2">&#34;data_filter&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Region&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">choices</span><span class="o">=</span><span class="p">[</span><span class="s2">&#34;All&#34;</span><span class="p">,</span> <span class="s2">&#34;North&#34;</span><span class="p">,</span> <span class="s2">&#34;South&#34;</span><span class="p">,</span> <span class="s2">&#34;East&#34;</span><span class="p">,</span> <span class="s2">&#34;West&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">                        <span class="n">icon</span><span class="o">=</span><span class="n">icon_svg</span><span class="p">(</span><span class="s2">&#34;filter&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                        <span class="n">tooltip</span><span class="o">=</span><span class="s2">&#34;Region&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="p">),</span>
</span></span><span class="line"><span class="cl">                    <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                        <span class="nb">id</span><span class="o">=</span><span class="s2">&#34;toggle_filter&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Switch to Product&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">icon</span><span class="o">=</span><span class="n">icon_svg</span><span class="p">(</span><span class="s2">&#34;right-left&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                        <span class="n">tooltip</span><span class="o">=</span><span class="s2">&#34;Switch to filtering by Product&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="p">),</span>
</span></span><span class="line"><span class="cl">                    <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_divider</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">                    <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                        <span class="nb">id</span><span class="o">=</span><span class="s2">&#34;save_btn&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Save&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">icon</span><span class="o">=</span><span class="n">icon_svg</span><span class="p">(</span><span class="s2">&#34;floppy-disk&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                    <span class="p">),</span>
</span></span><span class="line"><span class="cl">                    <span class="n">align</span><span class="o">=</span><span class="s2">&#34;right&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">card_body</span><span class="p">(</span><span class="n">ui</span><span class="o">.</span><span class="n">output_table</span><span class="p">(</span><span class="s2">&#34;sales_table&#34;</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">card</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">card_header</span><span class="p">(</span><span class="s2">&#34;Toolbar in Input Label&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">card_body</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="n">ui</span><span class="o">.</span><span class="n">input_numeric</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                    <span class="s2">&#34;quantity&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="n">label</span><span class="o">=</span><span class="n">ui</span><span class="o">.</span><span class="n">toolbar</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;Quantity:&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_spacer</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">                        <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                            <span class="s2">&#34;btn_preset_10&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="n">label</span><span class="o">=</span><span class="s2">&#34;10&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="n">show_label</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="n">tooltip</span><span class="o">=</span><span class="s2">&#34;Set to 10&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="p">),</span>
</span></span><span class="line"><span class="cl">                        <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                            <span class="s2">&#34;btn_preset_50&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="n">label</span><span class="o">=</span><span class="s2">&#34;50&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="n">show_label</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="n">tooltip</span><span class="o">=</span><span class="s2">&#34;Set to 50&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="p">),</span>
</span></span><span class="line"><span class="cl">                        <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                            <span class="s2">&#34;btn_preset_100&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="n">label</span><span class="o">=</span><span class="s2">&#34;100&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="n">show_label</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="n">tooltip</span><span class="o">=</span><span class="s2">&#34;Set to 100&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="p">),</span>
</span></span><span class="line"><span class="cl">                        <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_divider</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">                        <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                            <span class="s2">&#34;btn_reset&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Reset&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="n">icon</span><span class="o">=</span><span class="n">icon_svg</span><span class="p">(</span><span class="s2">&#34;rotate-left&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                            <span class="n">tooltip</span><span class="o">=</span><span class="s2">&#34;Reset to 1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="p">),</span>
</span></span><span class="line"><span class="cl">                        <span class="n">align</span><span class="o">=</span><span class="s2">&#34;right&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="p">),</span>
</span></span><span class="line"><span class="cl">                    <span class="n">value</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nb">min</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nb">max</span><span class="o">=</span><span class="mi">1000</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="p">),</span>
</span></span><span class="line"><span class="cl">                <span class="n">ui</span><span class="o">.</span><span class="n">output_text</span><span class="p">(</span><span class="s2">&#34;quantity_output&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">col_widths</span><span class="o">=</span><span class="p">[</span><span class="mi">6</span><span class="p">,</span> <span class="mi">6</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="n">ui</span><span class="o">.</span><span class="n">card</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">card_header</span><span class="p">(</span><span class="s2">&#34;Toolbar in Text Input Submit Area: Message Composer&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">card_body</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">layout_columns</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="n">ui</span><span class="o">.</span><span class="n">p</span><span class="p">(</span><span class="s2">&#34;Placeholder for an input&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                <span class="n">ui</span><span class="o">.</span><span class="n">div</span><span class="p">(</span><span class="n">ui</span><span class="o">.</span><span class="n">p</span><span class="p">(</span><span class="s2">&#34;Placeholder for outputs&#34;</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">                <span class="n">col_widths</span><span class="o">=</span><span class="p">[</span><span class="mi">6</span><span class="p">,</span> <span class="mi">6</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">            <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">server</span><span class="p">(</span><span class="nb">input</span><span class="p">:</span> <span class="n">Inputs</span><span class="p">,</span> <span class="n">output</span><span class="p">:</span> <span class="n">Outputs</span><span class="p">,</span> <span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Track current filter mode</span>
</span></span><span class="line"><span class="cl">    <span class="n">filter_mode</span> <span class="o">=</span> <span class="n">reactive</span><span class="o">.</span><span class="n">value</span><span class="p">(</span><span class="s2">&#34;Region&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.effect</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.event</span><span class="p">(</span><span class="nb">input</span><span class="o">.</span><span class="n">save_btn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">_</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">update_toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;save_btn&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">icon</span><span class="o">=</span><span class="n">icon_svg</span><span class="p">(</span><span class="s2">&#34;check&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Saved&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">notification_show</span><span class="p">(</span><span class="s2">&#34;Saving&#34;</span><span class="p">,</span> <span class="nb">type</span><span class="o">=</span><span class="s2">&#34;message&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Handle toggle filter button click</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.effect</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.event</span><span class="p">(</span><span class="nb">input</span><span class="o">.</span><span class="n">toggle_filter</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">_</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">filter_mode</span><span class="p">()</span> <span class="o">==</span> <span class="s2">&#34;Region&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">filter_mode</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="s2">&#34;Product&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">update_toolbar_input_select</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;data_filter&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Product&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="n">choices</span><span class="o">=</span><span class="p">[</span><span class="s2">&#34;All&#34;</span><span class="p">,</span> <span class="s2">&#34;Widget A&#34;</span><span class="p">,</span> <span class="s2">&#34;Widget B&#34;</span><span class="p">,</span> <span class="s2">&#34;Widget C&#34;</span><span class="p">,</span> <span class="s2">&#34;Widget D&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">                <span class="n">selected</span><span class="o">=</span><span class="s2">&#34;All&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">update_toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;toggle_filter&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Switch to Region&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">update_tooltip</span><span class="p">(</span><span class="s2">&#34;toggle_filter_tooltip&#34;</span><span class="p">,</span> <span class="s2">&#34;Switch to filtering by Region&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">update_tooltip</span><span class="p">(</span><span class="s2">&#34;data_filter_tooltip&#34;</span><span class="p">,</span> <span class="s2">&#34;Product&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">filter_mode</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="s2">&#34;Region&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">update_toolbar_input_select</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;data_filter&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Region&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="n">choices</span><span class="o">=</span><span class="p">[</span><span class="s2">&#34;All&#34;</span><span class="p">,</span> <span class="s2">&#34;North&#34;</span><span class="p">,</span> <span class="s2">&#34;South&#34;</span><span class="p">,</span> <span class="s2">&#34;East&#34;</span><span class="p">,</span> <span class="s2">&#34;West&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">                <span class="n">selected</span><span class="o">=</span><span class="s2">&#34;All&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">update_toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;toggle_filter&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Switch to Product&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">update_tooltip</span><span class="p">(</span><span class="s2">&#34;toggle_filter_tooltip&#34;</span><span class="p">,</span> <span class="s2">&#34;Switch to filtering by Product&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">update_tooltip</span><span class="p">(</span><span class="s2">&#34;data_filter_tooltip&#34;</span><span class="p">,</span> <span class="s2">&#34;Region&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Handle numeric input preset buttons</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.effect</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.event</span><span class="p">(</span><span class="nb">input</span><span class="o">.</span><span class="n">btn_preset_10</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">_</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">update_numeric</span><span class="p">(</span><span class="s2">&#34;quantity&#34;</span><span class="p">,</span> <span class="n">value</span><span class="o">=</span><span class="mi">10</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.effect</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.event</span><span class="p">(</span><span class="nb">input</span><span class="o">.</span><span class="n">btn_preset_50</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">_</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">update_numeric</span><span class="p">(</span><span class="s2">&#34;quantity&#34;</span><span class="p">,</span> <span class="n">value</span><span class="o">=</span><span class="mi">50</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.effect</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.event</span><span class="p">(</span><span class="nb">input</span><span class="o">.</span><span class="n">btn_preset_100</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">_</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">update_numeric</span><span class="p">(</span><span class="s2">&#34;quantity&#34;</span><span class="p">,</span> <span class="n">value</span><span class="o">=</span><span class="mi">100</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.effect</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.event</span><span class="p">(</span><span class="nb">input</span><span class="o">.</span><span class="n">btn_reset</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">_</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">update_numeric</span><span class="p">(</span><span class="s2">&#34;quantity&#34;</span><span class="p">,</span> <span class="n">value</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@render.text</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">quantity_output</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="sa">f</span><span class="s2">&#34;Current quantity: </span><span class="si">{</span><span class="nb">input</span><span class="o">.</span><span class="n">quantity</span><span class="p">()</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Sample sales data</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.calc</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">sales_data</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">pd</span><span class="o">.</span><span class="n">DataFrame</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;Product&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;Widget A&#34;</span><span class="p">,</span> <span class="s2">&#34;Widget B&#34;</span><span class="p">,</span> <span class="s2">&#34;Widget C&#34;</span><span class="p">,</span> <span class="s2">&#34;Widget D&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;Region&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;North&#34;</span><span class="p">,</span> <span class="s2">&#34;South&#34;</span><span class="p">,</span> <span class="s2">&#34;East&#34;</span><span class="p">,</span> <span class="s2">&#34;West&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;Sales&#34;</span><span class="p">:</span> <span class="p">[</span><span class="mi">1200</span><span class="p">,</span> <span class="mi">850</span><span class="p">,</span> <span class="mi">1450</span><span class="p">,</span> <span class="mi">920</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Filtered sales data based on current filter</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.calc</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">filtered_data</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="n">data</span> <span class="o">=</span> <span class="n">sales_data</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="nb">input</span><span class="o">.</span><span class="n">data_filter</span><span class="p">()</span> <span class="o">!=</span> <span class="s2">&#34;All&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="n">filter_mode</span><span class="p">()</span> <span class="o">==</span> <span class="s2">&#34;Region&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="n">data</span> <span class="o">=</span> <span class="n">data</span><span class="p">[</span><span class="n">data</span><span class="p">[</span><span class="s2">&#34;Region&#34;</span><span class="p">]</span> <span class="o">==</span> <span class="nb">input</span><span class="o">.</span><span class="n">data_filter</span><span class="p">()]</span>
</span></span><span class="line"><span class="cl">            <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="n">data</span> <span class="o">=</span> <span class="n">data</span><span class="p">[</span><span class="n">data</span><span class="p">[</span><span class="s2">&#34;Product&#34;</span><span class="p">]</span> <span class="o">==</span> <span class="nb">input</span><span class="o">.</span><span class="n">data_filter</span><span class="p">()]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">data</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Render the table</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@render.table</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">sales_table</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">filtered_data</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">            <span class="o">.</span><span class="n">style</span><span class="o">.</span><span class="n">hide</span><span class="p">(</span><span class="n">axis</span><span class="o">=</span><span class="s2">&#34;index&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="o">.</span><span class="n">format</span><span class="p">({</span><span class="s2">&#34;Sales&#34;</span><span class="p">:</span> <span class="s2">&#34;$</span><span class="si">{:,.0f}</span><span class="s2">&#34;</span><span class="p">})</span>
</span></span><span class="line"><span class="cl">            <span class="o">.</span><span class="n">set_table_styles</span><span class="p">([</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span><span class="s2">&#34;selector&#34;</span><span class="p">:</span> <span class="s2">&#34;th&#34;</span><span class="p">,</span> <span class="s2">&#34;props&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                    <span class="p">(</span><span class="s2">&#34;border-bottom&#34;</span><span class="p">,</span> <span class="s2">&#34;1px solid black&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                    <span class="p">(</span><span class="s2">&#34;padding&#34;</span><span class="p">,</span> <span class="s2">&#34;8px 6px&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                    <span class="p">(</span><span class="s2">&#34;text-align&#34;</span><span class="p">,</span> <span class="s2">&#34;left&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                <span class="p">]},</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span><span class="s2">&#34;selector&#34;</span><span class="p">:</span> <span class="s2">&#34;td&#34;</span><span class="p">,</span> <span class="s2">&#34;props&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                    <span class="p">(</span><span class="s2">&#34;padding&#34;</span><span class="p">,</span> <span class="s2">&#34;6px&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                <span class="p">]},</span>
</span></span><span class="line"><span class="cl">            <span class="p">])</span>
</span></span><span class="line"><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">app</span> <span class="o">=</span> <span class="n">App</span><span class="p">(</span><span class="n">app_ui</span><span class="p">,</span> <span class="n">server</span><span class="p">)</span></span></span></code></pre></div></div>
</details>
</div>
</div>
<p>The key here is passing the toolbar directly as the label. Include your label text as the first element, then use <code>toolbar_spacer()</code> to push the buttons to the right side. Toolbars automatically expand to full width when used as input labels, creating a compact control.</p>
<h2 id="complex-text-inputs-with-toolbars">Complex Text Inputs with Toolbars
</h2>
<p>Chatbots and AI applications have accelerated the need for more toolbars within text input areas to provide tools for attaching context or manipulating text.
Some newer Shiny components like <code>input_submit_textarea()</code> allow you to pass a toolbar directly into the input itself.</p>
<p>Here&rsquo;s a message composer example that includes buttons for attaching files, selecting a priority, and clearing the message:</p>
<figure>
<img src="https://opensource.posit.co/blog/2026-05-26_introducing-toolbars/card3.png" alt="Message composer textarea with a submit-area toolbar (attach, priority flag, delete) and a Submit button, beside a message history panel." />
<figcaption aria-hidden="true">Message composer textarea with a submit-area toolbar (attach, priority flag, delete) and a Submit button, beside a message history panel.</figcaption>
</figure>
<div class="panel-tabset" data-tabset-group="language">
<ul id="tabset-8" class="panel-tabset-tabby">
<li><a data-tabby-default href="#tabset-8-1">R</a></li>
<li><a href="#tabset-8-2">Python</a></li>
</ul>
<div id="tabset-8-1">
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="nf">input_submit_textarea</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">  <span class="n">id</span> <span class="o">=</span> <span class="s">&#34;message&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Your message&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="n">placeholder</span> <span class="o">=</span> <span class="s">&#34;Type your message here...&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="n">toolbar</span> <span class="o">=</span> <span class="nf">toolbar</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">align</span> <span class="o">=</span> <span class="s">&#34;left&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nf">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="n">id</span> <span class="o">=</span> <span class="s">&#34;attach_file&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Attach&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">icon</span> <span class="o">=</span> <span class="nf">icon</span><span class="p">(</span><span class="s">&#34;paperclip&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="nf">toolbar_input_select</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="n">id</span> <span class="o">=</span> <span class="s">&#34;priority&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Priority&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">choices</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="s">&#34;Low&#34;</span><span class="p">,</span> <span class="s">&#34;Medium&#34;</span><span class="p">,</span> <span class="s">&#34;High&#34;</span><span class="p">,</span> <span class="s">&#34;Urgent&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="n">icon</span> <span class="o">=</span> <span class="nf">icon</span><span class="p">(</span><span class="s">&#34;flag&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="nf">toolbar_divider</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">    <span class="nf">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="n">id</span> <span class="o">=</span> <span class="s">&#34;clear_message&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Clear&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">icon</span> <span class="o">=</span> <span class="nf">icon</span><span class="p">(</span><span class="s">&#34;trash&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span></span></span></code></pre></div></div>
<details>
<summary>
Complete app code
</summary>
<div class="code-block code-with-filename" role="group" aria-labelledby="code-filename-23">
  <div class="code-with-filename-label" id="code-filename-23"><span class="font-mono text-sm">app_6.R</span></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="nf">library</span><span class="p">(</span><span class="n">shiny</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nf">library</span><span class="p">(</span><span class="n">bslib</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">ui</span> <span class="o">&lt;-</span> <span class="nf">page_fluid</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">  <span class="nf">h2</span><span class="p">(</span><span class="s">&#34;Example App&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">  <span class="nf">layout_columns</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="nf">card</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="nf">card_header</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;Sales Data&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nf">toolbar</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">          <span class="n">align</span> <span class="o">=</span> <span class="s">&#34;right&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nf">toolbar_input_select</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">id</span> <span class="o">=</span> <span class="s">&#34;data_filter&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Region&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">choices</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="s">&#34;All&#34;</span><span class="p">,</span> <span class="s">&#34;North&#34;</span><span class="p">,</span> <span class="s">&#34;South&#34;</span><span class="p">,</span> <span class="s">&#34;East&#34;</span><span class="p">,</span> <span class="s">&#34;West&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">icon</span> <span class="o">=</span> <span class="nf">icon</span><span class="p">(</span><span class="s">&#34;filter&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">tooltip</span> <span class="o">=</span> <span class="s">&#34;Region&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="p">),</span>
</span></span><span class="line"><span class="cl">          <span class="nf">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">id</span> <span class="o">=</span> <span class="s">&#34;toggle_filter&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Switch to Product&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">icon</span> <span class="o">=</span> <span class="nf">icon</span><span class="p">(</span><span class="s">&#34;right-left&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">tooltip</span> <span class="o">=</span> <span class="s">&#34;Switch to filtering by Product&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="p">),</span>
</span></span><span class="line"><span class="cl">          <span class="nf">toolbar_divider</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">          <span class="nf">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">id</span> <span class="o">=</span> <span class="s">&#34;save_btn&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Save&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">icon</span> <span class="o">=</span> <span class="nf">icon</span><span class="p">(</span><span class="s">&#34;floppy-disk&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">          <span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="nf">card_body</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="nf">tableOutput</span><span class="p">(</span><span class="s">&#34;sales_table&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="nf">card</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="nf">card_header</span><span class="p">(</span><span class="s">&#34;Toolbar in Input Label&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="nf">card_body</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="nf">numericInput</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">          <span class="s">&#34;quantity&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="n">label</span> <span class="o">=</span> <span class="nf">toolbar</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="s">&#34;Quantity:&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nf">toolbar_spacer</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">            <span class="nf">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">              <span class="n">id</span> <span class="o">=</span> <span class="s">&#34;btn_preset_10&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;10&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="n">show_label</span> <span class="o">=</span> <span class="kc">TRUE</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="n">tooltip</span> <span class="o">=</span> <span class="s">&#34;Set to 10&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="nf">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">              <span class="n">id</span> <span class="o">=</span> <span class="s">&#34;btn_preset_50&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;50&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="n">show_label</span> <span class="o">=</span> <span class="kc">TRUE</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="n">tooltip</span> <span class="o">=</span> <span class="s">&#34;Set to 50&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="nf">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">              <span class="n">id</span> <span class="o">=</span> <span class="s">&#34;btn_preset_100&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;100&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="n">show_label</span> <span class="o">=</span> <span class="kc">TRUE</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="n">tooltip</span> <span class="o">=</span> <span class="s">&#34;Set to 100&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="nf">toolbar_divider</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">            <span class="nf">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">              <span class="n">id</span> <span class="o">=</span> <span class="s">&#34;btn_reset&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Reset&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="n">icon</span> <span class="o">=</span> <span class="nf">icon</span><span class="p">(</span><span class="s">&#34;rotate-left&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">              <span class="n">tooltip</span> <span class="o">=</span> <span class="s">&#34;Reset to 1&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">align</span> <span class="o">=</span> <span class="s">&#34;right&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="p">),</span>
</span></span><span class="line"><span class="cl">          <span class="n">value</span> <span class="o">=</span> <span class="m">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="n">min</span> <span class="o">=</span> <span class="m">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="n">max</span> <span class="o">=</span> <span class="m">1000</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="nf">textOutput</span><span class="p">(</span><span class="s">&#34;quantity_output&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="n">col_widths</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="m">6</span><span class="p">,</span> <span class="m">6</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">),</span>
</span></span><span class="line"><span class="cl">  <span class="nf">card</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="nf">card_header</span><span class="p">(</span><span class="s">&#34;Toolbar in Text Input Submit Area: Message Composer&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="nf">card_body</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="nf">layout_columns</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="nf">input_submit_textarea</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">          <span class="n">id</span> <span class="o">=</span> <span class="s">&#34;message&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Your message&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="n">placeholder</span> <span class="o">=</span> <span class="s">&#34;Type your message here...&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="n">toolbar</span> <span class="o">=</span> <span class="nf">toolbar</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">align</span> <span class="o">=</span> <span class="s">&#34;left&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nf">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">              <span class="n">id</span> <span class="o">=</span> <span class="s">&#34;attach_file&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Attach&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="n">icon</span> <span class="o">=</span> <span class="nf">icon</span><span class="p">(</span><span class="s">&#34;paperclip&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="nf">toolbar_input_select</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">              <span class="n">id</span> <span class="o">=</span> <span class="s">&#34;priority&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Priority&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="n">choices</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="s">&#34;Low&#34;</span><span class="p">,</span> <span class="s">&#34;Medium&#34;</span><span class="p">,</span> <span class="s">&#34;High&#34;</span><span class="p">,</span> <span class="s">&#34;Urgent&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">              <span class="n">icon</span> <span class="o">=</span> <span class="nf">icon</span><span class="p">(</span><span class="s">&#34;flag&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="nf">toolbar_divider</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">            <span class="nf">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">              <span class="n">id</span> <span class="o">=</span> <span class="s">&#34;clear_message&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Clear&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="n">icon</span> <span class="o">=</span> <span class="nf">icon</span><span class="p">(</span><span class="s">&#34;trash&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="p">)</span>
</span></span><span class="line"><span class="cl">          <span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="nf">div</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">          <span class="nf">h4</span><span class="p">(</span><span class="s">&#34;Message History&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">          <span class="nf">uiOutput</span><span class="p">(</span><span class="s">&#34;message_history&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">          <span class="n">style</span> <span class="o">=</span> <span class="s">&#34;max-height: 400px; overflow-y: auto;&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">col_widths</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="m">6</span><span class="p">,</span> <span class="m">6</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">server</span> <span class="o">&lt;-</span> <span class="kr">function</span><span class="p">(</span><span class="n">input</span><span class="p">,</span> <span class="n">output</span><span class="p">,</span> <span class="n">session</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># Track current filter mode</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">filter_mode</span> <span class="o">&lt;-</span> <span class="nf">reactiveVal</span><span class="p">(</span><span class="s">&#34;Region&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># Handle save button click</span>
</span></span><span class="line"><span class="cl">  <span class="nf">observeEvent</span><span class="p">(</span><span class="n">input</span><span class="o">$</span><span class="n">save_btn</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">update_toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="s">&#34;save_btn&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">icon</span> <span class="o">=</span> <span class="nf">icon</span><span class="p">(</span><span class="s">&#34;check&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Saved&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nf">showNotification</span><span class="p">(</span><span class="s">&#34;Saving&#34;</span><span class="p">,</span> <span class="n">type</span> <span class="o">=</span> <span class="s">&#34;message&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># Handle toggle filter button click</span>
</span></span><span class="line"><span class="cl">  <span class="nf">observeEvent</span><span class="p">(</span><span class="n">input</span><span class="o">$</span><span class="n">toggle_filter</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="p">(</span><span class="nf">filter_mode</span><span class="p">()</span> <span class="o">==</span> <span class="s">&#34;Region&#34;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nf">filter_mode</span><span class="p">(</span><span class="s">&#34;Product&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="nf">update_toolbar_input_select</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;data_filter&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Product&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">choices</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="s">&#34;All&#34;</span><span class="p">,</span> <span class="s">&#34;Widget A&#34;</span><span class="p">,</span> <span class="s">&#34;Widget B&#34;</span><span class="p">,</span> <span class="s">&#34;Widget C&#34;</span><span class="p">,</span> <span class="s">&#34;Widget D&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">selected</span> <span class="o">=</span> <span class="s">&#34;All&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="nf">update_toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;toggle_filter&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Switch to Region&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="nf">update_tooltip</span><span class="p">(</span><span class="s">&#34;toggle_filter_tooltip&#34;</span><span class="p">,</span> <span class="s">&#34;Switch to filtering by Region&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="nf">update_tooltip</span><span class="p">(</span><span class="s">&#34;data_filter_tooltip&#34;</span><span class="p">,</span> <span class="s">&#34;Product&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="kr">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nf">filter_mode</span><span class="p">(</span><span class="s">&#34;Region&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="nf">update_toolbar_input_select</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;data_filter&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Region&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">choices</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="s">&#34;All&#34;</span><span class="p">,</span> <span class="s">&#34;North&#34;</span><span class="p">,</span> <span class="s">&#34;South&#34;</span><span class="p">,</span> <span class="s">&#34;East&#34;</span><span class="p">,</span> <span class="s">&#34;West&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">selected</span> <span class="o">=</span> <span class="s">&#34;All&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="nf">update_toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;toggle_filter&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">label</span> <span class="o">=</span> <span class="s">&#34;Switch to Product&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="nf">update_tooltip</span><span class="p">(</span><span class="s">&#34;toggle_filter_tooltip&#34;</span><span class="p">,</span> <span class="s">&#34;Switch to filtering by Product&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="nf">update_tooltip</span><span class="p">(</span><span class="s">&#34;data_filter_tooltip&#34;</span><span class="p">,</span> <span class="s">&#34;Region&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># Handle numeric input preset buttons</span>
</span></span><span class="line"><span class="cl">  <span class="nf">observeEvent</span><span class="p">(</span><span class="n">input</span><span class="o">$</span><span class="n">btn_preset_10</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">updateNumericInput</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="s">&#34;quantity&#34;</span><span class="p">,</span> <span class="n">value</span> <span class="o">=</span> <span class="m">10</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nf">observeEvent</span><span class="p">(</span><span class="n">input</span><span class="o">$</span><span class="n">btn_preset_50</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">updateNumericInput</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="s">&#34;quantity&#34;</span><span class="p">,</span> <span class="n">value</span> <span class="o">=</span> <span class="m">50</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nf">observeEvent</span><span class="p">(</span><span class="n">input</span><span class="o">$</span><span class="n">btn_preset_100</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">updateNumericInput</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="s">&#34;quantity&#34;</span><span class="p">,</span> <span class="n">value</span> <span class="o">=</span> <span class="m">100</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nf">observeEvent</span><span class="p">(</span><span class="n">input</span><span class="o">$</span><span class="n">btn_reset</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">updateNumericInput</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="s">&#34;quantity&#34;</span><span class="p">,</span> <span class="n">value</span> <span class="o">=</span> <span class="m">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">output</span><span class="o">$</span><span class="n">quantity_output</span> <span class="o">&lt;-</span> <span class="nf">renderText</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="nf">paste</span><span class="p">(</span><span class="s">&#34;Current quantity:&#34;</span><span class="p">,</span> <span class="n">input</span><span class="o">$</span><span class="n">quantity</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># Sample sales data</span>
</span></span><span class="line"><span class="cl">  <span class="n">sales_data</span> <span class="o">&lt;-</span> <span class="nf">reactive</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="nf">data.frame</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="n">Product</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="s">&#34;Widget A&#34;</span><span class="p">,</span> <span class="s">&#34;Widget B&#34;</span><span class="p">,</span> <span class="s">&#34;Widget C&#34;</span><span class="p">,</span> <span class="s">&#34;Widget D&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="n">Region</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="s">&#34;North&#34;</span><span class="p">,</span> <span class="s">&#34;South&#34;</span><span class="p">,</span> <span class="s">&#34;East&#34;</span><span class="p">,</span> <span class="s">&#34;West&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="n">Sales</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="m">1200</span><span class="p">,</span> <span class="m">850</span><span class="p">,</span> <span class="m">1450</span><span class="p">,</span> <span class="m">920</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="n">stringsAsFactors</span> <span class="o">=</span> <span class="kc">FALSE</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># Filtered sales data based on current filter</span>
</span></span><span class="line"><span class="cl">  <span class="n">filtered_data</span> <span class="o">&lt;-</span> <span class="nf">reactive</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="n">data</span> <span class="o">&lt;-</span> <span class="nf">sales_data</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="p">(</span><span class="n">input</span><span class="o">$</span><span class="n">data_filter</span> <span class="o">!=</span> <span class="s">&#34;All&#34;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="kr">if</span> <span class="p">(</span><span class="nf">filter_mode</span><span class="p">()</span> <span class="o">==</span> <span class="s">&#34;Region&#34;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">data</span> <span class="o">&lt;-</span> <span class="n">data[data</span><span class="o">$</span><span class="n">Region</span> <span class="o">==</span> <span class="n">input</span><span class="o">$</span><span class="n">data_filter</span><span class="p">,</span> <span class="n">]</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span> <span class="kr">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">data</span> <span class="o">&lt;-</span> <span class="n">data[data</span><span class="o">$</span><span class="n">Product</span> <span class="o">==</span> <span class="n">input</span><span class="o">$</span><span class="n">data_filter</span><span class="p">,</span> <span class="n">]</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">data</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># Render the table</span>
</span></span><span class="line"><span class="cl">  <span class="n">output</span><span class="o">$</span><span class="n">sales_table</span> <span class="o">&lt;-</span> <span class="nf">renderTable</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="n">data</span> <span class="o">&lt;-</span> <span class="nf">filtered_data</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="p">(</span><span class="nf">nrow</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="o">&gt;</span> <span class="m">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">data</span><span class="o">$</span><span class="n">Sales</span> <span class="o">&lt;-</span> <span class="nf">paste0</span><span class="p">(</span><span class="s">&#34;$&#34;</span><span class="p">,</span> <span class="nf">format</span><span class="p">(</span><span class="n">data</span><span class="o">$</span><span class="n">Sales</span><span class="p">,</span> <span class="n">big.mark</span> <span class="o">=</span> <span class="s">&#34;,&#34;</span><span class="p">,</span> <span class="n">trim</span> <span class="o">=</span> <span class="kc">TRUE</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">data</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># Handle message input with toolbar</span>
</span></span><span class="line"><span class="cl">  <span class="n">messages</span> <span class="o">&lt;-</span> <span class="nf">reactiveVal</span><span class="p">(</span><span class="nf">character</span><span class="p">(</span><span class="m">0</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nf">observeEvent</span><span class="p">(</span><span class="n">input</span><span class="o">$</span><span class="n">message</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">new_message</span> <span class="o">&lt;-</span> <span class="nf">paste0</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="s">&#34;[&#34;</span><span class="p">,</span> <span class="n">input</span><span class="o">$</span><span class="n">priority</span><span class="p">,</span> <span class="s">&#34;] &#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">input</span><span class="o">$</span><span class="n">message</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nf">messages</span><span class="p">(</span><span class="nf">c</span><span class="p">(</span><span class="nf">messages</span><span class="p">(),</span> <span class="n">new_message</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nf">observeEvent</span><span class="p">(</span><span class="n">input</span><span class="o">$</span><span class="n">attach_file</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">showNotification</span><span class="p">(</span><span class="s">&#34;File attachment feature clicked&#34;</span><span class="p">,</span> <span class="n">type</span> <span class="o">=</span> <span class="s">&#34;message&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nf">observeEvent</span><span class="p">(</span><span class="n">input</span><span class="o">$</span><span class="n">clear_message</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">updateTextAreaInput</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="s">&#34;message&#34;</span><span class="p">,</span> <span class="n">value</span> <span class="o">=</span> <span class="s">&#34;&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">output</span><span class="o">$</span><span class="n">message_history</span> <span class="o">&lt;-</span> <span class="nf">renderUI</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="p">(</span><span class="nf">length</span><span class="p">(</span><span class="nf">messages</span><span class="p">())</span> <span class="o">==</span> <span class="m">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nf">p</span><span class="p">(</span><span class="s">&#34;No messages yet&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="kr">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nf">tagList</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="nf">lapply</span><span class="p">(</span><span class="nf">messages</span><span class="p">(),</span> <span class="kr">function</span><span class="p">(</span><span class="n">msg</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nf">div</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">msg</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">class</span> <span class="o">=</span> <span class="s">&#34;mb-2&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">style</span> <span class="o">=</span> <span class="s">&#34;background-color: #e9ecef; padding: 10px 15px; border-radius: 8px;&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">})</span>
</span></span><span class="line"><span class="cl">      <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">shinyApp</span><span class="p">(</span><span class="n">ui</span><span class="p">,</span> <span class="n">server</span><span class="p">)</span></span></span></code></pre></div></div>
</details>
</div>
<div id="tabset-8-2">
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">ui</span><span class="o">.</span><span class="n">input_submit_textarea</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="nb">id</span><span class="o">=</span><span class="s2">&#34;message&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Your message&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">placeholder</span><span class="o">=</span><span class="s2">&#34;Type your message here...&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">toolbar</span><span class="o">=</span><span class="n">ui</span><span class="o">.</span><span class="n">toolbar</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="nb">id</span><span class="o">=</span><span class="s2">&#34;attach_file&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Attach&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">icon</span><span class="o">=</span><span class="n">icon_svg</span><span class="p">(</span><span class="s2">&#34;paperclip&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_input_select</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="nb">id</span><span class="o">=</span><span class="s2">&#34;priority&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Priority&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">choices</span><span class="o">=</span><span class="p">[</span><span class="s2">&#34;Low&#34;</span><span class="p">,</span> <span class="s2">&#34;Medium&#34;</span><span class="p">,</span> <span class="s2">&#34;High&#34;</span><span class="p">,</span> <span class="s2">&#34;Urgent&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">            <span class="n">icon</span><span class="o">=</span><span class="n">icon_svg</span><span class="p">(</span><span class="s2">&#34;flag&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_divider</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="nb">id</span><span class="o">=</span><span class="s2">&#34;clear_message&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Clear&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">icon</span><span class="o">=</span><span class="n">icon_svg</span><span class="p">(</span><span class="s2">&#34;trash&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">align</span><span class="o">=</span><span class="s2">&#34;left&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span></span></span></code></pre></div></div>
<details>
<summary>
Complete app code
</summary>
<div class="code-block code-with-filename" role="group" aria-labelledby="code-filename-25">
  <div class="code-with-filename-label" id="code-filename-25"><span class="font-mono text-sm">app_6.py</span></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">pandas</span> <span class="k">as</span> <span class="nn">pd</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">faicons</span> <span class="kn">import</span> <span class="n">icon_svg</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">shiny</span> <span class="kn">import</span> <span class="n">App</span><span class="p">,</span> <span class="n">Inputs</span><span class="p">,</span> <span class="n">Outputs</span><span class="p">,</span> <span class="n">Session</span><span class="p">,</span> <span class="n">reactive</span><span class="p">,</span> <span class="n">render</span><span class="p">,</span> <span class="n">ui</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">app_ui</span> <span class="o">=</span> <span class="n">ui</span><span class="o">.</span><span class="n">page_fluid</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">ui</span><span class="o">.</span><span class="n">h2</span><span class="p">(</span><span class="s2">&#34;Example App&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="n">ui</span><span class="o">.</span><span class="n">layout_columns</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">card</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">card_header</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;Sales Data&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="n">ui</span><span class="o">.</span><span class="n">toolbar</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                    <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_input_select</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                        <span class="nb">id</span><span class="o">=</span><span class="s2">&#34;data_filter&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Region&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">choices</span><span class="o">=</span><span class="p">[</span><span class="s2">&#34;All&#34;</span><span class="p">,</span> <span class="s2">&#34;North&#34;</span><span class="p">,</span> <span class="s2">&#34;South&#34;</span><span class="p">,</span> <span class="s2">&#34;East&#34;</span><span class="p">,</span> <span class="s2">&#34;West&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">                        <span class="n">icon</span><span class="o">=</span><span class="n">icon_svg</span><span class="p">(</span><span class="s2">&#34;filter&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                        <span class="n">tooltip</span><span class="o">=</span><span class="s2">&#34;Region&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="p">),</span>
</span></span><span class="line"><span class="cl">                    <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                        <span class="nb">id</span><span class="o">=</span><span class="s2">&#34;toggle_filter&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Switch to Product&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">icon</span><span class="o">=</span><span class="n">icon_svg</span><span class="p">(</span><span class="s2">&#34;right-left&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                        <span class="n">tooltip</span><span class="o">=</span><span class="s2">&#34;Switch to filtering by Product&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="p">),</span>
</span></span><span class="line"><span class="cl">                    <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_divider</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">                    <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                        <span class="nb">id</span><span class="o">=</span><span class="s2">&#34;save_btn&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Save&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">icon</span><span class="o">=</span><span class="n">icon_svg</span><span class="p">(</span><span class="s2">&#34;floppy-disk&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                    <span class="p">),</span>
</span></span><span class="line"><span class="cl">                    <span class="n">align</span><span class="o">=</span><span class="s2">&#34;right&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">card_body</span><span class="p">(</span><span class="n">ui</span><span class="o">.</span><span class="n">output_table</span><span class="p">(</span><span class="s2">&#34;sales_table&#34;</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">card</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">card_header</span><span class="p">(</span><span class="s2">&#34;Toolbar in Input Label&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">card_body</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="n">ui</span><span class="o">.</span><span class="n">input_numeric</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                    <span class="s2">&#34;quantity&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="n">label</span><span class="o">=</span><span class="n">ui</span><span class="o">.</span><span class="n">toolbar</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;Quantity:&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_spacer</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">                        <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                            <span class="s2">&#34;btn_preset_10&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="n">label</span><span class="o">=</span><span class="s2">&#34;10&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="n">show_label</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="n">tooltip</span><span class="o">=</span><span class="s2">&#34;Set to 10&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="p">),</span>
</span></span><span class="line"><span class="cl">                        <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                            <span class="s2">&#34;btn_preset_50&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="n">label</span><span class="o">=</span><span class="s2">&#34;50&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="n">show_label</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="n">tooltip</span><span class="o">=</span><span class="s2">&#34;Set to 50&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="p">),</span>
</span></span><span class="line"><span class="cl">                        <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                            <span class="s2">&#34;btn_preset_100&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="n">label</span><span class="o">=</span><span class="s2">&#34;100&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="n">show_label</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="n">tooltip</span><span class="o">=</span><span class="s2">&#34;Set to 100&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="p">),</span>
</span></span><span class="line"><span class="cl">                        <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_divider</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">                        <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                            <span class="s2">&#34;btn_reset&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Reset&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="n">icon</span><span class="o">=</span><span class="n">icon_svg</span><span class="p">(</span><span class="s2">&#34;rotate-left&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                            <span class="n">tooltip</span><span class="o">=</span><span class="s2">&#34;Reset to 1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="p">),</span>
</span></span><span class="line"><span class="cl">                        <span class="n">align</span><span class="o">=</span><span class="s2">&#34;right&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="p">),</span>
</span></span><span class="line"><span class="cl">                    <span class="n">value</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nb">min</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nb">max</span><span class="o">=</span><span class="mi">1000</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="p">),</span>
</span></span><span class="line"><span class="cl">                <span class="n">ui</span><span class="o">.</span><span class="n">output_text</span><span class="p">(</span><span class="s2">&#34;quantity_output&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">col_widths</span><span class="o">=</span><span class="p">[</span><span class="mi">6</span><span class="p">,</span> <span class="mi">6</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="n">ui</span><span class="o">.</span><span class="n">card</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">card_header</span><span class="p">(</span><span class="s2">&#34;Toolbar in Text Input Submit Area: Message Composer&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">card_body</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">layout_columns</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="n">ui</span><span class="o">.</span><span class="n">input_submit_textarea</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                    <span class="nb">id</span><span class="o">=</span><span class="s2">&#34;message&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Your message&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="n">placeholder</span><span class="o">=</span><span class="s2">&#34;Type your message here...&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="n">toolbar</span><span class="o">=</span><span class="n">ui</span><span class="o">.</span><span class="n">toolbar</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                        <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                            <span class="nb">id</span><span class="o">=</span><span class="s2">&#34;attach_file&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Attach&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="n">icon</span><span class="o">=</span><span class="n">icon_svg</span><span class="p">(</span><span class="s2">&#34;paperclip&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                        <span class="p">),</span>
</span></span><span class="line"><span class="cl">                        <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_input_select</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                            <span class="nb">id</span><span class="o">=</span><span class="s2">&#34;priority&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Priority&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="n">choices</span><span class="o">=</span><span class="p">[</span><span class="s2">&#34;Low&#34;</span><span class="p">,</span> <span class="s2">&#34;Medium&#34;</span><span class="p">,</span> <span class="s2">&#34;High&#34;</span><span class="p">,</span> <span class="s2">&#34;Urgent&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">                            <span class="n">icon</span><span class="o">=</span><span class="n">icon_svg</span><span class="p">(</span><span class="s2">&#34;flag&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                        <span class="p">),</span>
</span></span><span class="line"><span class="cl">                        <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_divider</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">                        <span class="n">ui</span><span class="o">.</span><span class="n">toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                            <span class="nb">id</span><span class="o">=</span><span class="s2">&#34;clear_message&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Clear&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="n">icon</span><span class="o">=</span><span class="n">icon_svg</span><span class="p">(</span><span class="s2">&#34;trash&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                        <span class="p">),</span>
</span></span><span class="line"><span class="cl">                        <span class="n">align</span><span class="o">=</span><span class="s2">&#34;left&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="p">),</span>
</span></span><span class="line"><span class="cl">                <span class="p">),</span>
</span></span><span class="line"><span class="cl">                <span class="n">ui</span><span class="o">.</span><span class="n">div</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                    <span class="n">ui</span><span class="o">.</span><span class="n">h4</span><span class="p">(</span><span class="s2">&#34;Message History&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                    <span class="n">ui</span><span class="o">.</span><span class="n">output_ui</span><span class="p">(</span><span class="s2">&#34;message_history&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                    <span class="n">style</span><span class="o">=</span><span class="s2">&#34;max-height: 400px; overflow-y: auto;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="p">),</span>
</span></span><span class="line"><span class="cl">                <span class="n">col_widths</span><span class="o">=</span><span class="p">[</span><span class="mi">6</span><span class="p">,</span> <span class="mi">6</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">            <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">server</span><span class="p">(</span><span class="nb">input</span><span class="p">:</span> <span class="n">Inputs</span><span class="p">,</span> <span class="n">output</span><span class="p">:</span> <span class="n">Outputs</span><span class="p">,</span> <span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Track current filter mode</span>
</span></span><span class="line"><span class="cl">    <span class="n">filter_mode</span> <span class="o">=</span> <span class="n">reactive</span><span class="o">.</span><span class="n">value</span><span class="p">(</span><span class="s2">&#34;Region&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.effect</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.event</span><span class="p">(</span><span class="nb">input</span><span class="o">.</span><span class="n">save_btn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">_</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">update_toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;save_btn&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">icon</span><span class="o">=</span><span class="n">icon_svg</span><span class="p">(</span><span class="s2">&#34;check&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Saved&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">notification_show</span><span class="p">(</span><span class="s2">&#34;Saving&#34;</span><span class="p">,</span> <span class="nb">type</span><span class="o">=</span><span class="s2">&#34;message&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Handle toggle filter button click</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.effect</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.event</span><span class="p">(</span><span class="nb">input</span><span class="o">.</span><span class="n">toggle_filter</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">_</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">filter_mode</span><span class="p">()</span> <span class="o">==</span> <span class="s2">&#34;Region&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">filter_mode</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="s2">&#34;Product&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">update_toolbar_input_select</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;data_filter&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Product&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="n">choices</span><span class="o">=</span><span class="p">[</span><span class="s2">&#34;All&#34;</span><span class="p">,</span> <span class="s2">&#34;Widget A&#34;</span><span class="p">,</span> <span class="s2">&#34;Widget B&#34;</span><span class="p">,</span> <span class="s2">&#34;Widget C&#34;</span><span class="p">,</span> <span class="s2">&#34;Widget D&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">                <span class="n">selected</span><span class="o">=</span><span class="s2">&#34;All&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">update_toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;toggle_filter&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Switch to Region&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">update_tooltip</span><span class="p">(</span><span class="s2">&#34;toggle_filter_tooltip&#34;</span><span class="p">,</span> <span class="s2">&#34;Switch to filtering by Region&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">update_tooltip</span><span class="p">(</span><span class="s2">&#34;data_filter_tooltip&#34;</span><span class="p">,</span> <span class="s2">&#34;Product&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">filter_mode</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="s2">&#34;Region&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">update_toolbar_input_select</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;data_filter&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Region&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="n">choices</span><span class="o">=</span><span class="p">[</span><span class="s2">&#34;All&#34;</span><span class="p">,</span> <span class="s2">&#34;North&#34;</span><span class="p">,</span> <span class="s2">&#34;South&#34;</span><span class="p">,</span> <span class="s2">&#34;East&#34;</span><span class="p">,</span> <span class="s2">&#34;West&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">                <span class="n">selected</span><span class="o">=</span><span class="s2">&#34;All&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">update_toolbar_input_button</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;toggle_filter&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="n">label</span><span class="o">=</span><span class="s2">&#34;Switch to Product&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">update_tooltip</span><span class="p">(</span><span class="s2">&#34;toggle_filter_tooltip&#34;</span><span class="p">,</span> <span class="s2">&#34;Switch to filtering by Product&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">ui</span><span class="o">.</span><span class="n">update_tooltip</span><span class="p">(</span><span class="s2">&#34;data_filter_tooltip&#34;</span><span class="p">,</span> <span class="s2">&#34;Region&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Handle numeric input preset buttons</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.effect</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.event</span><span class="p">(</span><span class="nb">input</span><span class="o">.</span><span class="n">btn_preset_10</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">_</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">update_numeric</span><span class="p">(</span><span class="s2">&#34;quantity&#34;</span><span class="p">,</span> <span class="n">value</span><span class="o">=</span><span class="mi">10</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.effect</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.event</span><span class="p">(</span><span class="nb">input</span><span class="o">.</span><span class="n">btn_preset_50</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">_</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">update_numeric</span><span class="p">(</span><span class="s2">&#34;quantity&#34;</span><span class="p">,</span> <span class="n">value</span><span class="o">=</span><span class="mi">50</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.effect</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.event</span><span class="p">(</span><span class="nb">input</span><span class="o">.</span><span class="n">btn_preset_100</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">_</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">update_numeric</span><span class="p">(</span><span class="s2">&#34;quantity&#34;</span><span class="p">,</span> <span class="n">value</span><span class="o">=</span><span class="mi">100</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.effect</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.event</span><span class="p">(</span><span class="nb">input</span><span class="o">.</span><span class="n">btn_reset</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">_</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">update_numeric</span><span class="p">(</span><span class="s2">&#34;quantity&#34;</span><span class="p">,</span> <span class="n">value</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@render.text</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">quantity_output</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="sa">f</span><span class="s2">&#34;Current quantity: </span><span class="si">{</span><span class="nb">input</span><span class="o">.</span><span class="n">quantity</span><span class="p">()</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Sample sales data</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.calc</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">sales_data</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">pd</span><span class="o">.</span><span class="n">DataFrame</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;Product&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;Widget A&#34;</span><span class="p">,</span> <span class="s2">&#34;Widget B&#34;</span><span class="p">,</span> <span class="s2">&#34;Widget C&#34;</span><span class="p">,</span> <span class="s2">&#34;Widget D&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;Region&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;North&#34;</span><span class="p">,</span> <span class="s2">&#34;South&#34;</span><span class="p">,</span> <span class="s2">&#34;East&#34;</span><span class="p">,</span> <span class="s2">&#34;West&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;Sales&#34;</span><span class="p">:</span> <span class="p">[</span><span class="mi">1200</span><span class="p">,</span> <span class="mi">850</span><span class="p">,</span> <span class="mi">1450</span><span class="p">,</span> <span class="mi">920</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Filtered sales data based on current filter</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.calc</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">filtered_data</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="n">data</span> <span class="o">=</span> <span class="n">sales_data</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="nb">input</span><span class="o">.</span><span class="n">data_filter</span><span class="p">()</span> <span class="o">!=</span> <span class="s2">&#34;All&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="n">filter_mode</span><span class="p">()</span> <span class="o">==</span> <span class="s2">&#34;Region&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="n">data</span> <span class="o">=</span> <span class="n">data</span><span class="p">[</span><span class="n">data</span><span class="p">[</span><span class="s2">&#34;Region&#34;</span><span class="p">]</span> <span class="o">==</span> <span class="nb">input</span><span class="o">.</span><span class="n">data_filter</span><span class="p">()]</span>
</span></span><span class="line"><span class="cl">            <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="n">data</span> <span class="o">=</span> <span class="n">data</span><span class="p">[</span><span class="n">data</span><span class="p">[</span><span class="s2">&#34;Product&#34;</span><span class="p">]</span> <span class="o">==</span> <span class="nb">input</span><span class="o">.</span><span class="n">data_filter</span><span class="p">()]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">data</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Render the table</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@render.table</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">sales_table</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">filtered_data</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">            <span class="o">.</span><span class="n">style</span><span class="o">.</span><span class="n">hide</span><span class="p">(</span><span class="n">axis</span><span class="o">=</span><span class="s2">&#34;index&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="o">.</span><span class="n">format</span><span class="p">({</span><span class="s2">&#34;Sales&#34;</span><span class="p">:</span> <span class="s2">&#34;$</span><span class="si">{:,.0f}</span><span class="s2">&#34;</span><span class="p">})</span>
</span></span><span class="line"><span class="cl">            <span class="o">.</span><span class="n">set_table_styles</span><span class="p">([</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span><span class="s2">&#34;selector&#34;</span><span class="p">:</span> <span class="s2">&#34;th&#34;</span><span class="p">,</span> <span class="s2">&#34;props&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                    <span class="p">(</span><span class="s2">&#34;border-bottom&#34;</span><span class="p">,</span> <span class="s2">&#34;1px solid black&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                    <span class="p">(</span><span class="s2">&#34;padding&#34;</span><span class="p">,</span> <span class="s2">&#34;8px 6px&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                    <span class="p">(</span><span class="s2">&#34;text-align&#34;</span><span class="p">,</span> <span class="s2">&#34;left&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                <span class="p">]},</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span><span class="s2">&#34;selector&#34;</span><span class="p">:</span> <span class="s2">&#34;td&#34;</span><span class="p">,</span> <span class="s2">&#34;props&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                    <span class="p">(</span><span class="s2">&#34;padding&#34;</span><span class="p">,</span> <span class="s2">&#34;6px&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                <span class="p">]},</span>
</span></span><span class="line"><span class="cl">            <span class="p">])</span>
</span></span><span class="line"><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Handle message input with toolbar</span>
</span></span><span class="line"><span class="cl">    <span class="n">messages</span> <span class="o">=</span> <span class="n">reactive</span><span class="o">.</span><span class="n">value</span><span class="p">([])</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.effect</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.event</span><span class="p">(</span><span class="nb">input</span><span class="o">.</span><span class="n">message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">_</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="n">new_message</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&#34;[</span><span class="si">{</span><span class="nb">input</span><span class="o">.</span><span class="n">priority</span><span class="p">()</span><span class="si">}</span><span class="s2">] </span><span class="si">{</span><span class="nb">input</span><span class="o">.</span><span class="n">message</span><span class="p">()</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="n">messages</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="n">messages</span><span class="o">.</span><span class="n">get</span><span class="p">()</span> <span class="o">+</span> <span class="p">[</span><span class="n">new_message</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.effect</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.event</span><span class="p">(</span><span class="nb">input</span><span class="o">.</span><span class="n">attach_file</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">_</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">notification_show</span><span class="p">(</span><span class="s2">&#34;File attachment feature clicked&#34;</span><span class="p">,</span> <span class="nb">type</span><span class="o">=</span><span class="s2">&#34;message&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.effect</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@reactive.event</span><span class="p">(</span><span class="nb">input</span><span class="o">.</span><span class="n">clear_message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">_</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="n">ui</span><span class="o">.</span><span class="n">update_text_area</span><span class="p">(</span><span class="s2">&#34;message&#34;</span><span class="p">,</span> <span class="n">value</span><span class="o">=</span><span class="s2">&#34;&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@render.ui</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">message_history</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">messages</span><span class="o">.</span><span class="n">get</span><span class="p">())</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">ui</span><span class="o">.</span><span class="n">p</span><span class="p">(</span><span class="s2">&#34;No messages yet&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">ui</span><span class="o">.</span><span class="n">TagList</span><span class="p">([</span>
</span></span><span class="line"><span class="cl">                <span class="n">ui</span><span class="o">.</span><span class="n">div</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                    <span class="n">msg</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="n">class_</span><span class="o">=</span><span class="s2">&#34;mb-2&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="n">style</span><span class="o">=</span><span class="s2">&#34;background-color: #e9ecef; padding: 10px 15px; border-radius: 8px;&#34;</span>
</span></span><span class="line"><span class="cl">                <span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="k">for</span> <span class="n">msg</span> <span class="ow">in</span> <span class="n">messages</span><span class="o">.</span><span class="n">get</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">            <span class="p">])</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">app</span> <span class="o">=</span> <span class="n">App</span><span class="p">(</span><span class="n">app_ui</span><span class="p">,</span> <span class="n">server</span><span class="p">)</span></span></span></code></pre></div></div>
</details>
</div>
</div>
<p>The <code>input_submit_textarea()</code> component accepts a <code>toolbar</code> parameter directly, making it easy to add contextual controls right where users need them.</p>
<h2 id="toolbar-input-components">Toolbar Input Components
</h2>
<p>Here&rsquo;s a quick reference of the toolbar input components available in both <a href="https://rstudio.github.io/bslib/" target="_blank" rel="noopener">bslib</a> and <a href="https://shiny.posit.co/py/" target="_blank" rel="noopener">py-shiny</a>:</p>
<table>
  <thead>
      <tr>
          <th>R (bslib)</th>
          <th>Python (py-shiny)</th>
          <th>Description</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="https://rstudio.github.io/bslib/reference/toolbar.html" target="_blank" rel="noopener"><code>toolbar()</code></a></td>
          <td><a href="https://shiny.posit.co/py/api/ui.toolbar.html" target="_blank" rel="noopener"><code>ui.toolbar()</code></a></td>
          <td>The container for toolbar inputs</td>
      </tr>
      <tr>
          <td><a href="https://rstudio.github.io/bslib/reference/toolbar_input_button.html" target="_blank" rel="noopener"><code>toolbar_input_button()</code></a></td>
          <td><a href="https://shiny.posit.co/py/api/ui.toolbar_input_button.html" target="_blank" rel="noopener"><code>ui.toolbar_input_button()</code></a></td>
          <td>A small action button</td>
      </tr>
      <tr>
          <td><a href="https://rstudio.github.io/bslib/reference/toolbar_input_select.html" target="_blank" rel="noopener"><code>toolbar_input_select()</code></a></td>
          <td><a href="https://shiny.posit.co/py/api/ui.toolbar_input_select.html" target="_blank" rel="noopener"><code>ui.toolbar_input_select()</code></a></td>
          <td>A compact dropdown select</td>
      </tr>
      <tr>
          <td><a href="https://rstudio.github.io/bslib/reference/toolbar_divider.html" target="_blank" rel="noopener"><code>toolbar_divider()</code></a></td>
          <td><a href="https://shiny.posit.co/py/api/ui.toolbar_divider.html" target="_blank" rel="noopener"><code>ui.toolbar_divider()</code></a></td>
          <td>A visual separator between groups of controls</td>
      </tr>
      <tr>
          <td><a href="https://rstudio.github.io/bslib/reference/toolbar_divider.html" target="_blank" rel="noopener"><code>toolbar_spacer()</code></a></td>
          <td><a href="https://shiny.posit.co/py/api/ui.toolbar_spacer.html" target="_blank" rel="noopener"><code>ui.toolbar_spacer()</code></a></td>
          <td>A flexible spacer that pushes subsequent items to the opposite end of the toolbar</td>
      </tr>
  </tbody>
</table>
<p>Each toolbar input also has a corresponding <code>update_*</code> function (R) / <code>update_*</code> method on <code>ui</code> (Python) for modifying it dynamically.</p>
<h2 id="summary">Summary
</h2>
<p>Toolbars provide a flexible way to add compact controls to your Shiny apps:</p>
<ul>
<li><strong>Card headers and footers</strong> - Associate controls directly with card content</li>
<li><strong>Input labels</strong> - Add quick actions like reset or format buttons</li>
<li><strong>Text area inputs</strong> - Build rich message composers with attachment and formatting tools</li>
</ul>
<p>Their small footprint makes them perfect for dashboards and modern app interfaces where screen real estate is at a premium. Give them a try in your next Shiny app!</p>
<div class="callout callout-tip" role="note" aria-label="Tip">
<div class="callout-header">
<span class="callout-title">What else is new?</span>
</div>
<div class="callout-body">
<p>See what else is new in the <a href="https://rstudio.github.io/bslib/news/index.html" target="_blank" rel="noopener">bslib changelog</a> (R) and the <a href="https://github.com/posit-dev/py-shiny/blob/main/CHANGELOG.md" target="_blank" rel="noopener">py-shiny changelog</a> (Python).</p>
</div>
</div>
]]></description>
      <enclosure url="https://opensource.posit.co/blog/2026-05-26_introducing-toolbars/preview.png" length="193556" type="image/png" />
    </item>
    <item>
      <title>AI Newsletter: Gemma 4 in Posit Assistant</title>
      <link>https://opensource.posit.co/blog/2026-05-22_ai-newsletter/</link>
      <pubDate>Fri, 22 May 2026 00:00:00 +0000</pubDate>
      <guid>https://opensource.posit.co/blog/2026-05-22_ai-newsletter/</guid>
      <dc:creator>Sara Altman</dc:creator>
      <dc:creator>Simon Couch</dc:creator><description><![CDATA[<h2 id="external-news">External news
</h2>
<p>Through 2023 and 2024, the dominant narrative on AI token pricing was that the cost to use AI was <a href="https://epoch.ai/data-insights/llm-inference-price-trends" target="_blank" rel="noopener">dramatically</a> <a href="https://hai.stanford.edu/news/ai-index-2025-state-of-ai-in-10-charts" target="_blank" rel="noopener">decreasing</a> and <a href="https://blog.samaltman.com/three-observations" target="_blank" rel="noopener">would continue to do so</a>. However, in the last year, that trend is reversing course.</p>
<ul>
<li>On Tuesday, Google released <a href="https://blog.google/innovation-and-ai/models-and-research/gemini-models/gemini-3-5/" target="_blank" rel="noopener">Gemini 3.5 Flash</a>, and priced it at three times the previous Flash model, Gemini 3 Flash.</li>
<li><a href="https://openai.com/index/introducing-gpt-5-5/" target="_blank" rel="noopener">GPT-5.5</a>, released last month, is twice as expensive as GPT-5.4.</li>
<li><a href="https://www.anthropic.com/news/claude-opus-4-7" target="_blank" rel="noopener">Opus 4.7</a>, also released last month, has the same price per token as Opus 4.6, but uses an updated tokenizer that <a href="https://simonwillison.net/2026/apr/20/claude-token-counts/" target="_blank" rel="noopener">results in ~45% more tokens for the same text</a>.</li>
</ul>
<p>Combined with changes to subscription pricing (<a href="https://help.openai.com/en/articles/20001106-codex-rate-card#codex-rate-card-token-based-pricing" target="_blank" rel="noopener">Copilot</a>, <a href="https://help.openai.com/en/articles/20001106-codex-rate-card#codex-rate-card-token-based-pricing" target="_blank" rel="noopener">Codex</a>, <a href="https://www.pcworld.com/article/3100787/anthropic-confirms-its-been-adjusting-claude-usage-limits.html" target="_blank" rel="noopener">Claude</a>), this suggests that AI prices are on the rise. Over the last few years, the major model providers have subsidized tokens to acquire users, and it seems like that period is ending.</p>
<p><div class="not-prose"><figure>
    <img class="h-auto max-w-full rounded-lg"
      src="https://opensource.posit.co/blog/2026-05-22_ai-newsletter/images/plot-model-price.png"
      alt="Scatter plot showing the cost to run the Artificial Analysis benchmark suite over time for 12 models from Anthropic, Google, and OpenAI. Arrows connect predecessor-successor pairs, showing that most models have become more expensive to benchmark across generations, with Opus 4.7 the most expensive at over $5,000."  title="Costs to run the Artificial Analysis benchmark suite (https://artificialanalysis.ai/) are higher for recent models compared to their direct predecessors." 
      loading="lazy"
    ><figcaption class="text-sm text-center text-gray-500">Costs to run the Artificial Analysis benchmark suite (<a href="https://artificialanalysis.ai/" target="_blank" rel="noopener">https://artificialanalysis.ai/</a>) are higher for recent models compared to their direct predecessors.</figcaption>
  </figure></div>
</p>
<p>You can read more about this trend, and also how it shaped the thinking behind pricing Posit AI, in this blog post: <a href="https://posit.co/blog/posit-ai-priced-long-run" target="_blank" rel="noopener">Posit AI is priced for the long run</a>.</p>
<h2 id="posit-news">Posit news
</h2>
<h3 id="gemma-4-in-posit-assistant">Gemma 4 in Posit Assistant
</h3>
<p><strong>Gemma 4 is <a href="https://posit.co/blog/gemma-4-new-budget-focused-model-posit-ai/" target="_blank" rel="noopener">now available in Posit Assistant</a> via the Posit AI provider.</strong> Gemma 4 is a relatively small open-weights model released by Google Gemini, and one example of <a href="https://simonpcouch.com/blog/2026-04-16-local-agents-2/" target="_blank" rel="noopener">recent improvements</a> in local models small enough to run on a laptop.</p>
<p><div class="not-prose"><figure>
    <img class="h-auto max-w-full rounded-lg"
      src="https://opensource.posit.co/blog/2026-05-22_ai-newsletter/images/gemma.svg"
      alt="Screenshot of Posit Assistant in RStudio with Gemma 4 selected in the model picker."  title="Gemma 4 selected in the Posit Assistant model picker." 
      loading="lazy"
    ><figcaption class="text-sm text-center text-gray-500">Gemma 4 selected in the Posit Assistant model picker.</figcaption>
  </figure></div>
</p>
<p>The primary motivation for including a model like Gemma 4 in Posit AI is cost. We wanted to include a budget model that is still capable and reliable enough to power Posit Assistant across a wide range of tasks.</p>
<p>We recommend using the more capable models with longer-running tasks (e.g., implementing a feature that touches code across many files in a package), but Gemma is still capable of assisting with basic data analysis. You can read more about model choice in the <a href="https://posit.co/blog/gemma-4-new-budget-focused-model-posit-ai/" target="_blank" rel="noopener">announcement blog post</a>.</p>
<h3 id="tidy-design-principles">Tidy design principles
</h3>
<p><strong>Hadley Wickham restarted his <a href="https://tidydesign.substack.com/p/returning-to-life" target="_blank" rel="noopener">Tidy design principles substack</a> this week</strong>, starting with a thoughtful reflection on the tension between LLMs&rsquo; helpful and harmful effects.</p>
<p>The following is an excerpt of that post, but we encourage you to read the entire thing.</p>
<blockquote>
<p>In future posts, I’ll get more technical, but I wanted to begin by acknowledging your likely deeply conflicted feelings about AI.</p>
<p><strong>Programming accessibility.</strong> There are tons of people who could benefit from a programming language like R, but can’t justify the investment in learning it. AI has dramatically lowered that barrier and you can now get many of the benefits of reproducible programming with R much faster than you could before. Similarly, effective usage of git is now within reach to a much broader audience.</p>
<p><strong>Translation.</strong> While machine translations are still far from perfect, their quality has improved radically in the last few years. This means that much more of the programming ecosystem is now available to the majority of the world who are not fluent English speakers.</p>
<p><strong>Voice input.</strong> Voice input is a super exciting technology because it means that you no longer need to be a fluent touch typist in order to quickly get your thoughts into a computer. (Not to mention making a lot more technology available if you can’t read or write.) That’s a meaningful expansion of who gets to participate in technology.</p>
<p><strong>Wide and shallow expertise.</strong> I love Tukey’s quote that statisticians get to play in everyone’s backyard. And it’s now easier than ever thanks to AI. AI will not make you an expert but can give you shallow expertise in basically anything you’re curious about. I think that’s pretty cool.</p>
<p>Finally, I have found AI to be a tremendous accelerator in my own work. It’s allowed me to fix 100s of issue in core infrastructure packages like roxygen2 and testthat. This is not AI slop; this is carefully vetted code that I can now write ~2-5x faster than I could before.</p>
<p>But you can’t use AI without also considering the harms, of which there are many.</p>
<p><strong>Environmental impact.</strong> At the individual level, I believe that if you want to reduce your environmental footprint, there are higher-leverage changes that you can make. But at the societal level, the picture is more concerning: the rush to create new data centers is increasing need for electricity and water, and leading companies to rollback their climate commitments.</p>
<p><strong>Copyright theft.</strong> LLMs are trained on vast quantities of copyrighted material, taken at an unprecedented and industrial scale, without permission or compensation.</p>
<p><strong>Concentration of wealth.</strong> The AI craze is pushing more and more money into the hands of fewer and fewer people. I find the concentration of wealth and power into the hands of a very small number of people to be genuinely disturbing and I think is something that we should all be concerned about.</p>
<p><strong>Intellectual laziness.</strong> AI supports a kind of shallow engagement where you never have to strain your brain on any task. The path of least resistance is to disengage and just let the model handle it. You no longer have to experience any mental discomfort, and thus you never really learn.</p>
<p><strong>Equity and access.</strong> I’ve built my career around open source software, and one of the things I love about it is that it’s available to everyone, everywhere in the world, regardless of their means. That’s not possible with AI. The best tools cost real money, usually charged in US dollars, and that makes them out of reach for a lot of people in a lot of places.</p>
</blockquote>
]]></description>
      <enclosure url="https://opensource.posit.co/blog/2026-05-22_ai-newsletter/images/newsletter.png" length="223008" type="image/png" />
    </item>
    <item>
      <title>In Defense of YAML</title>
      <link>https://opensource.posit.co/blog/2026-05-21_in-defense-of-yaml/</link>
      <pubDate>Thu, 21 May 2026 00:00:00 +0000</pubDate>
      <guid>https://opensource.posit.co/blog/2026-05-21_in-defense-of-yaml/</guid>
      <dc:creator>Rich Iannone</dc:creator>
      <dc:creator>Tomasz Kalinowski</dc:creator><description><![CDATA[<p>Every programmer has opinions about configuration files. These opinions
tend to be strongly held and inversely proportional to the stakes
involved. In the last few years, the consensus view has shifted: YAML is
bad, TOML is good, and enthusiastic users of YAML just might be plainly
uninformed. This post takes a different view. We intend to present an
argument for YAML which is grounded in history, its specification, and
the state of tooling in 2026.</p>
<p>The case against YAML was, for a long time, a reasonable one. The format
attracted its critics for real reasons, through years of surprising
behavior that burned even careful users. But the specification evolved,
and the tooling is finally catching up. To understand why the current
consensus is outdated, we need to trace the lineage of configuration
formats themselves, because this sort of argument has played out before.</p>
<h2 id="a-brief-history-of-configuration-formats">A brief history of configuration formats
</h2>
<p>The INI file emerged in the early 1980s alongside MS-DOS and the first
versions of Windows.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> It was the simplest thing that could possibly
work: key-value pairs, grouped into sections denoted by square brackets,
with semicolons for comments. They are flat, readable, and
human-editable. For the needs of that era (like configuring device
drivers, specifying font paths, or setting application preferences) it
was entirely adequate. Its only real limitation was structural: you
could not nest deeper than one level, and there was no formal
specification, which meant every parser implemented its own dialect. But
for two decades, this was fine.</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="k">[boot]</span>
</span></span><span class="line"><span class="cl"><span class="na">shell</span><span class="o">=</span><span class="s">COMMAND.COM</span>
</span></span><span class="line"><span class="cl"><span class="na">device</span><span class="o">=</span><span class="s">HIMEM.SYS</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">[display]</span>
</span></span><span class="line"><span class="cl"><span class="na">resolution</span><span class="o">=</span><span class="s">640x480</span>
</span></span><span class="line"><span class="cl"><span class="na">colors</span><span class="o">=</span><span class="s">256</span></span></span></code></pre></div></div>
<p>Then came XML. In the late 1990s, the enterprise software world adopted
angle brackets broadly. XML could represent arbitrary hierarchy. It had
schemas, namespaces, transformations. It was self-describing. For a
while it seemed as though the debate was settled. But XML configuration
files grew large in practice. Anyone who maintained a Java <code>web.xml</code> or
an Ant build file in 2003 knows what it was like to edit dozens of
nested elements just to change a database connection string. The
verbosity made the files difficult to maintain by hand, which is
precisely what configuration files demand.</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;web-app&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;servlet&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;servlet-name&gt;</span>myServlet<span class="nt">&lt;/servlet-name&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;servlet-class&gt;</span>com.example.MyServlet<span class="nt">&lt;/servlet-class&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;init-param&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;param-name&gt;</span>database.url<span class="nt">&lt;/param-name&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;param-value&gt;</span>jdbc:postgresql://localhost/mydb<span class="nt">&lt;/param-value&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/init-param&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;/servlet&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/web-app&gt;</span></span></span></code></pre></div></div>
<p>JSON appeared as the lightweight reaction. Douglas Crockford, who claims
to have discovered rather than invented the format,<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> offered the
simplicity of the JavaScript object literal: curly braces, square
brackets, quoted strings, and a tiny set of types. JSON displaced XML in
web APIs through the late 2000s and early 2010s. But as people began
using it for configuration (rather than machine-to-machine data
exchange), its limitations became apparent. JSON has no comments. It has
no multiline strings. Trailing commas are illegal. These are reasonable
constraints for a serialization format, but they make JSON miserable for
files that humans must author and maintain. The removal of comments from
JSON&rsquo;s spec was, according to Crockford himself, motivated by people
abusing them for parsing directives.<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> It was the right call for data
interchange, but it left a gap.</p>
<p>YAML (2001) and TOML (2013) each arose to fill that gap, and both
positioned themselves explicitly against what came before. YAML offered
the full expressive power of a serialization language (including
arbitrary nesting, multiple documents, references, and custom types)
with a syntax built on indentation rather than brackets. TOML, created
by Tom Preston-Werner a dozen years later, was a reaction to YAML&rsquo;s
complexity: it aimed to be a &ldquo;standardized INI&rdquo; with explicit typing,
obvious semantics, and a formal specification.<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> The pattern repeats
in each generation: the previous format&rsquo;s excess becomes the new
format&rsquo;s founding grievance. What is interesting about the current
moment is that YAML&rsquo;s problems were not inherent to the format&rsquo;s design.
They were artifacts of a particular specification version and the
parsers frozen on it.</p>
<h2 id="the-case-against-yaml-as-it-was">The case against YAML (as it was)
</h2>
<p>The criticisms of YAML are not fabricated. They reflect real experiences
that real programmers had over many years.</p>
<p>The most infamous problem is the Norway incident, which has become
shorthand for YAML&rsquo;s implicit typing behavior. In YAML 1.1, the bare
scalar <code>NO</code> was interpreted as the boolean value <code>false</code>. This meant
that a list of country codes would silently transform Norway into a
falsehood:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="c"># What you wrote:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">countries</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">dk</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">fi</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">is</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="kc">no</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">se</span></span></span></code></pre></div></div>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># What YAML 1.1 parsed:</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="s2">&#34;dk&#34;</span><span class="p">,</span> <span class="s2">&#34;fi&#34;</span><span class="p">,</span> <span class="s2">&#34;is&#34;</span><span class="p">,</span> <span class="n">false</span><span class="p">,</span> <span class="s2">&#34;se&#34;</span><span class="p">]</span></span></span></code></pre></div></div>
<p>The same applied to <code>yes</code>, <code>on</code>, <code>off</code>, <code>y</code>, <code>n</code>, and various
capitalizations thereof. Ruud van Asseldonk&rsquo;s widely-circulated &ldquo;YAML
Document from Hell&rdquo;<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup> catalogued these and other problems: port
mappings like <code>22:22</code> parsed as sexagesimal (base-60) integers, version
numbers like <code>10.23</code> parsed as floats rather than strings, date-like
values parsed as timestamps,<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup> and tags beginning with <code>!</code> could
trigger arbitrary code execution in some parsers.</p>
<p>This is not only a country-code problem. In data science and machine
learning code, <code>n</code> and <code>y</code> are natural variable names:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">variables</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">x</span><span class="p">:</span><span class="w"> </span><span class="l">features</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">n</span><span class="p">:</span><span class="w"> </span><span class="l">sample_size</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">y</span><span class="p">:</span><span class="w"> </span><span class="l">target</span></span></span></code></pre></div></div>
<p>Under YAML 1.1&rsquo;s implicit boolean rules, a parser can resolve those keys
as booleans instead of strings:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="p">{</span><span class="s2">&#34;variables&#34;</span><span class="p">:</span> <span class="p">{</span><span class="s2">&#34;x&#34;</span><span class="p">:</span> <span class="s2">&#34;features&#34;</span><span class="p">,</span> <span class="kc">False</span><span class="p">:</span> <span class="s2">&#34;sample_size&#34;</span><span class="p">,</span> <span class="kc">True</span><span class="p">:</span> <span class="s2">&#34;target&#34;</span><span class="p">}}</span></span></span></code></pre></div></div>
<p>These were not edge cases encountered only by the reckless. They emerged
from the YAML 1.1 specification&rsquo;s design philosophy of aggressive
implicit typing, where the parser attempted to be &ldquo;helpful&rdquo; by guessing
the intended type of unquoted values. The intention was readability (you
could write <code>true</code> without quotes and get a boolean), but the result was
unpredictable behavior in practice. Configuration files are precisely
the domain where surprising behavior is least tolerable. They are edited
infrequently, often by people who did not write the original file, and a
silent misparse can propagate through a system undetected for months.</p>
<p>The complexity of the full specification compounded the problem. The
YAML 1.2.2 spec runs to ten chapters with sections numbered four levels
deep.<sup id="fnref:7"><a href="#fn:7" class="footnote-ref" role="doc-noteref">7</a></sup> There are dozens of ways to express multiline strings. Anchors
and aliases create a reference system that, while powerful, adds
conceptual weight far beyond what most configuration tasks require. And
the security implications of tag-based object deserialization (the
<code>yaml.load()</code> vulnerability in Python) became a well-known attack
vector.<sup id="fnref:8"><a href="#fn:8" class="footnote-ref" role="doc-noteref">8</a></sup> All of these criticisms were valid, and they were valid
specifically of YAML 1.1 and the tooling ecosystem built around it.</p>
<h2 id="what-toml-gets-right">What TOML gets right
</h2>
<p>TOML deserves some credit. For flat or shallow configuration structures,
it is clean, readable, and unambiguous. The syntax is familiar to anyone
who has seen an INI file, but with the addition of explicit types, a
formal specification, and support for nested tables via dot-separated
keys.</p>
<p>Consider a <code>pyproject.toml</code> or a <code>Cargo.toml</code>. These files are typically
one or two levels deep, with well-defined sections and predictable
content. TOML handles them well. Strings are always quoted, so there is
no ambiguity about whether <code>no</code> is a boolean or the word &ldquo;no&rdquo;. Integers
are integers, floats are floats, and dates are first-class types.
Comments work exactly as you would expect. For this class of problem,
TOML works well, and its adoption by the Python packaging ecosystem (PEP
518)<sup id="fnref:9"><a href="#fn:9" class="footnote-ref" role="doc-noteref">9</a></sup> and the Rust community (Cargo) makes sense.</p>
<p>TOML also benefits from simplicity of implementation. The specification
is short enough that a competent programmer can write a compliant parser
in a weekend. This means that the ecosystem of parsers is large,
well-tested, and consistent. There is no equivalent of the YAML 1.1/1.2
version split. TOML 1.0 is TOML 1.0, everywhere. These are real
advantages.</p>
<h2 id="where-toml-strains">Where TOML strains
</h2>
<p>The trouble begins when configuration needs to express depth. TOML&rsquo;s
handling of nested structures relies on either dot-separated section
headers (<code>[servers.alpha]</code>) or arrays of tables (<code>[[products]]</code>), both
of which become difficult to read as the nesting increases. This is not
a theoretical concern: it is the reason that Martin Vejnár, the author
of the PyTOML parser, eventually abandoned his own project. When asked
whether his library should become a dependency for pip, he declined and
explained: &ldquo;TOML is a bad file format. It looks good at first glance,
and for really really trivial things it is probably good. But once I
started using it and the configuration schema became more complex, I
found the syntax ugly and hard to read&rdquo;.<sup id="fnref:10"><a href="#fn:10" class="footnote-ref" role="doc-noteref">10</a></sup></p>
<p>Consider a moderately complex configuration. In YAML, the indentation
communicates the hierarchy at a glance:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">services</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">web</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">nginx:latest</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">environment</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">DB_HOST</span><span class="p">:</span><span class="w"> </span><span class="l">postgres</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">DB_PORT</span><span class="p">:</span><span class="w"> </span><span class="m">5432</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">resources</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">limits</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">memory</span><span class="p">:</span><span class="w"> </span><span class="l">512M</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">cpu</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;0.5&#34;</span></span></span></code></pre></div></div>
<p>The equivalent in TOML requires repeating the full path in each section
header:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">services</span><span class="p">.</span><span class="nx">web</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">image</span> <span class="p">=</span> <span class="s2">&#34;nginx:latest&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">services</span><span class="p">.</span><span class="nx">web</span><span class="p">.</span><span class="nx">environment</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">DB_HOST</span> <span class="p">=</span> <span class="s2">&#34;postgres&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">DB_PORT</span> <span class="p">=</span> <span class="mi">5432</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">services</span><span class="p">.</span><span class="nx">web</span><span class="p">.</span><span class="nx">resources</span><span class="p">.</span><span class="nx">limits</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">memory</span> <span class="p">=</span> <span class="s2">&#34;512M&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">cpu</span> <span class="p">=</span> <span class="s2">&#34;0.5&#34;</span></span></span></code></pre></div></div>
<p>The reader must mentally reconstruct the tree from a flat sequence of
qualified names. The StrictYAML documentation measured this concretely:
equivalent TOML files use approximately 50% more characters to represent
the same data, largely because of the repeated path prefixes.<sup id="fnref:11"><a href="#fn:11" class="footnote-ref" role="doc-noteref">11</a></sup></p>
<p>There is also the matter of meaningful indentation itself. Python
demonstrated decades ago that indentation as structure is not a weakness
but a strength: it eliminates the class of bugs where visual structure
disagrees with syntactic structure. YAML inherits this property. TOML
does not require indentation (though many authors add it voluntarily, as
a non-parsed visual aid), which means that the relationship between a
key and its containing table exists only in the section header, not in
the physical layout of the file. For deeply nested configurations, this
makes TOML files harder to scan and harder to edit confidently.</p>
<h2 id="what-yaml-12-changed">What YAML 1.2 changed
</h2>
<p>The YAML 1.2 specification was published in 2009, with a clarifying
revision (1.2.2) completed in October 2021.<sup id="fnref:12"><a href="#fn:12" class="footnote-ref" role="doc-noteref">12</a></sup> Its changes address the
complaints described above directly.</p>
<p>The implicit typing that created the Norway problem is gone. In the YAML
1.2 Core Schema, only <code>true</code> and <code>false</code> (and their capitalizations
<code>True</code>, <code>False</code>, <code>TRUE</code>, <code>FALSE</code>) are recognized as booleans. The words
<code>yes</code>, <code>no</code>, <code>on</code>, <code>off</code>, <code>y</code>, and <code>n</code> are plain strings. Sexagesimal
number literals (the <code>22:22</code> problem) are removed entirely. Timestamp is
no longer a core type, so an unquoted <code>2026-05-05</code> is a string under the
core schema rather than an automatically detected date. JSON is now a
strict, proper subset of YAML 1.2, which means any valid JSON document
parses identically as YAML. The tag resolution rules are tightened and
clarified. The specification itself, while still substantial, is written
more clearly and maintained openly on GitHub.</p>
<p>In short, the YAML that people complain about is YAML 1.1. The
specification that actually governs the language today is a different,
safer, more predictable document. The problem is that most people&rsquo;s
experience of YAML is mediated not by the specification but by their
parser, and for most Python users, that parser has been PyYAML, which
implements YAML 1.1 and has not changed its core semantics since 2006.</p>
<h2 id="the-python-yaml-parser-landscape">The Python YAML parser landscape
</h2>
<p><a href="https://github.com/yaml/pyyaml" target="_blank" rel="noopener">PyYAML</a>, written by Kirill Simonov in
2006, is the de facto standard YAML library in Python. It wraps LibYAML
(a C library) for performance and provides a pure-Python fallback. It is
downloaded millions of times per week, it is a dependency of countless
packages, and it implements YAML 1.1. This last fact is the root of most
YAML complaints in the Python ecosystem. When someone says &ldquo;YAML parsed
my country code as a boolean&rdquo;, they are describing PyYAML&rsquo;s behavior,
not YAML&rsquo;s specification. PyYAML&rsquo;s GitHub repository shows over 200 open
issues and 100 open pull requests.<sup id="fnref:13"><a href="#fn:13" class="footnote-ref" role="doc-noteref">13</a></sup> The project is maintained but
moves slowly, and a major version bump to YAML 1.2 semantics has not
materialized.</p>
<p>The <a href="https://github.com/ruamel/yaml" target="_blank" rel="noopener"><code>ruamel.yaml</code></a> library, maintained
by Anthon van der Neut, offers YAML 1.2 support with round-trip
preservation of comments, flow style, and key order.<sup id="fnref:14"><a href="#fn:14" class="footnote-ref" role="doc-noteref">14</a></sup> It is widely
used and is significantly more capable than PyYAML for tasks requiring
comment preservation or format-aware editing. However, it is primarily a
pure-Python implementation in its default round-trip mode, which makes
it considerably slower than PyYAML&rsquo;s C-backed fast path. Its packaging
history has also been complicated: namespace package issues and a
dependency chain that has occasionally confused deployment pipelines.</p>
<p><a href="https://github.com/crdoconnor/strictyaml" target="_blank" rel="noopener">StrictYAML</a> takes a different
approach entirely, implementing a deliberate subset of YAML with all
implicit typing removed, no tags, no anchors, and no flow style.<sup id="fnref:15"><a href="#fn:15" class="footnote-ref" role="doc-noteref">15</a></sup>
Philosophically it is closer to TOML than to full YAML: a safe, simple
format that happens to use YAML&rsquo;s indentation syntax. It is Python-only,
has no implementations in other languages, and does not aim for spec
compliance.</p>
<p>What has been missing from this landscape is a library that is fast,
fully 1.2-compliant, and simple enough to use as a drop-in replacement
for PyYAML&rsquo;s basic interface.</p>
<h2 id="introducing-py-yaml12">Introducing py-yaml12
</h2>
<p>The <a href="https://github.com/posit-dev/py-yaml12" target="_blank" rel="noopener"><code>py-yaml12</code></a> library is a
YAML 1.2 parser and formatter for Python, implemented in Rust for speed
and correctness. It is built on the <code>saphyr</code> crate<sup id="fnref:16"><a href="#fn:16" class="footnote-ref" role="doc-noteref">16</a></sup> (a Rust YAML
library) and exposes a minimal, focused API: <code>parse_yaml()</code> and
<code>read_yaml()</code> for loading, <code>format_yaml()</code> and <code>write_yaml()</code> for
serialization.</p>
<h3 id="simple">Simple
</h3>
<p>The design philosophy is straightforward. For the vast majority of use
cases, you work with plain Python builtins end to end: <code>dict</code>, <code>list</code>,
<code>int</code>, <code>float</code>, <code>str</code>, and <code>None</code>. There is no special document class,
no custom node types in the common path. Because YAML 1.2 is a superset
of JSON, all valid JSON parses identically. The library achieves 100%
compliance with the yaml-test-suite,<sup id="fnref:17"><a href="#fn:17" class="footnote-ref" role="doc-noteref">17</a></sup> the community-maintained
corpus of edge cases and conformance tests.</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">yaml12</span> <span class="kn">import</span> <span class="n">parse_yaml</span><span class="p">,</span> <span class="n">format_yaml</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">rich.pretty</span> <span class="kn">import</span> <span class="n">pprint</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">config</span> <span class="o">=</span> <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">server:
</span></span></span><span class="line"><span class="cl"><span class="s2">  host: 0.0.0.0
</span></span></span><span class="line"><span class="cl"><span class="s2">  port: 8080
</span></span></span><span class="line"><span class="cl"><span class="s2">  debug: false
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">database:
</span></span></span><span class="line"><span class="cl"><span class="s2">  url: postgres://localhost/mydb
</span></span></span><span class="line"><span class="cl"><span class="s2">  pool_size: 5
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">regions:
</span></span></span><span class="line"><span class="cl"><span class="s2">  - us-east-1
</span></span></span><span class="line"><span class="cl"><span class="s2">  - eu-west-1
</span></span></span><span class="line"><span class="cl"><span class="s2">  - no         # Norway, not false
</span></span></span><span class="line"><span class="cl"><span class="s2">&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">data</span> <span class="o">=</span> <span class="n">parse_yaml</span><span class="p">(</span><span class="n">config</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">pprint</span><span class="p">(</span><span class="n">data</span><span class="p">)</span></span></span></code></pre></div></div>
<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold">{</span>
<span style="color: #7fbf7f; text-decoration-color: #7fbf7f">│   </span><span style="color: #008000; text-decoration-color: #008000">'server'</span>: <span style="font-weight: bold">{</span><span style="color: #008000; text-decoration-color: #008000">'host'</span>: <span style="color: #008000; text-decoration-color: #008000">'0.0.0.0'</span>, <span style="color: #008000; text-decoration-color: #008000">'port'</span>: <span style="color: #008080; text-decoration-color: #008080; font-weight: bold">8080</span>, <span style="color: #008000; text-decoration-color: #008000">'debug'</span>: <span style="color: #ff0000; text-decoration-color: #ff0000; font-style: italic">False</span><span style="font-weight: bold">}</span>,
<span style="color: #7fbf7f; text-decoration-color: #7fbf7f">│   </span><span style="color: #008000; text-decoration-color: #008000">'database'</span>: <span style="font-weight: bold">{</span><span style="color: #008000; text-decoration-color: #008000">'url'</span>: <span style="color: #008000; text-decoration-color: #008000">'postgres://localhost/mydb'</span>, <span style="color: #008000; text-decoration-color: #008000">'pool_size'</span>: <span style="color: #008080; text-decoration-color: #008080; font-weight: bold">5</span><span style="font-weight: bold">}</span>,
<span style="color: #7fbf7f; text-decoration-color: #7fbf7f">│   </span><span style="color: #008000; text-decoration-color: #008000">'regions'</span>: <span style="font-weight: bold">[</span><span style="color: #008000; text-decoration-color: #008000">'us-east-1'</span>, <span style="color: #008000; text-decoration-color: #008000">'eu-west-1'</span>, <span style="color: #008000; text-decoration-color: #008000">'no'</span><span style="font-weight: bold">]</span>
<span style="font-weight: bold">}</span>
</pre>
<p>Notice the <code>no</code> in the regions list. Under PyYAML (YAML 1.1), this would
silently become <code>False</code>. Under <code>py-yaml12</code> (YAML 1.2), it is the string
<code>&quot;no&quot;</code>, as the specification requires. This single behavioral difference
encapsulates the entire argument: the format is not broken, the old
tooling was.</p>
<p>The file API is similarly direct:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">yaml12</span> <span class="kn">import</span> <span class="n">write_yaml</span><span class="p">,</span> <span class="n">read_yaml</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">path</span> <span class="o">=</span> <span class="s2">&#34;config.yaml&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">write_yaml</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">path</span><span class="p">)</span></span></span></code></pre></div></div>
<p>The round-trip is lossless. Writing a Python dictionary to disk and
reading it back produces an identical object:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">round_tripped</span> <span class="o">=</span> <span class="n">read_yaml</span><span class="p">(</span><span class="n">path</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">assert</span> <span class="n">round_tripped</span> <span class="o">==</span> <span class="n">data</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;Round-trip matches: </span><span class="si">{</span><span class="n">round_tripped</span> <span class="o">==</span> <span class="n">data</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span></span></span></code></pre></div></div>
<div class="code-block"><pre tabindex="0"><code>Round-trip matches: True</code></pre></div>
<p>For advanced YAML features like tagged values, <code>py-yaml12</code> provides the
<code>Yaml</code> wrapper type. It is opt-in and unnecessary for typical
configuration work.</p>
<h3 id="safe">Safe
</h3>
<p>The defaults in <code>py-yaml12</code> are not just about ergonomics and
simplicity; they also improve safety. PyYAML shows the risk of the
opposite approach: treating tags as instructions can execute arbitrary
Python code simply by reading a YAML file.<sup id="fnref:18"><a href="#fn:18" class="footnote-ref" role="doc-noteref">18</a></sup></p>
<p>For example, someone can produce a YAML file that aliases PyYAML&rsquo;s
Python object-apply tag namespace:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">dangerous_yaml</span> <span class="o">=</span> <span class="s2">&#34;&#34;&#34;</span><span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="s2">%TAG !py! tag:yaml.org,2002:python/object/apply:
</span></span></span><span class="line"><span class="cl"><span class="s2">--- !py!builtins.eval
</span></span></span><span class="line"><span class="cl"><span class="s2">- &#34;(__import__(&#39;os&#39;).environ.__setitem__(&#39;YAML_PAYLOAD_RAN&#39;, &#39;1&#39;), {&#39;debug&#39;: False, &#39;retries&#39;: 3})[1]&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s2">&#34;dangerous.yaml&#34;</span><span class="p">,</span> <span class="s2">&#34;w&#34;</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">f</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">dangerous_yaml</span><span class="p">)</span></span></span></code></pre></div></div>
<p>Then a user expecting only to load a config file runs that code during
parsing:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">yaml</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s2">&#34;dangerous.yaml&#34;</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">data</span> <span class="o">=</span> <span class="n">yaml</span><span class="o">.</span><span class="n">load</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="n">Loader</span><span class="o">=</span><span class="n">yaml</span><span class="o">.</span><span class="n">Loader</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">data</span><span class="p">)</span></span></span></code></pre></div></div>
<div class="code-block"><pre tabindex="0"><code>{&#39;debug&#39;: False, &#39;retries&#39;: 3}</code></pre></div>
<p>The <code>yaml.load()</code> call looks like ordinary data loading: it returns an
ordinary dictionary. But producing that dictionary executed Python code
first. Unless you inspect the YAML itself, nothing in the result tells
you that happened.</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">os</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="p">[</span><span class="s2">&#34;YAML_PAYLOAD_RAN&#34;</span><span class="p">]</span></span></span></code></pre></div></div>
<div class="code-block"><pre tabindex="0"><code>&#39;1&#39;</code></pre></div>
<p>In contrast, <code>py-yaml12</code> keeps an unhandled tag as data unless you
explicitly opt in:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">yaml12</span> <span class="kn">import</span> <span class="n">read_yaml</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">read_yaml</span><span class="p">(</span><span class="s2">&#34;dangerous.yaml&#34;</span><span class="p">)</span></span></span></code></pre></div></div>
<div class="code-block"><pre tabindex="0"><code>Yaml(value=[&#34;(__import__(&#39;os&#39;).environ.__setitem__(&#39;YAML_PAYLOAD_RAN&#39;, &#39;1&#39;), {&#39;debug&#39;: False, &#39;retries&#39;: 3})[1]&#34;], tag=&#39;tag:yaml.org,2002:python/object/apply:builtins.eval&#39;)</code></pre></div>
<h3 id="fast">Fast
</h3>
<p>Performance is a practical concern for any library that might be called
in startup paths or CI pipelines. The <code>py-yaml12</code> benchmarks<sup id="fnref:19"><a href="#fn:19" class="footnote-ref" role="doc-noteref">19</a></sup>
compare read and write performance against PyYAML (both its default
pure-Python path and the fast CSafeLoader/CSafeDumper backed by LibYAML)
and <code>ruamel.yaml</code>, across file sizes ranging from kilobytes to
megabytes. Because the core parsing and formatting logic is implemented
in compiled Rust rather than interpreted Python, <code>py-yaml12</code> is
competitive with PyYAML&rsquo;s C extension while maintaining full 1.2
compliance. As of this writing, few other Python libraries offer both.</p>
<h2 id="conclusion">Conclusion
</h2>
<p>The YAML-versus-TOML debate, as typically conducted, is an argument
against a format that no longer exists in its problematic form. The
complaints are real, but they are historical. They describe YAML 1.1 as
mediated by PyYAML, not YAML 1.2 as specified and now properly
implemented. TOML remains a good choice for shallow, flat
configurations, and <code>pyproject.toml</code> is well-suited to its role. But the
claim that YAML is inherently unsafe or unpredictable does not hold
against a compliant 1.2 parser.</p>
<p>This is, in the end, a familiar pattern in computing. Every generation
of configuration format is a correction of the previous generation&rsquo;s
excesses: INI was too flat, so XML added hierarchy; XML was too verbose,
so JSON stripped it bare; JSON was too austere for humans, so YAML and
TOML each offered different compromises. The interesting question is
never &ldquo;which format is best in the abstract&rdquo; but &ldquo;which format, with
which tooling, serves this particular task well&rdquo;. For complex, nested,
human-authored configuration, YAML 1.2 with a modern parser is a strong
answer. Perhaps in another decade, something new will arise to correct
YAML&rsquo;s remaining rough edges, and the cycle will continue. That is how
formats improve.</p>
<p>In the meantime, you can <code>pip install py-yaml12</code> and see what a modern,
spec-compliant YAML experience looks like in Python.</p>
<p>And if you work in R, the <a href="https://github.com/posit-dev/r-yaml12" target="_blank" rel="noopener"><code>r-yaml12</code></a>
package brings the same benefits: full YAML 1.2 compliance, Rust-backed
performance, and safe defaults. Everything good about the Python package
is in the R version as well.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Wikipedia, &ldquo;<a href="https://en.wikipedia.org/wiki/INI_file" target="_blank" rel="noopener">INI file</a>&rdquo;.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Douglas Crockford, &ldquo;<a href="https://www.youtube.com/watch?v=-C-JoyNuQJs" target="_blank" rel="noopener">The JSON
Saga</a>&rdquo;, presentation at
Yahoo, 2011.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>Douglas Crockford, in the same presentation (see [^2]),
explains that comments were removed because &ldquo;people were using
comments to hold parsing directives&rdquo;.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>Tom Preston-Werner, &ldquo;<a href="https://toml.io/en/" target="_blank" rel="noopener">TOML: Tom&rsquo;s Obvious Minimal
Language</a>&rdquo;.&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p>Ruud van Asseldonk, &ldquo;<a href="https://ruudvanasseldonk.com/2023/01/11/the-yaml-document-from-hell" target="_blank" rel="noopener">The yaml document from
hell</a>&rdquo;,
January 2023.&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6">
<p>Examples include Stack Overflow, &ldquo;<a href="https://stackoverflow.com/questions/50900727/skip-converting-entities-while-loading-a-yaml-string-using-pyyaml" target="_blank" rel="noopener">Skip converting entities while
loading a yaml string (using
PyYAML)</a>&rdquo;;
PyYAML issue <a href="https://github.com/yaml/pyyaml/issues/382" target="_blank" rel="noopener">#382</a>; and
ruamel.yaml ticket
<a href="https://sourceforge.net/p/ruamel-yaml/tickets/509/" target="_blank" rel="noopener">#509</a>.&#160;<a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:7">
<p>&ldquo;<a href="https://yaml.org/spec/1.2.2/" target="_blank" rel="noopener">YAML Ain&rsquo;t Markup Language (YAML) Version 1.2, Revision
1.2.2</a>&rdquo;, October 2021.&#160;<a href="#fnref:7" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:8">
<p>See
<a href="https://nvd.nist.gov/vuln/detail/CVE-2017-18342" target="_blank" rel="noopener">CVE-2017-18342</a>,
<a href="https://nvd.nist.gov/vuln/detail/CVE-2020-1747" target="_blank" rel="noopener">CVE-2020-1747</a>,
<a href="https://nvd.nist.gov/vuln/detail/CVE-2020-14343" target="_blank" rel="noopener">CVE-2020-14343</a>,
PyYAML issue <a href="https://github.com/yaml/pyyaml/issues/420" target="_blank" rel="noopener">#420</a>, and
the PyYAML wiki page on <a href="https://github.com/yaml/pyyaml/wiki/PyYAML-yaml.load%28input%29-Deprecation" target="_blank" rel="noopener"><code>yaml.load(input)</code>
deprecation</a>.&#160;<a href="#fnref:8" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:9">
<p>Brett Cannon et al., &ldquo;<a href="https://peps.python.org/pep-0518/" target="_blank" rel="noopener">PEP 518 &ndash; Specifying Minimum Build System
Requirements for Python
Projects</a>&rdquo;, 2016.&#160;<a href="#fnref:9" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:10">
<p>Martin Vejnár (avakar), comment on
<a href="https://github.com/avakar/pytoml/issues/15#issuecomment-217804599" target="_blank" rel="noopener">avakar/pytoml#15</a>,
May 2016.&#160;<a href="#fnref:10" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:11">
<p>StrictYAML documentation, &ldquo;<a href="https://hitchdev.com/strictyaml/why-not/toml/" target="_blank" rel="noopener">What is wrong with
TOML?</a>&rdquo;.&#160;<a href="#fnref:11" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:12">
<p>&ldquo;<a href="https://yaml.org/spec/1.2.2/" target="_blank" rel="noopener">YAML Ain&rsquo;t Markup Language (YAML) Version 1.2, Revision
1.2.2</a>&rdquo;, October 2021.&#160;<a href="#fnref:12" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:13">
<p><a href="https://github.com/yaml/pyyaml" target="_blank" rel="noopener">PyYAML GitHub repository</a>,
accessed April 2026.&#160;<a href="#fnref:13" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:14">
<p><a href="https://pypi.org/project/ruamel.yaml/" target="_blank" rel="noopener">ruamel.yaml on PyPI</a>.&#160;<a href="#fnref:14" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:15">
<p><a href="https://hitchdev.com/strictyaml/" target="_blank" rel="noopener">StrictYAML documentation</a>.&#160;<a href="#fnref:15" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:16">
<p><a href="https://crates.io/crates/saphyr" target="_blank" rel="noopener">saphyr crate</a> on crates.io.&#160;<a href="#fnref:16" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:17">
<p><a href="https://github.com/yaml/yaml-test-suite" target="_blank" rel="noopener">yaml-test-suite</a> on
GitHub.&#160;<a href="#fnref:17" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:18">
<p>See the PyYAML object-construction examples in issue #420, cited
above.&#160;<a href="#fnref:18" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:19">
<p>py-yaml12,
&ldquo;<a href="https://posit-dev.github.io/py-yaml12/benchmarks/benchmarks.html" target="_blank" rel="noopener">Benchmarks</a>&rdquo;.&#160;<a href="#fnref:19" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></description>
      <enclosure url="https://opensource.posit.co/blog/2026-05-21_in-defense-of-yaml/assets/in-defense-of-yaml.png" length="1094864" type="image/png" />
    </item>
    <item>
      <title>Welcome to the New Home for Posit Open Source</title>
      <link>https://opensource.posit.co/blog/2026-05-19_welcome-to-the-new-home-for-posit-open-source/</link>
      <pubDate>Tue, 19 May 2026 00:00:00 +0000</pubDate>
      <guid>https://opensource.posit.co/blog/2026-05-19_welcome-to-the-new-home-for-posit-open-source/</guid>
      <dc:creator>Jeroen Janssens</dc:creator><description><![CDATA[<p>For over 15 years, Posit has been building wonderful open source software and content. Over the years, that wealth of knowledge expanded across hundreds of package sites, dozens of blogs, and multiple YouTube channels. While this rapid, creative growth was wonderful, it also created a practical problem: it became difficult for you to keep up with it all.</p>
<p>Today, we are changing that. I am very excited to announce the Posit Open Source website, <a href="http://opensource.posit.co" target="_blank" rel="noopener">opensource.posit.co</a>. I am incredibly proud of the team that built it.</p>
<p>This new central hub connects you to our open source software, blog posts, videos, events, and cheatsheets. We built the site on three core principles: Curate, Connect, and Contribute.</p>
<h2 id="curate">Curate
</h2>
<p>It starts with bringing our content together. <a href="https://opensource.posit.co/people/charlotte-wickham/" target="_blank" rel="noopener">Charlotte Wickham</a> took over 900 posts that were originally scattered across a dozen different blogs and moved them all into one single open source blog. We also gathered over 1,600 videos that were scattered across multiple YouTube channels. Now, you can find all of this content curated and easily accessible in one place.</p>
<p><div class="not-prose"><figure>
    <img class="h-auto max-w-full rounded-lg"
      src="https://opensource.posit.co/blog/2026-05-19_welcome-to-the-new-home-for-posit-open-source/osw-mobile.gif"
      alt="" 
      loading="lazy"
    >
  </figure></div>
</p>
<h2 id="connect">Connect
</h2>
<p>Our software remains front and center. Every package now gets its own profile featuring a short description, links to the repository, a list of contributors, and related resources.</p>
<p><div class="not-prose"><figure>
    <img class="h-auto max-w-full rounded-lg"
      src="https://opensource.posit.co/blog/2026-05-19_welcome-to-the-new-home-for-posit-open-source/osw-packages.gif"
      alt="" 
      loading="lazy"
    >
  </figure></div>
</p>
<p>We also made our video content more useful. With the help of Wes McKinney, we built a video transcription pipeline using <a href="https://openai.com/index/whisper/" target="_blank" rel="noopener">Whisper</a> and <a href="https://claude.ai/" target="_blank" rel="noopener">Claude</a>. So far we have transcribed 1,600 videos, which is roughly 800 hours of footage. You can now click any text in the transcript to jump right to that exact moment in the video.</p>
<p><div class="not-prose"><figure>
    <img class="h-auto max-w-full rounded-lg"
      src="https://opensource.posit.co/blog/2026-05-19_welcome-to-the-new-home-for-posit-open-source/osw-videos.gif"
      alt="" 
      loading="lazy"
    >
  </figure></div>
</p>
<p>Connecting this ecosystem also requires a clear voice. <a href="https://opensource.posit.co/people/isabella-vel%C3%A1squez/" target="_blank" rel="noopener">Isabella Velasquez</a> writes the copy to ensure our story resonates with the community.</p>
<p>Furthermore, the content on the site is heavily interconnected. Events link to talks, talks link to people, and people link to software. These connections, combined with a site-wide search, help you navigate the ecosystem easily. <a href="https://opensource.posit.co/people/greg-swinehart/" target="_blank" rel="noopener">Greg Swinehart</a> led the design to ensure the entire experience is accessible and on-brand.</p>
<h2 id="contribute">Contribute
</h2>
<p>The new website is open source because we want the community to contribute. If you have any feedback or suggestions, please open an issue on our <a href="https://github.com/posit-dev/open-source-website" target="_blank" rel="noopener">GitHub repository</a>.</p>
<p>Everyone who has contributed to Posit Open Source deserves a profile on the website. If you want to add or update your profile, we encourage you to submit a Pull Request.</p>
<p>Head over to <a href="http://opensource.posit.co" target="_blank" rel="noopener">opensource.posit.co</a> to explore the new site. I hope you like it.</p>
]]></description>
      <enclosure url="https://opensource.posit.co/blog/2026-05-19_welcome-to-the-new-home-for-posit-open-source/osw-blog-image.png" length="2477443" type="image/png" />
    </item>
    <item>
      <title>Ten Great Things We Added to Great Docs</title>
      <link>https://opensource.posit.co/blog/2026-05-13_great-docs-ten-things/</link>
      <pubDate>Wed, 13 May 2026 00:00:00 +0000</pubDate>
      <guid>https://opensource.posit.co/blog/2026-05-13_great-docs-ten-things/</guid>
      <dc:creator>Rich Iannone</dc:creator><description><![CDATA[<p>Great Docs started with a premise: you should be able to point a tool at
your Python package and get a documentation site that looks good without
any design work. Run <code>great-docs init</code>, run <code>great-docs build</code>, open the
result in a browser, and you are done. That three-command workflow has
not changed since <code>v0.1</code>, and the simplicity of the entry point is
important. But behind that simple surface, ten releases have added a
world of capability for when you are ready to go further.</p>
<p>With the release of <code>v0.10.0</code>, we have reached a nice, round number and
it&rsquo;s worth looking back and surveying all the things that were done! So
what follows is a survey of ten features (one per release) that
represent the range of what Great Docs has become. Taken together, they
tell the story of a documentation generator that starts simple and
scales with your ambitions.</p>
<h2 id="1-auto-discovery">1. Auto-Discovery
</h2>
<p>The original release (<code>v0.1</code>) came out with the feature that defines the
project&rsquo;s philosophy. When you run <code>great-docs init</code>, Great Docs
inspects your package and discovers its public API automatically. It
finds classes, functions, dataclasses, protocols, enumerations,
exceptions, type aliases, and more, using a combination of runtime
introspection and static analysis via
<a href="https://mkdocstrings.github.io/griffe/" target="_blank" rel="noopener">griffe</a>. It detects your
docstring style (NumPy, Google, or Sphinx) and writes a <code>great-docs.yml</code>
configuration file that captures the full structure of your API.</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">reference</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">sections</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">title</span><span class="p">:</span><span class="w"> </span><span class="l">Core</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">contents</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">MyApp, Config, build, preview]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">title</span><span class="p">:</span><span class="w"> </span><span class="l">Utilities</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">contents</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">parse, validate, format_output]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">title</span><span class="p">:</span><span class="w"> </span><span class="l">Exceptions</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">contents</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">ConfigError, BuildError]</span></span></span></code></pre></div></div>
<p>The practical consequence is that you don&rsquo;t have to enumerate your
exports by hand. If you add a new class or function to your package, the
next build picks it up. If you remove something, it disappears from the
site. The configuration file exists so you can reorder sections, add
display names, or exclude internal symbols, but the default is complete
and correct without any having to do any manual intervention.</p>
<p>This was the design decision that everything else built on: if
Great Docs can figure something out automatically, it should, and
configuration should be for expressing preferences rather than providing
information that we could have inferred.</p>
<h2 id="2-seo-and-proofreading">2. SEO and Proofreading
</h2>
<p>The <code>v0.2</code> release added two capabilities that address different aspects
of the same problem: making sure people can find your documentation and
making sure what they find reads well.</p>
<p>On the discoverability side, Great Docs now generates <code>sitemap.xml</code>,
<code>robots.txt</code>, canonical URLs, and JSON-LD structured data automatically.
Per-page Open Graph meta tags are injected so that when someone shares a
link to your documentation, the preview card shows a title, description,
and image rather than a generic URL. The <code>great-docs seo</code> audit command
checks your site for common problems (e.g., missing descriptions, broken
canonical links, orphaned pages, etc.).</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">great-docs proofread</span></span></code></pre></div></div>
<p>On the quality side, the <code>great-docs proofread</code> command runs local
grammar and spelling checks powered by
<a href="https://writewithharper.com/" target="_blank" rel="noopener">Harper</a>, a fast grammar checker that runs
entirely on your machine. It skips code blocks and YAML frontmatter,
ships with a technical dictionary so it does not flag standard
programming terms, and supports project-specific custom dictionaries for
your package&rsquo;s terminology. The output is available as plain text or
JSON (which is well-suited for CI pipelines).</p>
<p>The combination means that documentation sites built with Great Docs
tend to surface well in search results and tend to read cleanly once
readers arrive. Neither of these things sound all that exciting, but
both matter more than a lot of visual features.</p>
<h2 id="3-documentation-linting">3. Documentation Linting
</h2>
<p>Release <code>v0.3</code> introduced <code>great-docs lint</code>, a static analysis tool for
your documentation rather than your code. It inspects your package&rsquo;s
public API and checks for problems that would otherwise surface only
when a user encounters a confusing or broken page.</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">great-docs lint</span></span></code></pre></div></div>
<p>The linter catches missing docstrings (a function exists in your API but
has no documentation), broken cross-references (you reference
<code>MyClass.process</code> but the method is actually called <code>MyClass.run</code>),
style mismatches (you declared NumPy-style docstrings but a function
uses Google style), and unknown directives in your docstrings. It
produces machine-readable JSON output, making it straightforward to add
a lint step to your CI pipeline that fails the build when documentation
quality degrades.</p>
<p>This sort of feature is more useful than you&rsquo;d expect. You might run it,
discover three functions that never got docstrings and two
cross-references that broke during a refactor, fix them, and then feel
quite happy. Using it again and again will probably prevent the same
sort of problems from recurring. The goal is that no user should ever
land on an API reference page and find it empty or confusing because of
some oversight.</p>
<h2 id="4-internationalization">4. Internationalization
</h2>
<p>Great Docs <code>v0.4</code> made it possible to present your entire documentation
site in any of 23 languages with a single configuration option. The
actual content of your pages remains in whatever language you write it
in (usually English), but every piece of UI text (navbar labels, button
tooltips, relative timestamps, search placeholders, accessibility
attributes, pagination controls) is automatically translated.</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">site</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">language</span><span class="p">:</span><span class="w"> </span><span class="l">fr</span></span></span></code></pre></div></div>
<p>That single line transforms every &ldquo;Next page&rdquo; into &ldquo;Page suivante&rdquo;,
every &ldquo;Search&rdquo; into &ldquo;Rechercher&rdquo;, and every &ldquo;2 days ago&rdquo; into &ldquo;il y a 2
jours&rdquo;. Translations include proper plural forms for languages that need
them (like Polish, which has three plural forms depending on the number)
and localized date expressions.</p>
<p>The reasoning behind this feature is straightforward: if your package
has international users (and most packages do, whether they realize it
or not), the friction of navigating a site where every label is in a
language you do not speak fluently is real. While translating content is
a large undertaking, translating UI chrome is something a tool can do
for you.</p>
<p><div class="not-prose"><figure>
    <img class="h-auto max-w-full rounded-lg"
      src="https://opensource.posit.co/blog/2026-05-13_great-docs-ten-things/assets/reference-page-french.png"
      alt="A reference page for a class called TraiteurDeDonnees with all UI
elements rendered in French." 
      loading="lazy"
    >
  </figure></div>
</p>
<h2 id="5-keyboard-navigation">5. Keyboard Navigation
</h2>
<p>The <code>v0.5</code> release added a full keyboard shortcut layer that ships with
every documentation site. Press <code>/</code> or <code>s</code> to focus search. Press <code>[</code>
and <code>]</code> to navigate to the previous or next page. Press <code>d</code> to toggle
dark mode. Press <code>c</code> to copy the current page as Markdown. Press <code>h</code> or
<code>?</code> to see a help overlay listing all available shortcuts.</p>
<p><div class="not-prose"><figure>
    <img class="h-auto max-w-full rounded-lg"
      src="https://opensource.posit.co/blog/2026-05-13_great-docs-ten-things/assets/keyboard-shortcuts-overlay.png"
      alt="The keyboard shortcuts help overlay, showing all available navigation
shortcuts." 
      loading="lazy"
    >
  </figure></div>
</p>
<p>All shortcuts are disabled when a text input has focus (so typing in
search does not trigger navigation) and respect <code>prefers-reduced-motion</code>
for users who have requested reduced animation. The system is enabled by
default and can be disabled entirely via <code>keyboard_nav: false</code> in the
configuration for projects that don&rsquo;t want it.</p>
<p>Keyboard navigation is one of those features that, once you have used it
for a day, makes every site without it feel slightly slower. The ability
to browse through a long user guide using <code>[</code> and <code>]</code> without moving
your hands to the trackpad is a small daily improvement that compounds
over time. It also makes the sites more accessible to users who navigate
primarily or exclusively by keyboard.</p>
<h2 id="6-page-tags-and-status-badges">6. Page Tags and Status Badges
</h2>
<p>Release <code>v0.6</code> introduced page-level metadata that surfaces in both the
page body and the sidebar, giving documentation sites a lightweight
content management layer.</p>
<p>Page tags let you categorize pages by topic using YAML frontmatter:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">tags</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">Configuration, Theming, Advanced]</span></span></span></code></pre></div></div>
<p>Great Docs renders these as pill-shaped links above the page title and
auto-generates a tags index page listing all tags across the site with
links to their associated pages. If you maintain a large user guide with
dozens of pages, tags give readers an alternative navigation path:
instead of scrolling through the sidebar, they can click a tag to find
all related content. Every User Guide page on the <a href="https://posit-dev.github.io/great-docs/" target="_blank" rel="noopener">Great Docs
site</a> uses page tags, and the
<a href="https://posit-dev.github.io/great-docs/user-guide/page-tags.html" target="_blank" rel="noopener">Page
Tags</a>
guide explains how to set them up.</p>
<p><div class="not-prose"><figure>
    <img class="h-auto max-w-full rounded-lg"
      src="https://opensource.posit.co/blog/2026-05-13_great-docs-ten-things/assets/page-tag-index.png"
      alt="The auto-generated Tags index page on the Great Docs site, showing the
AI/LLM and API tag categories with their associated
pages." 
      loading="lazy"
    >
  </figure></div>
</p>
<p>Page status badges mark pages with lifecycle states:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">status</span><span class="p">:</span><span class="w"> </span><span class="l">beta</span></span></span></code></pre></div></div>
<p>The supported statuses are <code>new</code>, <code>beta</code>, <code>deprecated</code>, and
<code>experimental</code>, each rendered as a color-coded badge below the page
title. In the sidebar, these appear as compact icons so that readers can
see at a glance which parts of the API are stable and which are still
evolving. Status badges are automatically translated for non-English
sites (the same i18n system from v0.4 applies) and include built-in
Lucide icons and color schemes.</p>
<p><div class="not-prose"><figure>
    <img class="h-auto max-w-full rounded-lg"
      src="https://opensource.posit.co/blog/2026-05-13_great-docs-ten-things/assets/page-status-badges.png"
      alt="The five preconfigured page status badges: New, Update, Beta,
Deprecated, and Experimental." 
      loading="lazy"
    >
  </figure></div>
</p>
<p>Together, tags and status badges give documentation authors a way to
communicate structure and maturity without writing prose about it. A
reader landing on a page immediately knows whether it describes a stable
feature or an experiment, and can navigate to related topics through
shared tags. The <a href="https://posit-dev.github.io/great-docs/user-guide/page-status-badges.html" target="_blank" rel="noopener">Page Status
Badges</a>
guide covers the full set of options.</p>
<h2 id="7-scale-to-fit">7. Scale-to-Fit
</h2>
<p>Great Docs <code>v0.7</code> introduced a solution to a problem that plagues
documentation for data-oriented packages: wide tables and HTML widgets
that overflow the content column. When a table produced by <a href="https://posit-dev.github.io/great-tables/" target="_blank" rel="noopener">Great
Tables</a> or a Pandas DataFrame
is slightly wider than the page, the reader has to scroll horizontally
to see a sliver of remaining content, which is a frustrating experience.
But when content is dramatically wider than the container, shrinking it
would make text unreadably small. So, in essence, different situations
demand different responses.</p>
<p>Scale-to-Fit handles both cases. It automatically shrinks targeted
elements so they fit within the container width, and falls back to
horizontal scrolling when the element would need to shrink beyond a
configurable minimum threshold. The default threshold is 60% of natural
size, meaning content will shrink up to 40% to avoid scrollbars, but
anything more extreme gets a scrollbar instead.</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">scale_to_fit</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">scale_to_fit_min_scale</span><span class="p">:</span><span class="w"> </span><span class="m">0.6</span></span></span></code></pre></div></div>
<p>The feature can be enabled globally (all rendered HTML output gets the
treatment), per-page via frontmatter, or manually on individual elements
using a <code>:::{.scale-to-fit}</code> div wrapper. The global setting is what
most data-oriented packages want: turn it on once and every GT table,
every DataFrame display, and every custom widget in your documentation
adapts gracefully to the reader&rsquo;s viewport without any per-page
configuration. The
<a href="https://posit-dev.github.io/great-docs/user-guide/scale-to-fit.html" target="_blank" rel="noopener">Scale-to-Fit</a>
guide demonstrates the behavior at different widths.</p>
<h2 id="8-versioned-documentation">8. Versioned Documentation
</h2>
<p>Release <code>v0.8</code> addressed a fundamental challenge of maintaining
documentation for evolving libraries: users on older releases need docs
that match the version they have installed. Great Docs makes
multi-version documentation a build-time concern rather than a
deployment concern. You declare your versions in configuration, and the
build system produces a coherent, version-aware site.</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">versions</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="s2">&#34;0.3&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="s2">&#34;0.2&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="s2">&#34;0.1&#34;</span></span></span></code></pre></div></div>
<p>That configuration produces three independent copies of your site: the
latest version at the root URL, and previous versions under <code>/v/0.2/</code>
and <code>/v/0.1/</code>. A version selector dropdown appears in the navbar,
letting readers switch between versions. Each version can reference an
<code>api_snapshot</code> to regenerate API pages from that release (rather than
the current code), and pages can use <code>versions</code> frontmatter to declare
which releases they apply to. Pages that did not exist in older releases
are automatically excluded from older builds.</p>
<p>The system also supports prerelease versions (marked in the dropdown but
not served as &ldquo;latest&rdquo;), end-of-life versions (visually distinguished),
and floating URL aliases like <code>/v/stable/</code> that always resolve to the
current release. Maintainers declare versions once and the build handles
the rest. The <a href="https://posit-dev.github.io/great-docs/" target="_blank" rel="noopener">Great Docs documentation
site</a> itself is a good example
of this feature in practice: its version selector currently contains ten
versions, one for each release from <code>v0.1</code> through <code>v0.10.0</code>. The
<a href="https://posit-dev.github.io/great-docs/user-guide/multi-version-docs.html" target="_blank" rel="noopener">Versioned
Docs</a>
guide covers the full configuration in detail.</p>
<p><div class="not-prose"><figure>
    <img class="h-auto max-w-full rounded-lg"
      src="https://opensource.posit.co/blog/2026-05-13_great-docs-ten-things/assets/version-selector-widget.png"
      alt="The version selector dropdown on the Great Docs site, showing all ten
versions (plus the prerelease) available for
navigation." 
      loading="lazy"
    >
  </figure></div>
</p>
<h2 id="9-color-swatches">9. Color Swatches
</h2>
<p>Great Docs <code>v0.9</code> added a shortcode for documenting color palettes
interactively. If your package has a theming system, a set of brand
colors, or a palette of status indicators, you have probably faced the
problem of showing colors in documentation. Screenshots go stale,
hand-coded HTML swatches are tedious, and plain text hex codes are
meaningless without a visual reference. The <code>color-swatch</code> shortcode
turns a YAML file into an interactive palette.</p>
<p><div class="not-prose"><figure>
    <img class="h-auto max-w-full rounded-lg"
      src="https://opensource.posit.co/blog/2026-05-13_great-docs-ten-things/assets/color-swatches.png"
      alt="The Color Swatches shortcode in action: a YAML color definition file
rendered as interactive circle swatches with names and hex
labels." 
      loading="lazy"
    >
  </figure></div>
</p>
<p>Each swatch displays its hex code, shows RGB and HSL breakdowns on
hover, evaluates WCAG and APCA contrast ratios against white and black
backgrounds, and lets readers copy the color value with a single click.
Two display modes are available: circles (compact, scannable) and
rectangles (detailed, with contrast information front and center). Great
Docs also ships with built-in presets for its own gradient themes, so
you can reference <code>preset=&quot;sky&quot;</code> or <code>preset=&quot;lilac&quot;</code> without creating a
YAML file at all.</p>
<p>For packages that deal with color (charting libraries, design systems,
theming frameworks), this feature eliminates a category of maintenance
burden. The YAML file is the source of truth; the rendered output is
always current, always interactive, and always accessible. The <a href="https://posit-dev.github.io/great-docs/user-guide/color-swatches.html" target="_blank" rel="noopener">Color
Swatches</a>
page in the User Guide covers the full range of options.</p>
<h2 id="10-table-previews-and-table-explorer">10. Table Previews and Table Explorer
</h2>
<p>The most recent release, <code>v0.10.0</code>, added two complementary tools for
embedding data directly in documentation pages. If your package works
with tabular data (and many Python packages do), showing what a dataset
looks like is one of the most common documentation tasks, and also one
of the hardest to do well with static publishing.</p>
<p><code>tbl_preview()</code> generates a self-contained, JavaScript-free HTML table
from almost any data source:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">great_docs</span> <span class="kn">import</span> <span class="n">tbl_preview</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">tbl_preview</span><span class="p">(</span><span class="s2">&#34;data/example.parquet&#34;</span><span class="p">)</span></span></span></code></pre></div></div>
<p>It accepts Polars DataFrames, Pandas DataFrames, PyArrow Tables, CSV
files, Parquet files, and plain Python dictionaries. Each preview
includes a colored type badge identifying the source format, compact
dtype labels beneath every column header, row-number gutters, automatic
head/tail splitting for large tables (showing the first and last rows
with an ellipsis in between), and missing-value highlighting. The output
works identically in light and dark modes and requires no JavaScript at
all.</p>
<p><div class="not-prose"><figure>
    <img class="h-auto max-w-full rounded-lg"
      src="https://opensource.posit.co/blog/2026-05-13_great-docs-ten-things/assets/tbl-preview-light-dark.png"
      alt="A table preview rendered in light mode (top) and dark mode (bottom),
showing identical structure and readability in both
themes." 
      loading="lazy"
    >
  </figure></div>
</p>
<p><code>tbl_explorer()</code> is the interactive counterpart. It embeds all data as
inline JSON and progressively enhances a static fallback table with
sorting, token-based filtering, pagination, column toggling,
copy-to-clipboard, and CSV download. A Quarto shortcode variant lets you
embed an explorer directly in a <code>.qmd</code> page by pointing at a data file,
without writing any Python.</p>
<p><div class="not-prose"><figure>
    <img class="h-auto max-w-full rounded-lg"
      src="https://opensource.posit.co/blog/2026-05-13_great-docs-ten-things/assets/tbl-explorer-view.png"
      alt="A Table Explorer widget with three active filters and two column sorts
applied, showing how readers can interactively narrow and reorder
data." 
      loading="lazy"
    >
  </figure></div>
</p>
<p>The distinction between the two is deliberate. <code>tbl_preview()</code> is for
showing what data looks like: schema, types, a representative sample.
<code>tbl_explorer()</code> is for letting readers interact with data: sort columns
to find extremes, filter rows by values, hide irrelevant columns, and
export subsets. Together they cover the full spectrum of data
documentation needs, from a quick &ldquo;here is the structure of this
DataFrame&rdquo; to a fully explorable reference dataset. The User Guide has
dedicated pages for <a href="https://posit-dev.github.io/great-docs/user-guide/table-previews.html" target="_blank" rel="noopener">Table
Previews</a>
and <a href="https://posit-dev.github.io/great-docs/user-guide/table-explorer.html" target="_blank" rel="noopener">Table
Explorer</a>;
the latter is worth visiting just to try the interactive tables yourself
and see how sorting, filtering, and column toggling feel in practice.</p>
<h2 id="what-ten-releases-add-up-to">What Ten Releases Add Up To
</h2>
<p>Looking back across ten releases, what is striking is not any single
feature but the range of concerns the tool now addresses. Documentation
generation is the starting point, but the problems that come after
generation (discoverability, quality, accessibility,
internationalization, versioning, interactivity) are where most of the
ongoing work has gone. A documentation site is not just a rendering of
your docstrings; it is a product that needs search engine optimization,
proofreading, linting, keyboard accessibility, responsive design, and
support for readers across languages and connection speeds.</p>
<p>The guiding principle remains the same as it was in <code>v0.1</code>: you should
be able to get a good site with minimal effort! But &ldquo;minimal effort&rdquo;
scales so, for a new package, that means three commands and zero
configuration. For a mature package with international users, multiple
supported versions, and extensive user guides, it means a
<code>great-docs.yml</code> file that declares your preferences while the tool
handles the mechanics of producing a site that meets the standard you
have set.</p>
<p>Great Docs is <a href="https://pypi.org/project/great-docs/" target="_blank" rel="noopener">available on PyPI</a>
so if you maintain a Python package and want to try it:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">pip install great-docs
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> your-python-project
</span></span><span class="line"><span class="cl">great-docs init
</span></span><span class="line"><span class="cl">great-docs build
</span></span><span class="line"><span class="cl">great-docs preview</span></span></code></pre></div></div>
<p>The <a href="https://posit-dev.github.io/great-docs/" target="_blank" rel="noopener">documentation site</a> covers
everything discussed here and much more. If there is a feature you wish
your documentation site had (something that would save you time, help
your readers, or make maintenance less painful), we would genuinely like
to hear about it. The best ideas for the next ten releases will come
from people who use the tool daily and notice what is missing. Open an
<a href="https://github.com/posit-dev/great-docs/issues" target="_blank" rel="noopener">issue on GitHub</a> with
your idea, however rough, and we will take it seriously!</p>
]]></description>
      <enclosure url="https://opensource.posit.co/blog/2026-05-13_great-docs-ten-things/assets/great-docs-ten-things.png" length="1750648" type="image/png" />
    </item>
    <item>
      <title>A memory model for production async R: mirai 2.7.0 and mori 0.2.0</title>
      <link>https://opensource.posit.co/blog/2026-05-12_production-async-r/</link>
      <pubDate>Tue, 12 May 2026 00:00:00 +0000</pubDate>
      <guid>https://opensource.posit.co/blog/2026-05-12_production-async-r/</guid>
      <dc:creator>Charlie Gao</dc:creator><description><![CDATA[<p>Async R has always come with tradeoffs. Parallel batch compute meant duplicated data and an unbounded queue that could push your session out of memory, while Shiny gave you an event loop but background-worker submissions had no way to tell you when to slow down.</p>
<p>Shared memory across processes is standard in other programming languages. In R it&rsquo;s been either platform-specific (<code>fork</code> on Unix-alikes, unsafe from GUIs like Positron or with multi-threaded code) or file-format-specific (memory-mapped Parquet).</p>
<p>Two CRAN releases address those constraints directly:</p>
<ul>
<li><strong><a href="https://mirai.r-lib.org/" target="_blank" rel="noopener">mirai 2.7.0</a></strong> — <code>daemons(memory = ...)</code> puts a hard byte-level cap on the dispatcher queue. <code>try_mirai()</code> returns <code>NULL</code> immediately when that cap is hit, instead of blocking your session. The dispatcher itself runs as an in-process thread now; and through <a href="https://nanonext.r-lib.org/" target="_blank" rel="noopener">nanonext 1.9.0</a> underneath, peak memory during serialized sends has halved.</li>
<li><strong><a href="https://shikokuchuo.net/mori/" target="_blank" rel="noopener">mori 0.2.0</a></strong> — graduates from experimental status, with a stable wire format underneath. Sub-lists and extracted elements now expose stable path-form names (<code>/mori_abc[2,3]</code>) that any process can attach to via <code>map_shared()</code> directly, without going through the parent.</li>
</ul>
<h2 id="building-out-the-foundations">Building out the foundations
</h2>
<p>mirai already shipped a lot of what production async needs: microsecond round-trip latency, FIFO scheduling, task cancellation, OpenTelemetry tracing, reproducible parallel RNG, custom serialization for torch / Arrow / polars, an event-driven promises method for Shiny ExtendedTask, and deployment across SSH / Slurm / Posit Workbench.</p>
<p>What these releases add is more foundational. R hasn&rsquo;t really had a built-in memory model for asynchronous work — no way to size a task queue in bytes, and no portable way to share data across processes without paying for a full copy. mirai 2.7.0 introduces the first: a bounded queue built into the dispatcher, with <code>try_mirai()</code> exposing non-blocking submission under this constraint. mori 0.2.0 brings the second to a stable footing: a self-describing shared-memory format, addressable down to individual list elements by name, that any process on the machine can attach to.</p>
<p>Together they extend what R can credibly be used for — streaming pipelines, multi-stage data flows, and event-loop applications that previously had to reach outside the language.</p>
<h2 id="mirai-270--bounded-queues-and-try_mirai">mirai 2.7.0 — bounded queues and <code>try_mirai()</code>
</h2>
<h3 id="daemonsmemory--"><code>daemons(memory = ...)</code>
</h3>
<p>The dispatcher is the single point in your session where every <code>mirai()</code> call lands before being routed to a daemon. Each call carries its serialized arguments — the queued payload — and that payload sits in memory until a daemon is ready for it. By default the queue is unbounded, which is fine until it isn&rsquo;t.</p>
<p><code>daemons()</code> now takes a <code>memory</code> argument that caps the approximate total payload of queued tasks at the dispatcher, in MB:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="nf">library</span><span class="p">(</span><span class="n">mirai</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nf">daemons</span><span class="p">(</span><span class="m">4</span><span class="p">,</span> <span class="n">memory</span> <span class="o">=</span> <span class="m">100</span><span class="p">)</span>  <span class="c1"># 100 MB queue capacity</span></span></span></code></pre></div></div>
<p>When the queued bytes are below the cap, <code>mirai()</code> calls submit normally. When the cap is reached, <code>mirai()</code> blocks the calling R thread until daemons drain the queue enough for the new payload to fit. Existing code keeps working — the only visible difference is that your session can no longer be pushed out of memory by a runaway producer.</p>
<p><code>status()</code> surfaces current and peak usage:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="nf">status</span><span class="p">()</span><span class="o">$</span><span class="n">memory</span>
</span></span><span class="line"><span class="cl"><span class="c1">#     used     peak capacity </span>
</span></span><span class="line"><span class="cl"><span class="c1">#     12.4     87.3    100.0 </span></span></span></code></pre></div></div>
<p><code>peak</code> is the high-watermark queued payload over the lifetime of the dispatcher, which gives you an empirical way to size the cap. The recommended workflow is to run a representative workload with <code>memory = NULL</code> first (where <code>capacity</code> reports <code>NA</code>), observe <code>peak</code>, then set <code>memory</code> at or above that watermark.</p>
<p>If profiling isn&rsquo;t practical, the local machine has to fit your session, the dispatcher queue, and <code>n</code> daemon processes (each a full R session holding an in-flight payload while executing). Half of currently available RAM (<code>ps::ps_system_memory()[[&quot;avail&quot;]] / 2e6</code>, in MB) is a reasonable starting <code>memory</code> cap, revised down for large <code>n</code> or payloads. Remote daemons don&rsquo;t draw on the local budget, so when most workers are remote, the cap can be more generous.</p>
<p>For <code>n</code> itself, one less than your number of CPU cores is the right starting point for CPU-bound work (one core left for your session), while I/O-bound work (API calls, database queries, file reads) can comfortably exceed core count since idle daemons cost little.</p>
<p>The capacity is measured in <em>bytes</em>, not <em>task count</em>. A queue capped at 100 tasks can hold either 100 small tasks or 100 huge ones, and only the second runs out of memory. A queue capped at 100 MB never runs your session out of memory regardless of the mix.</p>
<h3 id="try_mirai"><code>try_mirai()</code>
</h3>
<p>A blocking submit is fine in a batch script, but not an event loop: a Shiny session that calls <code>mirai()</code> from inside an ExtendedTask can&rsquo;t afford to block while the queue drains, because that same session is also driving the UI for all users.</p>
<p><code>try_mirai()</code> returns immediately when the queue is full, instead of blocking. The semantics are simple:</p>
<ul>
<li>If the queue is below capacity, behaves identically to <code>mirai()</code> and returns a mirai.</li>
<li>If the queue is at capacity, returns <code>NULL</code> immediately without blocking, and leaves the caller to decide what to do next.</li>
</ul>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="n">m</span> <span class="o">&lt;-</span> <span class="nf">try_mirai</span><span class="p">({</span> <span class="nf">slow_thing</span><span class="p">()</span> <span class="p">})</span> <span class="o">%||%</span> <span class="nf">stop</span><span class="p">(</span><span class="s">&#34;queue full&#34;</span><span class="p">)</span></span></span></code></pre></div></div>
<p>The three response strategies are: drop the task (best when the work is idempotent and frequent), retry later (queue the request behind a <code>later::later()</code> call), or propagate backpressure upstream — as in the example above, by raising a condition. Which is right is application-specific — <code>try_mirai()</code> hands you the flexibility.</p>
<p>If <code>memory</code> isn&rsquo;t set, or the daemon configuration has no dispatcher, <code>try_mirai()</code> always returns a mirai and is identical to <code>mirai()</code> — so the function is safe to use unconditionally; it only does anything different in the bounded-queue case.</p>
<p>The two halves — <code>daemons(memory = ...)</code> and <code>try_mirai()</code> — are the right combination for any event-loop integration. Set the cap to reflect what your session can actually afford to hold, submit through <code>try_mirai()</code>, and the application gracefully adapts to load instead of locking up or crashing.</p>
<h3 id="under-the-hood">Under the hood
</h3>
<p>A few transport-layer changes ship alongside 2.7.0 — invisible most of the time, important when payloads are large or throughput is high:</p>
<ul>
<li><strong>Dispatcher as a thread.</strong> The dispatcher is no longer a separate R process; it runs as an in-process thread, courtesy of a C-level reimplementation in nanonext. mirai round-trip latency is now in the tens of microseconds.</li>
<li><strong>Halved peak memory on sends.</strong> <a href="https://nanonext.r-lib.org/" target="_blank" rel="noopener">nanonext 1.9.0</a>, the transport underneath mirai, now writes serialized sends directly instead of going through an intermediate buffer. A 500 MB argument going to a daemon used to need ~1 GB of momentary headroom in your session; now it needs ~500 MB. The dispatcher&rsquo;s actual resident memory tracks the <code>daemons(memory = ...)</code> cap more accurately as a result.</li>
<li><strong>&gt;2 GB payloads on macOS and Windows.</strong> Transfers above 2 GB previously failed silently near the <code>INT_MAX</code>-byte boundary on those platforms. They now go through cleanly. Linux was already fine.</li>
</ul>
<h2 id="mori-020--out-of-experimental-status">mori 0.2.0 — out of experimental status
</h2>
<p>mori is a younger package — its <a href="https://opensource.posit.co/blog/2026-04-23_mori-0-1-0/">first CRAN release</a> was just last month — and its job is the other half of memory pressure: the data itself, rather than the queue. R processes don&rsquo;t share memory, and data crosses between them through message-passing. When eight workers each need the same 200 MB data frame, that&rsquo;s 1.6 GB of serialize / transfer / deserialize, plus eight separate copies of the data resident in RAM.</p>
<p><code>mori::share()</code> writes the object once into OS-level shared memory and returns an ALTREP (alternative representation) wrapper. Multiple processes on the same machine map the same physical pages — zero-copy, lazy, and managed by R&rsquo;s garbage collector. Pass the wrapper to a daemon and only the shared memory identifier (hundreds of bytes) crosses the wire; the daemon attaches and reads the pages directly.</p>
<p>We implemented this in mori 0.1.0, but 0.2.0 takes mori out of experimental status, landing a stable wire format. A key consequence of this is that every sub-list and extracted element of a shared region now has a stable identifier of its own. <code>shared_name()</code> emits path-form names (<code>/mori_abc_1[2]</code> for the second element of region <code>/mori_abc_1</code>, <code>/mori_abc_1[2,3]</code> for nested addressing), and <code>map_shared()</code> accepts the path form directly.</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="nf">library</span><span class="p">(</span><span class="n">mori</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nf">library</span><span class="p">(</span><span class="n">mirai</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">lst</span> <span class="o">&lt;-</span> <span class="nf">share</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">  <span class="nf">list</span><span class="p">(</span><span class="n">weights</span> <span class="o">=</span> <span class="nf">rnorm</span><span class="p">(</span><span class="m">1e6</span><span class="p">),</span> <span class="n">targets</span> <span class="o">=</span> <span class="nf">rnorm</span><span class="p">(</span><span class="m">1e6</span><span class="p">),</span> <span class="n">inputs</span> <span class="o">=</span> <span class="nf">rnorm</span><span class="p">(</span><span class="m">1e6</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Sub-elements have their own path-form names</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">shared_name</span><span class="p">(</span><span class="n">lst</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt; [1] &#34;/mori_abc_1&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nf">shared_name</span><span class="p">(</span><span class="n">lst[[1]]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt; [1] &#34;/mori_abc_1[1]&#34;</span></span></span></code></pre></div></div>
<p>Pass the shared object directly to a daemon — the ALTREP wrapper serializes as just the name, and the daemon attaches on deserialize:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="nf">mirai</span><span class="p">(</span><span class="nf">length</span><span class="p">(</span><span class="n">weights</span><span class="p">),</span> <span class="n">weights</span> <span class="o">=</span> <span class="n">lst[[1]]</span><span class="p">)</span><span class="n">[]</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt; [1] 1000000</span></span></span></code></pre></div></div>
<p>A consumer in a separate session that only has the name as a string — from a config file, message queue, or HTTP response — attaches with <code>map_shared()</code>:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="c1"># In a separate R session</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">weights</span> <span class="o">&lt;-</span> <span class="n">mori</span><span class="o">::</span><span class="nf">map_shared</span><span class="p">(</span><span class="s">&#34;/mori_abc_1[1]&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nf">length</span><span class="p">(</span><span class="n">weights</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt; [1] 1000000</span></span></span></code></pre></div></div>
<p>A path-form name is a small thing on the wire – just a string – but it enables a lot:</p>
<ul>
<li><strong>Attach by name.</strong> Path-form identifiers are plain strings, so they fit anywhere a string can go: a queue payload, a config entry, an HTTP response, a database row. A consumer reads the name, calls <code>map_shared()</code>, and gets exactly that slice — the parent region never has to enter their session.</li>
<li><strong>Decoupled pipelines.</strong> Stage A shares a 100-column frame and tells stage B the names of the three columns to process. Stage B passes the name of its output to stage C, and so on. No stage holds more in memory than it actually uses, and no stage needs to know how its inputs were produced — only the names.</li>
<li><strong>Code symmetry.</strong> <code>map_shared(shared_name(x))</code> returns an equivalent of <code>x</code> whether <code>x</code> is a root region or a single leaf element, so consumer code doesn&rsquo;t need a special branch for sub-objects.</li>
<li><strong>Lifetime through one chain.</strong> Holding a reference to a leaf keeps the whole parent region alive automatically — the garbage collector walks the chain of dependencies — so a consumer never has to manually pin the root to stay safe.</li>
</ul>
<h3 id="composing-with-daemonsmemory--">Composing with <code>daemons(memory = ...)</code>
</h3>
<img src="https://opensource.posit.co/blog/2026-05-12_production-async-r/composition-diagram.svg" data-fig-alt="Diagram: the single call try_mirai({ f(y) }, y = share(x)) forks into two effects — submission to a bounded dispatcher queue (memory = 100 MB) and writing the data once into a shared-memory region (/mori_abc_1). Both feed a single daemon: the queue carries only the name, and the daemon attaches to shared memory by name via mmap." />
<p>The two memory tools compose. <code>mori::share()</code> shrinks each task&rsquo;s <em>queued payload</em> from megabytes to hundreds of bytes, which means the dispatcher&rsquo;s <code>memory</code> cap rarely fills up at all when workers are reading shared data. The pieces are complementary:</p>
<ul>
<li><code>mori::share()</code> removes the per-worker copy of large data.</li>
<li><code>daemons(memory = ...)</code> caps the dispatcher backlog when something <em>is</em> being sent.</li>
<li><code>try_mirai()</code> makes hitting that cap a non-event in an event loop.</li>
</ul>
<h2 id="putting-r-on-the-concurrency-map">Putting R on the concurrency map
</h2>
<p>The design of <code>daemons(memory = ...)</code>, <code>try_mirai()</code>, and <code>mori::share()</code> draws on how async runtimes in other languages handle the same problems. Here&rsquo;s the comparison:</p>
<p><strong>Byte-budgeted backpressure.</strong> Async frameworks elsewhere — Python&rsquo;s <code>asyncio.Queue</code>, Ray&rsquo;s <code>max_pending_tasks</code>, Tokio&rsquo;s bounded <code>mpsc</code> channels — measure queue capacity by task count. <code>daemons(memory = ...)</code> caps by <em>bytes</em> instead, which tracks what actually drives memory pressure.</p>
<p><strong>Non-blocking submission on a full queue.</strong> Tokio&rsquo;s <code>try_send</code> is the closest direct analogue: it returns immediately when the channel is full instead of blocking. <code>try_mirai()</code> provides the same semantics for R submission, with the additional advantage that the cap itself is byte-aware.</p>
<p><strong>Integrated cross-process zero-copy.</strong> Ray&rsquo;s Plasma object store offers cross-process zero-copy, but it&rsquo;s a heavyweight component — you run a Ray cluster to use it, not a function. <code>mori::share()</code> is just a function call, producing an ALTREP wrapper that consumer processes attach to via lazy reads, down to individual columns by name.</p>
<p>Combined with mirai&rsquo;s microsecond round-trip latency and event-driven C-level transport, these three pieces give R a concurrency story that stands on its own: byte-aware backpressure, non-blocking submission, and integrated zero-copy sharing.</p>
<h2 id="try-it">Try it
</h2>
<p>Install from CRAN:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-r" data-lang="r"><span class="line"><span class="cl"><span class="nf">install.packages</span><span class="p">(</span><span class="nf">c</span><span class="p">(</span><span class="s">&#34;mirai&#34;</span><span class="p">,</span> <span class="s">&#34;mori&#34;</span><span class="p">))</span></span></span></code></pre></div></div>
<p>Pointers from here:</p>
<ul>
<li><a href="https://mirai.r-lib.org/articles/v01-reference.html" target="_blank" rel="noopener">mirai reference vignette</a> — the memory-management section walks through <code>daemons(memory = ...)</code>, <code>status()$memory</code>, <code>try_mirai()</code>, and shared-memory composition.</li>
<li><a href="https://mirai.r-lib.org/articles/v02-promises.html" target="_blank" rel="noopener">mirai promises vignette</a> — Shiny ExtendedTask and event-driven async.</li>
<li><a href="https://shikokuchuo.net/mori/" target="_blank" rel="noopener">mori package site</a> — <code>share()</code>, <code>shared_name()</code> and <code>map_shared()</code>.</li>
<li><a href="https://nanonext.r-lib.org/" target="_blank" rel="noopener">nanonext package site</a> — the transport layer and concurrency primitives.</li>
<li>The <code>r-lib</code> agent skill in the <a href="https://github.com/posit-dev/skills" target="_blank" rel="noopener"><code>posit-dev-skills</code></a> plugin includes an LLM-optimised mirai guide for AI coding assistants.</li>
</ul>
<p>Issues, feedback, and questions are very welcome on GitHub: <a href="https://github.com/r-lib/mirai/issues" target="_blank" rel="noopener">r-lib/mirai</a>, <a href="https://github.com/r-lib/nanonext/issues" target="_blank" rel="noopener">r-lib/nanonext</a>, <a href="https://github.com/shikokuchuo/mori/issues" target="_blank" rel="noopener">shikokuchuo/mori</a>.</p>
<h2 id="acknowledgments">Acknowledgments
</h2>
<p>A big thanks to everyone who contributed to mirai 2.7.0, mori 0.2.0, and nanonext 1.9.0 through their issues, pull requests, and discussions: <a href="https://github.com/HenrikBengtsson" target="_blank" rel="noopener">@HenrikBengtsson</a>, <a href="https://github.com/kentqin-cve" target="_blank" rel="noopener">@kentqin-cve</a>, <a href="https://github.com/king-of-poppk" target="_blank" rel="noopener">@king-of-poppk</a>, <a href="https://github.com/manforkr" target="_blank" rel="noopener">@manforkr</a>, <a href="https://github.com/mcol" target="_blank" rel="noopener">@mcol</a>, <a href="https://github.com/michaelmayer2" target="_blank" rel="noopener">@michaelmayer2</a>, <a href="https://github.com/pmac0451" target="_blank" rel="noopener">@pmac0451</a>, <a href="https://github.com/t-kalinowski" target="_blank" rel="noopener">@t-kalinowski</a>, and <a href="https://github.com/wlandau" target="_blank" rel="noopener">@wlandau</a>.</p>
]]></description>
      <enclosure url="https://opensource.posit.co/blog/2026-05-12_production-async-r/featured.jpg" length="261721" type="image/jpeg" />
    </item>
  </channel>
</rss>
