<?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>Tue, 26 May 2026 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://opensource.posit.co/blog/index.xml" rel="self" type="application/rss+xml" />
    <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>
    <item>
      <title>Positron May Release Highlights</title>
      <link>https://opensource.posit.co/blog/2026-05-11_positron-2026-05-release/</link>
      <pubDate>Mon, 11 May 2026 00:00:00 +0000</pubDate>
      <guid>https://opensource.posit.co/blog/2026-05-11_positron-2026-05-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="product-updates">Product updates
</h2>
<h3 id="announcing-the-packages-pane">Announcing the Packages pane
</h3>
<p>Our 






<a href="https://positron.posit.co/packages-pane" target="_blank" rel="noopener">Packages pane</a>
 is available in this release as a preview feature. The new Packages pane brings streamlined package management directly into the IDE so you can see at a glance what&rsquo;s installed, what&rsquo;s attached, and what&rsquo;s out of date.</p>
<img src="https://opensource.posit.co/blog/2026-05-11_positron-2026-05-release/packages-pane-attached.gif" data-fig-alt="The Positron Packages pane in the Primary Sidebar, showing installed packages with attach status and a search field; the user attaches a package and the row updates to show it as attached." />
<p>Find it in the Primary Sidebar by clicking the package icon. Positron automatically detects and integrates with your current interpreter; today we have support for pip, venv, uv, and conda for Python, plus base R, pak, and renv for R. You can search, sort, install, update, remove, filter by category (including &ldquo;Outdated Packages&rdquo;), and even see which packages are attached in your current session. To hide the pane, disable 






<a href="positron://settings/positron.packages.enable"><code>positron.packages.enable</code></a>
. 






<a href="https://github.com/posit-dev/positron/discussions" target="_blank" rel="noopener">Let us know</a>
 how it fits into your workflow.</p>
<h3 id="inline-output-for-quarto">Inline output for Quarto
</h3>
<p>Inline output for <code>.qmd</code> documents was one of Positron&rsquo;s most-requested features ever, with many users telling us it was the one thing keeping them tied to RStudio or Jupyter notebooks. We are happy to share that Quarto inline output is available this release as a preview feature.</p>
<img src="https://opensource.posit.co/blog/2026-05-11_positron-2026-05-release/quarto-inline-output.png" data-fig-alt="A Quarto document open in Positron showing inline output beneath an executed code cell, with an execution status line indicating runtime." />
<p>Quarto kernels start automatically when you open a document with inline output, so you don&rsquo;t wait for a kernel on first execution. Each inline output cell shows a status line with execution status and elapsed time, and outputs are now collapsible when they get long. We&rsquo;ve also added a tool to switch the interpreter used for inline output, support for <em>Show Notebook Console</em> for Quarto documents, and fixed a handful of papercuts around execution indicators, autocompletion, and the kernel selector. Enable inline output via the 






<a href="positron://settings/positron.quarto.inlineOutput.enabled"><code>positron.quarto.inlineOutput.enabled</code></a>
 setting.</p>
<h3 id="extensions-available-from-posit-public-package-manager">Extensions available from Posit Public Package Manager
</h3>
<p>Positron&rsquo;s 






<a href="https://positron.posit.co/extensions" target="_blank" rel="noopener">extension gallery</a>
 now uses 






<a href="https://p3m.dev" target="_blank" rel="noopener">Posit Public Package Manager</a>
 (P3M) by default, replacing Open VSX as the source for browsing and installing extensions. The catalog is the same set of extensions you already use, served from Posit infrastructure for reliable distribution. If you prefer to stay on Open VSX, the new 






<a href="positron://settings/positron.extensions.gallerySource"><code>positron.extensions.gallerySource</code></a>
 setting lets you switch back at any time. For organizations on Posit Workbench, 






<a href="https://posit.co/blog/manage-vs-code-extensions-like-packages-with-posit-package-manager-2026-04-0/" target="_blank" rel="noopener">Posit Package Manager also added support for mirroring and serving VS Code extensions</a>
 from your own instance, bringing the same governance, security, and air-gapped distribution to extensions that you already have for R and Python packages.</p>
<h3 id="positron-notebook-editor-is-now-in-beta">Positron Notebook Editor is now in beta
</h3>
<p>The 






<a href="https://positron.posit.co/positron-notebook-editor" target="_blank" rel="noopener">Positron Notebook Editor</a>
 for <code>.ipynb</code> files is officially moving from alpha to beta.</p>
<img src="https://opensource.posit.co/blog/2026-05-11_positron-2026-05-release/notebook-rstats.gif" data-fig-alt="Working with an R Jupyter notebook in the Positron Notebook Editor." />
<p>This release brings a wave of polish and reliability we hope makes it ready for your daily workflow:</p>
<ul>
<li>
<p><strong>Cleaner git diffs by default:</strong> New 






<a href="positron://settings/notebook.save.outputs"><code>notebook.save.outputs</code></a>
 and 






<a href="positron://settings/notebook.save.executionCounts"><code>notebook.save.executionCounts</code></a>
 settings let you exclude outputs and execution counts from saved notebook files so reviewers see only the relevant code changes.</p>
</li>
<li>
<p><strong>Stronger R support:</strong> R notebooks can now be debugged with breakpoints and the &ldquo;Debug Cell&rdquo; action, and R dataframe outputs in notebook cells now render in the inline Data Explorer for the same interactive exploration you get in the Console.</p>
</li>
<li>
<p><strong>Help on F1:</strong> Pressing <kbd>F1</kbd> on a function in a notebook cell now opens the 






<a href="https://positron.posit.co/help-pane" target="_blank" rel="noopener">Help pane</a>
, just like in <code>.py</code> and <code>.R</code> files. Works for both Python and R Jupyter notebooks.</p>
</li>
<li>
<p><strong>UX polish:</strong> Long cell outputs default to scrolling instead of overwhelming the page, the cell action bar stays visible as you scroll through tall cells, markdown cells render footnotes, and your scroll position is preserved when switching between notebook tabs.</p>
</li>
</ul>
<h3 id="ai-chat-and-code-assistance">AI chat and code assistance
</h3>
<p>This release introduces two new AI options: Posit Assistant and Posit AI.</p>
<ul>
<li>
<p>You may have tried some of our previous experiments for AI code assistance available in Positron, Positron Assistant and/or Databot. We are now migrating to a more data-science-specific, holistic, unified approach to AI chat, built on the same Posit Assistant interface that 






<a href="https://posit.co/blog/introducing-ai-in-rstudio/" target="_blank" rel="noopener">we recently brought to RStudio</a>
. We acknowledge that the number of different tools we&rsquo;ve made available and how their names have evolved have been pretty confusing, but this space is changing fast and our initial experiments have helped us understand the best ways to integrate AI into your data science workflow. With Posit Assistant, instead of switching between tools, you get code generation, next edit suggestions, chat, and agentic tools all in one place. 






<a href="https://posit-dev.github.io/assistant/docs/downloads/positron/" target="_blank" rel="noopener">Learn how to get started</a>
.</p>
</li>
<li>
<p>






<a href="https://posit.co/products/ai" target="_blank" rel="noopener">Posit AI</a>
 is a new, optional model provider service available to use with Posit Assistant. Posit AI is a subscription priced at $20/month for individual users; both Positron and Posit Assistant remain free to use. 






<a href="https://posit.co/products/ai" target="_blank" rel="noopener">Learn how to sign up for an account</a>
.</p>
</li>
</ul>
<p>This release also provides additional improvements to the broader Assistant experience. Each Assistant response now shows which model generated it, so you always know whether you&rsquo;re getting output from Claude, GPT-5, or another provider. We also improved the documentation and error handling for Microsoft Foundry and prompt caching for AWS Bedrock.</p>
<h2 id="whats-coming-next">What&rsquo;s coming next
</h2>
<ul>
<li>






<a href="https://opensource.posit.co/blog/2026-04-07_april-newsletter/" target="_blank" rel="noopener">Last release</a>
, we announced 






<a href="https://opensource.posit.co/blog/2026-04-06_positron-server-jupyterhub/" target="_blank" rel="noopener">Positron Server</a>
 for academic use in JupyterHub, making it free to host Positron for teaching. We are now expanding support for Positron Server on Open OnDemand. If you are interested in using Positron for teaching, 






<a href="https://opensource.posit.co/blog/2026-04-06_positron-server-jupyterhub/" target="_blank" rel="noopener">follow the instructions</a>
 to get a teaching license and 






<a href="https://scheduler.zoom.us/cindy-tong/positron-education" target="_blank" rel="noopener">book time with our team</a>
 to discuss your use case.</li>
<li>We&rsquo;re 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>Join 






<a href="https://posit.co/webinar/compliance-without-friction-mastering-the-persistent-analysis-lifecycle" target="_blank" rel="noopener">our next webinar</a>
 on May 12 to learn how Positron, Workbench, Connect, and Package Manager work together in the reproducible analysis lifecycle.</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-05-11_positron-2026-05-release/featured.svg" length="79216" type="image/svg&#43;xml" />
    </item>
    <item>
      <title>AI Newsletter: A Data Cleaning Mode for Posit Assistant</title>
      <link>https://opensource.posit.co/blog/2026-05-08_ai-newsletter/</link>
      <pubDate>Fri, 08 May 2026 00:00:00 +0000</pubDate>
      <guid>https://opensource.posit.co/blog/2026-05-08_ai-newsletter/</guid>
      <dc:creator>Sara Altman</dc:creator>
      <dc:creator>Simon Couch</dc:creator><description><![CDATA[<h2 id="posit-news">Posit news
</h2>
<p><strong>The latest release of Posit Assistant includes a Data Cleaning Mode.</strong> When Posit Assistant enters the mode, it spends time identifying and fixing data quality issues and preparing your data for analysis. When certain decisions (e.g., how to recode a variable) require user decisions, it surfaces those decisions in a specialized interface. When you and Posit Assistant are done making decisions about the cleaning process, all the cleaning code is written to a script.</p>
<p><div class="not-prose"><figure>
    <img class="h-auto max-w-full rounded-lg"
      src="https://opensource.posit.co/blog/2026-05-08_ai-newsletter/images/data-cleaning-mode.png"
      alt="A screenshot of Posit Assistant in Data Cleaning Mode. The agent is asking the user a number of questions, presented as tabs, about data quality. The active question points out that the distribution of a variable in the data is very uneven and includes a table of row counts by the variable. The agent recommends keeping all rows, but runs this decision by the user first."  title="Posit Assistant in Data Cleaning Mode, surfacing data quality decisions to the user." 
      loading="lazy"
    ><figcaption class="text-sm text-center text-gray-500">Posit Assistant in Data Cleaning Mode, surfacing data quality decisions to the user.</figcaption>
  </figure></div>
</p>
<p>Broadly, we&rsquo;ve seen that many coding agents seem to have a superficial regard for data quality, focusing only on errors thrown by analysis code. This is one of several features we&rsquo;re iterating on to tailor the agent experience more closely to the real work of data science.</p>
<p><strong>The initial release of the tabpfn package 






<a href="https://tidyverse.org/blog/2026/03/tabpfn-0-1-0/" target="_blank" rel="noopener">recently made it to CRAN</a>
.</strong> TabPFN is a pre-trained neural network for tabular data. In a typical predictive modeling workflow, you train a model on existing data (the training data), and then apply the resulting model to new, unseen data. TabPFN, in contrast, is a neural network pre-trained on a vast array of synthetic datasets. When using TabPFN, the model learns from the analyst&rsquo;s training data <em>in-context</em>, in a similar way that an LLM can &rsquo;learn&rsquo; over the course of a conversation, allowing it to predict on new data without an explicit training step. The tabpfn package provides R tidymodels bindings to the pre-trained model.</p>
<h2 id="terms">Terms
</h2>
<p>TabPFN and LLMs rely on <strong>in-context learning</strong> to provide more helpful responses.</p>
<p>Many recent model releases have announced increased <strong>context windows</strong> of 1 million tokens.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> The context window is the maximum length of the &lsquo;conversation history&rsquo;. So, if you&rsquo;ve had 10 messages back and forth with an LLM, and each message was composed of 100 tokens, the context length would be 1,000, well under the context window for most modern LLMs.</p>
<p>LLMs learn in a few different stages. The most recognizable stage is probably <strong>pre-training,</strong> in which the model gains general knowledge and capabilities by learning from massive collections of text. Another important stage of learning is <strong>in-context learning,</strong> which happens over the course of a conversation. For example, if you paste a document into an LLM chat and then start asking questions about it, the document has been learned in-context.</p>
<p>TabPFN follows this same learning setup as modern LLMs. The TabPFN team pre-trains the neural network and then allows users to download the resulting weights. Then, the model learns from the analysts&rsquo; training data in-context.</p>
<h2 id="learn-more">Learn more
</h2>
<ul>
<li>OpenAI released 






<a href="https://openai.com/index/introducing-gpt-5-5/" target="_blank" rel="noopener">GPT 5.5</a>
, an incremental improvement over GPT 5.4. Google Gemini released 






<a href="https://blog.google/innovation-and-ai/models-and-research/gemini-models/next-generation-gemini-deep-research/" target="_blank" rel="noopener">Deep Research Max</a>
, an incremental improvement over its Deep Research tool.</li>
<li>Yihui Xie 






<a href="https://yihui.org/en/2026/05/ai-reflections/" target="_blank" rel="noopener">wrote up a thoughtful reflection on his experience with AI-assisted coding</a>
.</li>
<li>LLM use for open-source contributions can be controversial. 






<a href="https://ziglang.org/" target="_blank" rel="noopener">Zig</a>
 has instituted a 






<a href="https://ziglang.org/code-of-conduct/" target="_blank" rel="noopener">ban on LLM contributions</a>
. You can read their rationale 






<a href="https://kristoff.it/blog/contributor-poker-and-ai/" target="_blank" rel="noopener">here</a>
.</li>
</ul>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>A token is, roughly, a word.&#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-05-08_ai-newsletter/images/featured.png" length="1641281" type="image/png" />
    </item>
    <item>
      <title>Bringing OpenTelemetry to R in production</title>
      <link>https://opensource.posit.co/blog/2026-05-07_opentelemetry/</link>
      <pubDate>Thu, 07 May 2026 00:00:00 +0000</pubDate>
      <guid>https://opensource.posit.co/blog/2026-05-07_opentelemetry/</guid>
      <dc:creator>Charlie Gao</dc:creator>
      <dc:creator>Aaron Jacobs</dc:creator>
      <dc:creator>Barret Schloerke</dc:creator>
      <dc:creator>Gábor Csárdi</dc:creator><description><![CDATA[<p>We&rsquo;re bringing 






<a href="https://opentelemetry.io/" target="_blank" rel="noopener">OpenTelemetry</a>
 to R. As a Posit-wide initiative across our open source packages, we&rsquo;ve instrumented some of the most widely-used R packages for production workloads &ndash; 






<a href="https://shiny.posit.co/" target="_blank" rel="noopener">Shiny</a>
, 






<a href="https://plumber2.posit.co/" target="_blank" rel="noopener">plumber2</a>
, 






<a href="https://mirai.r-lib.org" target="_blank" rel="noopener">mirai</a>
, 






<a href="https://httr2.r-lib.org" target="_blank" rel="noopener">httr2</a>
, 






<a href="https://ellmer.tidyverse.org" target="_blank" rel="noopener">ellmer</a>
, 






<a href="https://pkg.yihui.org/knitr/" target="_blank" rel="noopener">knitr</a>
, 






<a href="https://testthat.r-lib.org" target="_blank" rel="noopener">testthat</a>
 and 






<a href="https://dbi.r-dbi.org" target="_blank" rel="noopener">DBI</a>
. You can add observability to your R applications with <strong>no code changes</strong>: set a few environment variables and you get traces, logs, and metrics flowing to the backend of your choice.</p>
<p>This is part of our commitment to R in production. As R applications scale &ndash; more users, more processes, more machines &ndash; you need tools to understand what&rsquo;s happening across your entire system. That&rsquo;s what OpenTelemetry is built for, and it&rsquo;s now available for R.</p>
<h2 id="what-is-opentelemetry">What is OpenTelemetry?
</h2>
<p>






<a href="https://opentelemetry.io/" target="_blank" rel="noopener">OpenTelemetry</a>
 (OTel) is a vendor-neutral, open source observability framework backed by the 






<a href="https://www.cncf.io/" target="_blank" rel="noopener">Cloud Native Computing Foundation</a>
.
It has broad industry support across languages and platforms, and is already the standard in the Python, Java, JavaScript, and Go ecosystems. Now it&rsquo;s available for R.</p>
<p>OpenTelemetry defines a standard for collecting telemetry data:</p>
<ul>
<li><strong>Traces</strong> follow an operation as it moves through your system, showing exactly which functions ran, in what order, and how long each took.</li>
<li><strong>Metrics</strong> capture numerical measurements over time &ndash; things like request counts, response latencies, or memory usage.</li>
<li><strong>Logs</strong> record detailed events as they happen, providing context when you need to investigate a specific moment.</li>
</ul>
<p>The instrumented packages described in this post all use the 






<a href="https://otel.r-lib.org" target="_blank" rel="noopener">otel</a>
 package under the hood, and focus on traces, which provide the most immediate value for understanding production behavior. You can also use otel directly to add your own metrics and logs to your application code.</p>
<h2 id="why-observability-matters-for-r-in-production">Why observability matters for R in production
</h2>
<p>When you&rsquo;re developing interactively in RStudio or Positron, debugging is straightforward &ndash; you can step through code, inspect objects, and add print statements. But when your R code runs in production &ndash; a Shiny app serving hundreds of users, a plumber2 API handling thousands of requests, a batch pipeline running across a cluster &ndash; the picture changes.</p>
<p>The core concept in OpenTelemetry is a <strong>trace</strong>: the full path of a request through your system. Each trace is made up of <strong>spans</strong> &ndash; individual units of work with a name and a duration. Spans nest inside each other, so you can follow how each operation triggered the next:</p>
<p><div class="not-prose"><figure>
    <img class="h-auto max-w-full rounded-lg"
      src="https://opensource.posit.co/blog/2026-05-07_opentelemetry/otel-layers.svg"
      alt="A trace is made up of spans, each representing a unit of work. Nested spans show how operations cascade through your system." 
      loading="lazy"
    >
  </figure></div>
</p>
<p>This structure gives you four things that are hard to get any other way:</p>
<ul>
<li><strong>Performance</strong>: Which part of a request is slow? Span durations pinpoint where time is spent &ndash; and nesting reveals unnecessary overhead.</li>
<li><strong>Errors</strong>: In development and testing, you know where errors are &ndash; you wrote the test, you control the inputs. In production, errors surface far from their root cause, across process boundaries and async operations, triggered by conditions you never anticipated. Traces show you the full chain of real operations that led to each failure, in the context where it actually happened.</li>
<li><strong>Centralized view</strong>: When your application extends across multiple R processes or machines &ndash; a Shiny app with mirai workers, or a plumber2 API behind a load balancer &ndash; traces are aggregated into a single view across all of them.</li>
<li><strong>Real-time monitoring</strong>: OTel is designed to be left on in production, not just enabled during testing or staging. With low overhead, it runs continuously &ndash; so you see what&rsquo;s happening as it happens, and dashboards and alerts catch problems before users report them.</li>
</ul>
<h2 id="instrumented-packages">Instrumented packages
</h2>
<p>We&rsquo;ve worked across teams to add OpenTelemetry instrumentation to the R packages where it matters most:</p>
<table>
  <thead>
      <tr>
          <th style="text-align: left">Package</th>
          <th style="text-align: left">Version</th>
          <th style="text-align: left">What it traces</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td style="text-align: left">






<a href="https://shiny.posit.co/" target="_blank" rel="noopener">Shiny</a>
</td>
          <td style="text-align: left">≥ 1.12.0</td>
          <td style="text-align: left">Session lifecycle, reactive updates, reactive expressions, background tasks</td>
      </tr>
      <tr>
          <td style="text-align: left">






<a href="https://plumber2.posit.co/" target="_blank" rel="noopener">plumber2</a>
</td>
          <td style="text-align: left">≥ 0.2.0</td>
          <td style="text-align: left">API request handling, routing, endpoint execution</td>
      </tr>
      <tr>
          <td style="text-align: left">






<a href="https://mirai.r-lib.org" target="_blank" rel="noopener">mirai</a>
</td>
          <td style="text-align: left">≥ 2.5.0</td>
          <td style="text-align: left">Task dispatch, daemon execution, results</td>
      </tr>
      <tr>
          <td style="text-align: left">






<a href="https://httr2.r-lib.org" target="_blank" rel="noopener">httr2</a>
</td>
          <td style="text-align: left">≥ 1.2.2</td>
          <td style="text-align: left">HTTP requests and responses</td>
      </tr>
      <tr>
          <td style="text-align: left">






<a href="https://ellmer.tidyverse.org" target="_blank" rel="noopener">ellmer</a>
</td>
          <td style="text-align: left">≥ 0.4.1</td>
          <td style="text-align: left">LLM API calls, tool execution, token usage</td>
      </tr>
      <tr>
          <td style="text-align: left">






<a href="https://pkg.yihui.org/knitr/" target="_blank" rel="noopener">knitr</a>
</td>
          <td style="text-align: left">≥ 1.51</td>
          <td style="text-align: left">Document rendering, chunk evaluation</td>
      </tr>
      <tr>
          <td style="text-align: left">






<a href="https://testthat.r-lib.org" target="_blank" rel="noopener">testthat</a>
</td>
          <td style="text-align: left">≥ 3.3.2</td>
          <td style="text-align: left">Test execution</td>
      </tr>
      <tr>
          <td style="text-align: left">






<a href="https://dbi.r-dbi.org" target="_blank" rel="noopener">DBI</a>
</td>
          <td style="text-align: left">≥ 1.3.0</td>
          <td style="text-align: left">Database queries and connections</td>
      </tr>
  </tbody>
</table>
<p>Together, these packages cover the most common production R workloads: web applications, APIs, parallel computing, HTTP clients, AI/LLM tools, report rendering pipelines, CI test runs, and database access. Because the instrumentation is built into the packages themselves, you benefit from it automatically &ndash; no wrapper functions, nor modifications to existing code. If you already have a Shiny app or plumber2 API, your app will generate traces as soon as you enable OpenTelemetry. Your application code stays exactly as it is.</p>
<h2 id="seeing-it-in-action">Seeing it in action
</h2>
<p>To make this concrete, let&rsquo;s look at a Shiny chat app built with 






<a href="https://posit-dev.github.io/shinychat/" target="_blank" rel="noopener">shinychat</a>
 and 






<a href="https://ellmer.tidyverse.org" target="_blank" rel="noopener">ellmer</a>
 that fetches weather forecasts. It uses mirai for async execution and httr2 for weather API requests.</p>
<p><div class="not-prose"><figure>
    <img class="h-auto max-w-full rounded-lg"
      src="https://opensource.posit.co/blog/2026-05-07_opentelemetry/chat-demo.png"
      alt="The weather chat app uses Shiny, shinychat, ellmer, mirai, and httr2 &ndash; all instrumented with OpenTelemetry." 
      loading="lazy"
    >
  </figure></div>
</p>
<p>A user asks about the weather in Atlanta and Newcastle:</p>
<p><div class="not-prose"><figure>
    <img class="h-auto max-w-full rounded-lg"
      src="https://opensource.posit.co/blog/2026-05-07_opentelemetry/chat-screenshot.png"
      alt="The running chat application, showing a successful weather query for Atlanta and a failed one for Newcastle." 
      loading="lazy"
    >
  </figure></div>
</p>
<p>With OpenTelemetry enabled, every step is captured automatically. Here are the traces from those two queries:</p>
<p><div class="not-prose"><figure>
    <img class="h-auto max-w-full rounded-lg"
      src="https://opensource.posit.co/blog/2026-05-07_opentelemetry/otel-traces.png"
      alt="Traces from the weather chat app, showing the full chain of operations from Shiny session through to HTTP requests. The second request shows an error (red) that&rsquo;s immediately visible in the trace." 
      loading="lazy"
    >
  </figure></div>
</p>
<p>The trace reveals the full chain of operations from user input through to the HTTP request, across process boundaries, with no manual logging. The nesting shows how each step triggered the next, and the durations show where time was spent.</p>
<p>The second query failed. Without tracing, you&rsquo;d see an error in your logs and start investigating. Here, the failure is immediately visible &ndash; the red span pinpoints where it occurred and the surrounding context shows why. In a production system with many concurrent users, that&rsquo;s the difference between minutes and seconds of debugging.</p>
<h2 id="getting-started">Getting started
</h2>
<p>Getting started requires the otelsdk package and a few environment variables. No changes to your application code.</p>
<p>Here&rsquo;s how the pieces fit together: the instrumented R packages generate telemetry data as they run. The 






<a href="https://otelsdk.r-lib.org" target="_blank" rel="noopener">otelsdk</a>
 package collects and exports this data over HTTP to a <strong>backend</strong> &ndash; a service that stores your traces and provides a web dashboard where you can search, filter, and visualize them (like the trace screenshots above).</p>
<h3 id="step-1-install-otelsdk">Step 1: Install otelsdk
</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">install.packages</span><span class="p">(</span><span class="s">&#34;otelsdk&#34;</span><span class="p">)</span></span></span></code></pre></div></div>
<h3 id="step-2-choose-a-backend">Step 2: Choose a backend
</h3>
<p>OpenTelemetry is vendor-neutral, so you can send your data to any compatible backend:</p>
<ul>
<li><strong>Cloud services</strong>: 






<a href="https://logfire.pydantic.dev/" target="_blank" rel="noopener">Logfire</a>
, 






<a href="https://grafana.com/products/cloud/" target="_blank" rel="noopener">Grafana Cloud</a>
, 






<a href="https://langfuse.com/" target="_blank" rel="noopener">Langfuse</a>
</li>
<li><strong>Self-hosted</strong>: 






<a href="https://www.jaegertracing.io/" target="_blank" rel="noopener">Jaeger</a>
, 






<a href="https://zipkin.io/" target="_blank" rel="noopener">Zipkin</a>
, 






<a href="https://prometheus.io/" target="_blank" rel="noopener">Prometheus</a>
</li>
</ul>
<p>During development, you can also run an 






<a href="https://opentelemetry.io/docs/collector/" target="_blank" rel="noopener">OpenTelemetry Collector</a>
 on your own machine and inspect traces directly, without sending data anywhere else.</p>
<p>Each backend will give you an endpoint URL and an authentication token. The 


  
  
  





<a href="https://otelsdk.r-lib.org/reference/collecting.html#setup" target="_blank" rel="noopener">otelsdk collecting telemetry data guide</a>
 has examples for some common backends.</p>
<h3 id="step-3-set-environment-variables">Step 3: Set environment variables
</h3>
<p>Add these to your <code>.Renviron</code> file (use <code>usethis::edit_r_environ()</code> to open it), replacing the endpoint and token with the values from your chosen backend. This example uses 






<a href="https://logfire.pydantic.dev/" target="_blank" rel="noopener">Logfire</a>
, which offers a free tier to get started:</p>
<div class="code-block"><pre tabindex="0"><code>OTEL_TRACES_EXPORTER=&#34;http&#34;
OTEL_EXPORTER_OTLP_ENDPOINT=&#34;https://logfire-eu.pydantic.dev&#34;
OTEL_EXPORTER_OTLP_HEADERS=&#34;Authorization=&lt;YOUR-WRITE-TOKEN&gt;&#34;</code></pre></div>
<p>If you&rsquo;re deploying content to Posit Connect, refer to 


  
  
  





<a href="https://docs.posit.co/connect/user/content-settings/#setting-env-vars" target="_blank" rel="noopener">how to set environment variables on Posit Connect</a>
.</p>
<h3 id="step-4-run-your-app">Step 4: Run your app
</h3>
<p>That&rsquo;s it. Restart R, then run your Shiny app, plumber2 API, or any code that uses the instrumented packages. Traces will flow to your backend automatically. Open your backend&rsquo;s web dashboard to see them &ndash; you&rsquo;ll see a view like the trace screenshots shown above, with each span representing an operation in your application.</p>
<p>You can verify that tracing is active at any time:</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">otel</span><span class="o">::</span><span class="nf">is_tracing_enabled</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt; [1] TRUE</span></span></span></code></pre></div></div>
<p>OpenTelemetry is designed to be safe in production. If anything goes wrong in the telemetry code itself, it will never crash your application &ndash; errors are silently suppressed so your app keeps running.</p>
<h2 id="zero-code-instrumentation">Zero-code instrumentation
</h2>
<p>Beyond the packages that ship with built-in instrumentation, otel supports <strong>zero-code instrumentation</strong> for any R package. Set the <code>OTEL_R_INSTRUMENT_PKGS</code> environment variable to a comma-separated list of package names, and otel will automatically create spans for their exported functions:</p>
<div class="code-block"><pre tabindex="0"><code>OTEL_R_INSTRUMENT_PKGS=dplyr,tidyr</code></pre></div>
<p>You can also fine-tune which functions are instrumented using include and exclude filters:</p>
<div class="code-block"><pre tabindex="0"><code>OTEL_R_INSTRUMENT_PKGS_DPLYR_INCLUDE=mutate,filter,select</code></pre></div>
<p>This is useful for adding visibility to any package in your stack, even those without built-in OTel support. See the 






<a href="https://otel.r-lib.org/reference/zci.html" target="_blank" rel="noopener">otel documentation</a>
 for full details.</p>
<h2 id="configuration-options">Configuration options
</h2>
<p>The otelsdk package is configured entirely through environment variables, following OpenTelemetry conventions:</p>
<table>
  <thead>
      <tr>
          <th style="text-align: left">Variable</th>
          <th style="text-align: left">Purpose</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td style="text-align: left"><code>OTEL_TRACES_EXPORTER</code></td>
          <td style="text-align: left">Exporter type for traces (e.g. <code>&quot;http&quot;</code>)</td>
      </tr>
      <tr>
          <td style="text-align: left"><code>OTEL_LOGS_EXPORTER</code></td>
          <td style="text-align: left">Exporter type for logs</td>
      </tr>
      <tr>
          <td style="text-align: left"><code>OTEL_METRICS_EXPORTER</code></td>
          <td style="text-align: left">Exporter type for metrics</td>
      </tr>
      <tr>
          <td style="text-align: left"><code>OTEL_EXPORTER_OTLP_ENDPOINT</code></td>
          <td style="text-align: left">URL of the OTLP-compatible backend</td>
      </tr>
      <tr>
          <td style="text-align: left"><code>OTEL_EXPORTER_OTLP_HEADERS</code></td>
          <td style="text-align: left">Authentication headers for the backend</td>
      </tr>
      <tr>
          <td style="text-align: left"><code>OTEL_R_INSTRUMENT_PKGS</code></td>
          <td style="text-align: left">Packages for zero-code instrumentation</td>
      </tr>
      <tr>
          <td style="text-align: left"><code>OTEL_R_EMIT_SCOPES</code></td>
          <td style="text-align: left">Restrict telemetry to specific packages</td>
      </tr>
      <tr>
          <td style="text-align: left"><code>OTEL_R_SUPPRESS_SCOPES</code></td>
          <td style="text-align: left">Exclude specific packages from telemetry</td>
      </tr>
  </tbody>
</table>
<p>See the 






<a href="https://otelsdk.r-lib.org/reference/environmentvariables.html" target="_blank" rel="noopener">otelsdk environment variables reference</a>
 for the complete list.</p>
<h2 id="looking-ahead">Looking ahead
</h2>
<p>With instrumentation built into the packages that R users already rely on, observability becomes something you can turn on, not something you have to build.</p>
<p>We&rsquo;re also integrating an OpenTelemetry collector into 






<a href="https://posit.co/products/enterprise/connect/" target="_blank" rel="noopener">Posit Connect</a>
, giving you an end-to-end observability solution you can simply turn on or off.</p>
<p>OTel support across the ecosystem continues to expand. If you&rsquo;d like to learn more:</p>
<ul>
<li>






<a href="https://otel.r-lib.org" target="_blank" rel="noopener">otel package documentation</a>
 &ndash; the instrumentation API</li>
<li>






<a href="https://otelsdk.r-lib.org" target="_blank" rel="noopener">otelsdk package documentation</a>
 &ndash; the SDK for collecting and exporting telemetry</li>
<li>






  
  

<a href="https://opensource.posit.co/blog/2025-12-10_shiny-r-1.12/">Shiny 1.12 OTel blog post</a>
 &ndash; deep dive into Shiny&rsquo;s OpenTelemetry support</li>
<li>






<a href="https://opentelemetry.io/" target="_blank" rel="noopener">OpenTelemetry project</a>
 &ndash; the upstream standard</li>
</ul>
<p>We&rsquo;re excited about what this opens up for the R community. Whether you&rsquo;re running a Shiny dashboard for a small team, a plumber2 API serving thousands of requests, or a data pipeline distributed across a cluster &ndash; you now have the tools to see what&rsquo;s happening, in real time, with no code changes required.</p>
]]></description>
      <enclosure url="https://opensource.posit.co/blog/2026-05-07_opentelemetry/featured.jpg" length="489929" type="image/jpeg" />
    </item>
    <item>
      <title>Quarto 2: Parsing and Source Maps</title>
      <link>https://opensource.posit.co/blog/2026-05-07_quarto-2-parsing/</link>
      <pubDate>Thu, 07 May 2026 00:00:00 +0000</pubDate>
      <guid>https://opensource.posit.co/blog/2026-05-07_quarto-2-parsing/</guid>
      <dc:creator>Carlos Scheidegger</dc:creator><description><![CDATA[<p>This is the first of a series of posts about the design and features in Quarto 2.</p>
<h2 id="ux-requirements-for-a-text-centric-authoring-system">UX Requirements for a text-centric authoring system
</h2>
<p>Although Quarto 2 is now a standalone, new version of the Quarto system, it started as an attempt to solve long-standing parsing problems in Quarto 1.
We soon realized there were three fundamental, separate syntax concerns: syntax errors, awareness of source locations during document processing, and syntax stability. We eventually concluded that none of these features could be solved incrementally in Quarto 1, which led to where we are today.</p>
<h3 id="requirement-1-syntax-errors">Requirement 1: Syntax errors
</h3>
<p>Markdown is a very convenient language for lightly formatted text, and its minimalism
keeps the source exceedingly readable on its own.
Unfortunately, Markdown (in)famously has no syntax errors; every sequence of characters is a valid Markdown document. This is explicitly enshrined in the 


  
  
  





<a href="https://spec.commonmark.org/0.31.2/#characters-and-lines" target="_blank" rel="noopener">CommonMark spec</a>
:</p>
<blockquote>
<p>Any sequence of Unicode characters is a valid Commonmark document.</p>
</blockquote>
<p>We believe this to be a fundamentally misguided principle.
Instead, we believe that error messages are communication scaffolds, and that accepting error messages as a useful tool better reflects the reality of Markdown authoring in 2026.
In short, Quarto has expectations about input documents, and users make typing mistakes.</p>
<p>In the course of teaching Quarto, we repeatedly witness learners make the same classes of Markdown syntax errors when authoring Quarto documents. Let&rsquo;s take the following, typical example. Quarto makes extensive use of <em>fenced divs</em>, structural elements in Pandoc Markdown documents that can denote a variety of constructs, such as figures, multiple-column layouts, and callouts.</p>
<pre><code>::: {.callout-warning appearance=&quot;minimal&quot;}

If you make syntax errors in Quarto 1, the system is unable to tell you about them.

:::
</code></pre>
<p>Fenced divs can have classes and attributes, but the attribute syntax in Pandoc Markdown is somewhat brittle: <code>{key=&quot;value&quot;}</code> produces an attribute, but <code>{key = &quot;value&quot;}</code> doesn&rsquo;t.
But Markdown has no syntax errors. As a result, at best users see the attributes
in the text and need to fix their source. At worst, this mistake falls through the cracks all the way to the published document.
Quarto 1 attempts to detect and patch over these rough edges, but this isn&rsquo;t robust enough. If a user accidentally adds spaces between the key and value of an attribute, they get a mangled paragraph with <code>:::</code> in it instead of a div.</p>
<p>If we accept this reality, then the best we can do is provide guidance, as clearly as we can, about the sources of errors.</p>
<p>This requirement provided the initial motivation for us to design a formal grammar of the Quarto Markdown (&ldquo;qmd&rdquo;) dialect using the 






<a href="https://tree-sitter.github.io/tree-sitter/" target="_blank" rel="noopener">Tree-sitter system</a>
. Because we have a formal grammar, documents might fail to parse as Markdown, and must be fixed before output is produced. But this trade-off allows us to provide contextual feedback in editors and in the command-line tooling.</p>
<p>Our early experience with Quarto 2 gives us reason for optimism: we find that early reporting of syntax errors is not overly cumbersome and helps catch real problems. This includes, notably, several syntax errors which had slipped through our review into the 






<a href="https://quarto.org" target="_blank" rel="noopener">Quarto website</a>
. It also gives us more than just the ability to reject invalid input. Parse failures have additional information that we can use to produce precise, actionable error messages. We&rsquo;ll have more to say about that in the future: stay tuned!</p>
<p>In the meantime, here&rsquo;s a preview of what syntax errors can buy you. Consider this simple Quarto 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></span><span class="line"><span class="cl">format: html
</span></span><span class="line"><span class="cl">---
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">I _accidentally forgot to end this emphasis.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">A new paragraph.</span></span></code></pre></div></div>
<p>In Quarto 2, you will get an error like this:</p>
<pre><code>syntax-error-1.qmd: Error: [Q-2-5] Unclosed Underscore Emphasis
   ╭─[ syntax-error-1.qmd:5:45 ]
   │
 5 │ I _accidentally forgot to end this emphasis.
   │  ─┬                                         ┬
   │   ╰──────────────────────────────────────────── This is the opening '_' mark.
   │                                             │
   │                                             ╰── I reached the end of the block before finding a closing '_' for the emphasis.
</code></pre>
<h3 id="requirement-2-accurate-fine-grained-source-maps">Requirement 2: Accurate, fine-grained source maps
</h3>
<p>Most error states in Quarto can be associated with a particular region of a source document. Syntax errors can always be traced to the first character that fails to correspond to the grammar of the language being parsed (and often to more useful diagnostics). YAML metadata problems, such as using a number where a string is expected, are not <em>syntax</em> errors, but are errors nevertheless, and also can be associated with the portion of the document where the user typed a number (intentionally or not).</p>
<p>Quarto 1 has good support for YAML error messages (as well as auto-completion).
What it lacks is support for error messages <em>like</em> those of the YAML system beyond YAML metadata.
For example, there are only a fixed number of callout types in Quarto. If someone writes <code>::: callout-beware</code>, it&rsquo;s likely that this is a mistake. Even if we don&rsquo;t want to issue a syntax error, it would be great to offer a warning with accurate source information in the command-line application; even better, we should have diagnostics available for modern text editors and IDEs, so the warning shows up instantly as the user is authoring the document.</p>
<p>In order to do this reliably, Quarto needs access to source information for the entirety of the document: metadata, headings, divs, spans, attributes, and so on.
In addition, this information needs to be preserved through the entire processing pipeline, from parsing to crossref generation to the application of format-specific templates.
That granularity of source information simply isn&rsquo;t compatible with a system like Pandoc, whose entire point is to provide independence between input <em>and</em> output formats.
This isn&rsquo;t to say that Pandoc is wrong here; it&rsquo;s a brilliant design and system that will remain useful and necessary in the Markdown ecosystem (and Quarto 2 will continue to bundle Pandoc for a number of tasks). But Quarto&rsquo;s constraints are different, and require a different solution.</p>
<p>We note that Quarto will continue to interoperate with Pandoc. The final notable feature of Quarto 2&rsquo;s source maps is that Quarto&rsquo;s JSON representation of its AST is fully compatible with Pandoc, and yet includes source mapping information for every node in the AST. We designed it such that Pandoc accepts the document, by picking field names in the JSON schema that are not used by Pandoc, and maintaining the Pandoc fields precisely as they are.</p>
<p>For example, in Quarto 2 this means that error messages include source locations deep in document templates.
Concretely, if a user doesn&rsquo;t define an expected template variable in Quarto 2, we&rsquo;re able to emit diagnostics.
Consider what happens if a user specifies a custom template with an <code>$author-greeting$</code> variable but the Quarto 2 document doesn&rsquo;t define that:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="cp">&lt;!doctype html&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">html</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;en&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">charset</span><span class="o">=</span><span class="s">&#34;utf-8&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">title</span><span class="p">&gt;</span>$title$<span class="p">&lt;/</span><span class="nt">title</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">head</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">header</span><span class="p">&gt;</span>by $author-greeting$<span class="p">&lt;/</span><span class="nt">header</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">main</span><span class="p">&gt;</span>$body$<span class="p">&lt;/</span><span class="nt">main</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">html</span><span class="p">&gt;</span></span></span></code></pre></div></div>
<p>The diagnostic you&rsquo;ll get looks like this:</p>
<pre><code>Warning: [Q-10-2] Undefined variable: author-greeting
   ╭─[ post.html:8:16 ]
   │
 8 │     &lt;header&gt;by $author-greeting$&lt;/header&gt;
   │                ────────┬───────
   │                        ╰───────── Undefined variable: author-greeting
───╯
</code></pre>
<h3 id="requirement-3-syntax-stability-over-time">Requirement 3: Syntax stability over time
</h3>
<p>In the half decade since its inception, Quarto 1 has accrued a large number of workarounds to support its <code>qmd</code> dialect, many inherited from the RMarkdown and knitr ecosystems.</p>
<p>Consider this basic example of an executable code cell in Quarto/RMarkdown syntax:</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="sb">`{r}
</span></span></span><span class="line"><span class="cl"><span class="sb">cat(&#34;hello world&#34;)
</span></span></span><span class="line"><span class="cl"><span class="sb">`</span>``</span></span></code></pre></div></div>
<p>This syntax is widely supported in the Quarto/RMarkdown ecosystem, but the syntax hasn&rsquo;t actually been supported in Pandoc since 


  
  
  





<a href="https://pandoc.org/releases.html#pandoc-3.0-2023-01-18" target="_blank" rel="noopener">Pandoc 3</a>
, released in January 2023.</p>
<p>Quarto also supports <em>shortcodes</em> inspired by Hugo: <code>{{&lt; video https://youtube.com/... &gt;}}</code>. Early versions of Quarto processed shortcodes after Pandoc&rsquo;s parser was done. But this is a fundamentally impossible task, because transforming Markdown into an internal representation (the &ldquo;AST&rdquo;) is a many-to-one mapping. Both <code>*hello*</code> and <code>_hello_</code> translate to (<code>Emph [Str &quot;hello&quot;]</code>); if we find an instance of that syntax inside something that looks like a shortcode invocation, there&rsquo;s no way to uniquely pull these nodes back to a single string. As a result, Quarto 1 now ships with a complex &ldquo;pre-parser&rdquo; that understands enough <code>qmd</code> syntax to transform shortcodes (and other constructs like the code blocks above) to a syntax that Pandoc can safely parse.</p>
<p>This process is necessary in the Quarto 1 architecture, but is also unacceptably slow. In our benchmarks of rendering <code>quarto-dev/quarto-web</code>, we find that parsing Markdown takes about 1/3 of the <em>total</em> rendering time. In addition, it&rsquo;s very brittle: we have to constantly maintain it to respond to small changes in Pandoc&rsquo;s Markdown parsing.</p>
<p>Finally, Quarto is now five years old. The traditional heuristic tells us we can expect it to be a useful system for at least five more.
We are a bit more ambitious. We want to deliver a system with a useful lifespan of at least 10 to 20 years, and that&rsquo;s what we&rsquo;ve started planning for.</p>
<p>In our view, this means that syntax changes become <em>less</em> enticing over time; as a result, in Quarto 2, we&rsquo;ve replaced the front end of our pipeline with a dedicated parser and AST processor,
to ensure we fully control our largest source of slow but substantial syntax drift.</p>
<h2 id="a-tree-sitter-parser-for-markdown">A tree-sitter parser for Markdown
</h2>
<p>Our first experiment started from a pair of 






<a href="https://github.com/tree-sitter-grammars/tree-sitter-markdown" target="_blank" rel="noopener">tree-sitter grammars from the community</a>
, and extended it to add support for Quarto&rsquo;s unique syntax constructs. Unfortunately, the split between block and inline parsers meant we couldn&rsquo;t get the quality of the resulting parses to match our requirements: Markdown&rsquo;s inline and block processing are fundamentally intertwined, especially when handling indentation-sensitive constructs like block quotes and bulleted lists. As a result, we needed to write a single parser from scratch.</p>
<p>The main technique we employ is tree-sitter&rsquo;s coupled parser+lexer infrastructure. Originally designed to allow incremental parsing, tree-sitter only lexes as much of the document as necessary to make a parsing decision. As a result, it can allow the lexer to use information from the parser to make <em>lexing</em> decisions. This makes tree-sitter parsers technically context-dependent, because tree-sitter lexers are actually in principle Turing-complete, and written in C (for the academics in the audience, we think this is an understudied object of theoretical interest!) After a few false starts, we realized we could lean heavily into this to cleanly solve some thorny issues in Markdown parsing.</p>
<p>The prototypical example is a string like <code>^hello^beautiful^world^</code>. Should this be parsed as <code>Superscript [Str &quot;hello&quot;, Superscript [Str &quot;beautiful&quot;], Str &quot;world&quot;]</code> or <code>[Superscript [Str &quot;hello&quot;], Str &quot;beautiful&quot;, Superscript [Str &quot;world&quot;]]</code>? Markdown is full of such ambiguities. In Quarto 2&rsquo;s new parser, we generally opt for a &ldquo;shortest bracket&rdquo; interpretation: if a character can close an active bracket, we greedily choose to do so. This is possible in tree-sitter because at the moment of lexing the second <code>^</code>, we know the set of possible productions; if we know that <code>CLOSE_SUPERSCRIPT</code> is an acceptable production (and the tree-sitter API offers this information), then we produce it. Otherwise, we produce <code>OPEN_SUPERSCRIPT</code>.</p>
<p>The general consequence is that Quarto 2&rsquo;s Markdown parser will open brackets when closing them would be a syntax error, and close them otherwise.
This technique also works well in other cases.</p>
<h2 id="read-more">Read more
</h2>
<p>The source location infrastructure allows information about the textual source of the document to travel forward in Quarto 2's processing pipeline. One of the exciting new features of Quarto 2 is its ability to &ldquo;pull source information backward&rdquo;. Quarto 2 includes infrastructure to take a change in an AST node, and reason through the necessary changes in the Markdown source, without having to rewrite the entire document.
Our upcoming follow-up post on bidirectionality will continue this discussion.</p>
]]></description>
      <enclosure url="https://opensource.posit.co/blog/2026-05-07_quarto-2-parsing/thumbnail.png" length="207707" type="image/png" />
    </item>
    <item>
      <title>posit::glimpse() Newsletter – May 2026</title>
      <link>https://opensource.posit.co/blog/2026-05-06_2026-05-glimpse/</link>
      <pubDate>Wed, 06 May 2026 00:00:00 +0000</pubDate>
      <guid>https://opensource.posit.co/blog/2026-05-06_2026-05-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! We&rsquo;ve moved to monthly editions, and we still have so much to share.</p>
<h2 id="registration-for-positconf2026-is-now-open">Registration for posit::conf(2026) is now open!
</h2>
<p>Check out the 






<a href="https://posit.co/blog/posit-conf-2026-keynotes" target="_blank" rel="noopener">keynote speakers</a>
, 






<a href="https://posit.co/blog/workshops-at-positconf2026" target="_blank" rel="noopener">workshops</a>
, and 






<a href="https://conf.posit.co/2026/sessions/" target="_blank" rel="noopener">agenda</a>
 for our upcoming conference, happening in Houston and online. It’s sure to be incredible! 






<a href="https://conf.posit.co/2026/registration/" target="_blank" rel="noopener">Register here</a>
.</p>
<h2 id="key-product-updates-and-new-releases">Key product updates and new releases
</h2>
<h3 id="ggsql-alpha-release">ggsql alpha release
</h3>
<p>The alpha release of 






<a href="https://ggsql.org/" target="_blank" rel="noopener">ggsql</a>
 brings the grammar of graphics to SQL, enabling powerful data visualization directly within SQL queries using declarative clauses. Built by the ggplot2 team with 18 years of experience, ggsql executes visualization computations as optimized SQL queries on backends, works without R or Python runtimes, and is designed for integration with Quarto, Jupyter, Positron, and AI agents.</p>
<ul>
<li>Learn more in the 






<a href="https://opensource.posit.co/blog/2026-04-20_ggsql_alpha_release/" target="_blank" rel="noopener">ggsql alpha release</a>
 blog post.</li>
<li>We love seeing folks in the community be early adopters! Harry Snart shares how he 






<a href="https://medium.com/@harrysnart/exploring-ggsql-with-oracle-database-61f59b3e36da" target="_blank" rel="noopener">explored ggsql with Oracle Database</a>
, and Ansgar Wolsing 






<a href="https://bsky.app/profile/ansgarw.bsky.social/post/3mk6noshl222q" target="_blank" rel="noopener">tried it out for the #30DayChartChallenge</a>
.</li>
</ul>
<p><div class="not-prose"><figure>
    <img class="h-auto max-w-full rounded-lg"
      src="https://opensource.posit.co/blog/2026-05-06_2026-05-glimpse/images/image2.png"
      alt="Scatter plot of bill depth versus bill length for three penguin species, where Adelie, Chinstrap, and Gentoo penguins show distinct clusters." 
      loading="lazy"
    >
  </figure></div>
</p>
<h3 id="rag-with-raghilda">RAG with raghilda
</h3>
<p>LLMs are great at reasoning and generating text, but their knowledge is frozen at training time. Retrieval-augmented generation (RAG) solves this by giving the model access to relevant information at query time, without needing to retrain it.</p>
<p>The new 






<a href="https://posit-dev.github.io/raghilda/" target="_blank" rel="noopener">raghilda</a>
 package simplifies building RAG systems in Python. It supports multiple storage backends (DuckDB, ChromaDB, OpenAI Vector Stores) with a consistent API, enabling teams to start locally and scale to hosted solutions without code rewrites.</p>
<ul>
<li>Read more in the 






<a href="https://opensource.posit.co/blog/2026-04-14_rag-with-raghilda/" target="_blank" rel="noopener">RAG with raghilda</a>
 blog post.</li>
</ul>
<h3 id="quarto-19-and-quarto-2">Quarto 1.9 and Quarto 2
</h3>
<p>The Quarto team has announced plans for Quarto 2, a complete rewrite in Rust with a major focus on collaborative editing! Key features include a collaborative editor for web and command-line, a visual editor that works seamlessly alongside source editing without disrupting code, and enhanced error detection across projects. The public release is expected in at least 6 months.</p>
<ul>
<li>Learn more in the 






<a href="https://opensource.posit.co/blog/2026-04-06_whats-next-quarto-2/" target="_blank" rel="noopener">What’s next: Quarto 2 announcement</a>
.</li>
</ul>
<p>Meanwhile, be sure to take a look at all the exciting updates from v1.9, including:</p>
<ul>
<li>






<a href="https://opensource.posit.co/blog/2026-04-14_chrome-headless-shell/" target="_blank" rel="noopener">Chrome Headless Shell in Quarto</a>
</li>
<li>






<a href="https://opensource.posit.co/blog/2026-03-31_typst-books-and-more/" target="_blank" rel="noopener">Typst Books, Article Layout, and typst-gather</a>
</li>
<li>






<a href="https://quarto.org/docs/blog/posts/2026-03-05-pdf-accessibility-and-standards/" target="_blank" rel="noopener">PDF Accessibility and Standards</a>
</li>
</ul>
<p>All v1.9 updates can be found in the 






<a href="https://quarto.org/docs/blog/posts/2026-03-24-1.9-release/" target="_blank" rel="noopener">roundup blog post</a>
.</p>
<h3 id="whats-new-in-positron">What’s new in Positron
</h3>
<p>Our new data science IDE, 






<a href="https://positron.posit.co/" target="_blank" rel="noopener">Positron</a>
, has been significantly updated with numerous improvements, including 


  
  
  





<a href="https://opensource.posit.co/blog/2026-04-07_april-newsletter/#positron-server-for-academic-use-via-jupyterhub" target="_blank" rel="noopener">Positron Server for Academic Use</a>
 via JupyterHub, 


  
  
  





<a href="https://opensource.posit.co/blog/2026-04-07_april-newsletter/#ai-next-steps-in-the-native-jupyter-notebook-editor" target="_blank" rel="noopener">AI enhancements</a>
, 


  
  
  





<a href="https://opensource.posit.co/blog/2026-04-07_april-newsletter/#telemetry-update-anonymous-session-identifiers" target="_blank" rel="noopener">telemetry updates</a>
 and 


  
  
  





<a href="https://opensource.posit.co/blog/2026-04-07_april-newsletter/#rstudio-addins-support" target="_blank" rel="noopener">R improvements</a>
 (like Addins!).</p>
<ul>
<li>Read more in the 






<a href="https://opensource.posit.co/blog/2026-04-07_april-newsletter/" target="_blank" rel="noopener">April 2026 Newsletter</a>
.</li>
<li>For continuous updates on Positron, follow the new 






<a href="https://www.linkedin.com/showcase/positron-ide/posts/?feedView=all" target="_blank" rel="noopener">Positron LinkedIn page</a>
 and subscribe to the 






<a href="https://posit.co/positron-updates-signup/" target="_blank" rel="noopener">Positron newsletter</a>
.</li>
</ul>
<h3 id="shiny-for-python-16">Shiny for Python 1.6
</h3>
<p>






<a href="https://shiny.posit.co/py/" target="_blank" rel="noopener">Shiny for Python</a>
 1.6 is now available. The release introduces toolbar components designed for tight spaces such as card headers, input labels, and AI chat interfaces.</p>
<p>The second major addition is built-in OpenTelemetry support, enabling zero-configuration observability by automatically tracing session lifecycles, reactive updates, and individual reactive expressions. Set <code>SHINY_OTEL_COLLECT=reactivity</code> and send traces to any OTLP-compatible backend like Pydantic Logfire, Jaeger, or Grafana Cloud.</p>
<ul>
<li>Read more in the 






<a href="https://opensource.posit.co/blog/2026-04-02_shiny-python-1.6/" target="_blank" rel="noopener">Shiny for Python 1.6 brings toolbars and OpenTelemetry</a>
 blog post.</li>
</ul>
<h3 id="pointblank-0240">Pointblank 0.24.0
</h3>
<p>






<a href="https://posit-dev.github.io/pointblank/user-guide/integrations/otel-integration.html" target="_blank" rel="noopener">Pointblank</a>
 is a package for data validation. With the release of Pointblank 0.24.0, there is now OpenTelemetry integration for bridging data validation and production observability. After running checks on a table, you can push pass/fail metrics, per-validation-step trace spans, and structured threshold-breach logs to your OTel-compatible backend (Grafana, Datadog, New Relic, etc.). For those that run pipelines in Airflow, Prefect, or Dagster, validation spans slot into existing distributed traces automatically.</p>
<ul>
<li>Read the 






<a href="https://posit-dev.github.io/pointblank/user-guide/integrations/otel-integration.html" target="_blank" rel="noopener">full OTel integration guide</a>
 for setup instructions and examples.</li>
</ul>
<h3 id="tidymodels">Tidymodels
</h3>
<p>The 






<a href="https://www.tidymodels.org/" target="_blank" rel="noopener">tidymodels</a>
 team is on a roll! There are new updates available for dial, parsnip, yardstick, tune, and tidymodels, as well as two new cheatsheets.</p>
<ul>
<li>Read the 






<a href="https://opensource.posit.co/blog/2026-04-27_tidymodels-april-2026/" target="_blank" rel="noopener">New tidymodels Releases for April 2026</a>
 blog post and 






<a href="https://opensource.posit.co/blog/2026-04-29_tidymodels-cheatsheets/" target="_blank" rel="noopener">tidymodels Cheatsheet</a>
 blog posts.</li>
</ul>
<p>The group has also been developing a set of skill files for machine learning with tidymodels as well as developer focused skills. You can find the current versions at 






<a href="https://skills.tidymodels.org/" target="_blank" rel="noopener">skills.tidymodels.org</a>
. Give them a try; the group would love some feedback.</p>
<p><div class="not-prose"><figure>
    <img class="h-auto max-w-full rounded-lg"
      src="https://opensource.posit.co/blog/2026-05-06_2026-05-glimpse/images/image5.png"
      alt="Two-page Create models with parsnip Cheat Sheet, detailing functions for regression, classification, and more." 
      loading="lazy"
    >
  </figure></div>
</p>
<h3 id="mori">mori
</h3>
<p>






<a href="https://shikokuchuo.net/mori/" target="_blank" rel="noopener">mori</a>
 is a new R package for shared memory across processes. ​​Parallel R no longer has to mean duplicating your dataset in every worker&rsquo;s RAM. mori places it in OS-level shared memory once, and every worker maps the same physical pages via R&rsquo;s ALTREP framework. Works with any parallel backend that uses R serialization, including mirai, parallel, and callr.</p>
<ul>
<li>Learn more in the 






<a href="https://opensource.posit.co/blog/2026-04-23_mori-0-1-0/" target="_blank" rel="noopener">mori 0.1.0</a>
 blog post.</li>
<li>Tyler Morgan-Wall has already implemented a fork to the targets package to incorporate mori! 






<a href="https://bsky.app/profile/tylermw.com/post/3mkddpqka4k2t" target="_blank" rel="noopener">Read the discussion here</a>
. It’s fantastic to see the community adopt and improve the ecosystem with open source.</li>
</ul>
<h3 id="tabpfn-010">tabpfn 0.1.0
</h3>
<p>The new 






<a href="https://tabpfn.tidymodels.org/" target="_blank" rel="noopener">tabpfn</a>
 v0.1.0 package provides an R interface to TabPFN, a pretrained deep learning model for tabular data that delivers strong predictive performance without requiring model training. The package integrates with tidymodels syntax and future updates will add parsnip model types and additional interpretability tools.</p>
<ul>
<li>Learn more in the 






<a href="https://opensource.posit.co/blog/2026-03-31_tabpfn-0-1-0/" target="_blank" rel="noopener">tabpfn 0.1.0</a>
 blog post.</li>
</ul>
<h3 id="nanoparquet-051">nanoparquet 0.5.1
</h3>
<p>






<a href="https://nanoparquet.r-lib.org/" target="_blank" rel="noopener">nanoparquet</a>
 is a small, self-sufficient R package for reading and writing Parquet files. Version 0.5.1 introduces list columns, bit64::integer64 and blob::blob support, writing Parquet to the standard output.</p>
<ul>
<li>Read more in the 






<a href="https://opensource.posit.co/blog/2026-04-30_nanoparquet-0-5-1/" target="_blank" rel="noopener">nanoparquet 0.5.1</a>
 blog post.</li>
</ul>
<h3 id="torch-ecosystem-updates">torch Ecosystem Updates
</h3>
<p>The team has expanded 






<a href="https://torch.mlverse.org/" target="_blank" rel="noopener">torch</a>
 ecosystem support to include cudatoolkit packages, torchvision datasets, and advanced model architectures and transformations for computer vision.</p>
<ul>
<li>Read more in the 






<a href="https://opensource.posit.co/blog/2026-04-30_torch-ecosystem-updates-2026/" target="_blank" rel="noopener">torch Ecosystem Updates</a>
 blog post.</li>
</ul>
<h3 id="roxygen2-800">roxygen2 8.0.0
</h3>
<p>






<a href="https://roxygen2.r-lib.org/" target="_blank" rel="noopener">roxygen2</a>
 uses specially formatted comments in your R code to generate <code>.Rd</code> files. This version offers new support for S7, a raft of improvements to R6 documentation, a more natural way to configure roxygen2 in your DESCRIPTION, the changes to rendered <code>.Rd</code> files you’re most likely to see, and some other minor improvements, and a bunch of new vignettes.</p>
<ul>
<li>Learn more in the 






<a href="https://opensource.posit.co/blog/2026-05-01_roxygen2-8-0-0/" target="_blank" rel="noopener">roxygen2 8.0.0</a>
 blog post.</li>
</ul>
<h3 id="great-docs">Great Docs
</h3>
<p>Last month, we introduced 






<a href="https://posit-dev.github.io/great-docs/" target="_blank" rel="noopener">Great Docs</a>
 for beautiful documentation for Python packages. Author Rich Iannone shares more details in the 






<a href="https://opensource.posit.co/blog/2026-04-15_great-docs-introduction/" target="_blank" rel="noopener">Great Docs introductory blog post</a>
.</p>
<h2 id="learning-and-community">Learning and community
</h2>
<h3 id="posit-website-relaunch">Posit website relaunch
</h3>
<p>The 






<a href="https://posit.co/" target="_blank" rel="noopener">Posit website</a>
 has a new look! Check out the refreshed pages, in particular the wonderful demo gallery with examples of workflows using Posit tools.</p>
<h3 id="showcases-from-the-community">Showcases from the community
</h3>
<p>There are so many community examples to share, here is just a small <code>sample()</code>:</p>









  
  
    
  

  
  
    
  





  


<div class="grid gap-12 items-start mt-12 md:grid-cols-2 ">
  
  
    
    
    
      <div class="prose max-w-none "><p>






<a href="https://rubuky.com/" target="_blank" rel="noopener">Dianyi Yang</a>
, DPhil candidate in Politics at the University of Oxford (DPIR), shares practical guide to structuring reproducible academic research projects using Git, renv, Quarto, and GitHub, from data cleaning to manuscript preparation.</p>
<p>






<a href="https://opensource.posit.co/blog/2026-04-13_reproducible-research-renv-quarto-github/" target="_blank" rel="noopener">Read the blog post 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-05-06_2026-05-glimpse/images/image1.png"
        alt="Screenshot of an RStudio code editor displaying a script titled 3_main_analysis.R. An outline on the left shows sections: INFO, Setup, Read in the processed data, Main analysis, and Output the model summary. The script header indicates it performs linear regression analysis on Brexit data." 
        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-05-06_2026-05-glimpse/images/image3.png"
        alt="Dashboard titled Leigh Syndrome Registry Explorer featuring key metrics and four data visualizations. It shows 440 total enrolled participants and includes a bar chart of participants by region, a histogram for age distribution, and a sex distribution chart." 
        loading="lazy"
      >
    </figure></div></div>
    
  
    
    
    
      <div class="prose max-w-none "><p>






<a href="https://www.linkedin.com/posts/sophiazilber_i-built-a-public-facing-dashboard-for-the-share-7450912100962586624-S3MN/?rcm=ACoAAB0DXA0BRYdwbGNKW2-OfIAa3MsVywURURg" target="_blank" rel="noopener">Sophia Zilber</a>
 shared a public-facing dashboard for the 






<a href="https://www.curemito.org/" target="_blank" rel="noopener">Cure Mito Foundation</a>
 Leigh syndrome patient registry using Shiny for R.</p>
<p>






<a href="https://curemito.shinyapps.io/lsregistry/" target="_blank" rel="noopener">See the dashboard here</a>
.</p>
</div>
    
  
</div>










  
  
    
  

  
  
    
  





  


<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/in/tomgeens/" target="_blank" rel="noopener">Tom Geens</a>
 and his team used Quarto to generate HTML and LaTeX reports on occupational accidents in Belgium.</p>
<p>






<a href="https://info.liantis.be/hubfs/onderzoek/arbeidsongevallen/index.html" target="_blank" rel="noopener">See the report 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-05-06_2026-05-glimpse/images/image6.png"
        alt="Webpage for a report titled &lsquo;Towards a better understanding of occupational accidents in Belgium,&rsquo; published by Liantis on October 29, 2025." 
        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-05-06_2026-05-glimpse/images/image4.png"
        alt="Posit logo and text overlay on a blurred office background: 6X FASTER Underwriting Workflows at Gen Re." 
        loading="lazy"
      >
    </figure></div></div>
    
  
    
    
    
      <div class="prose max-w-none "><p>Gen Re uses Posit Connect to automate their underwriting workflow, ingesting broker submissions every minute and routing them through AI services that extract key information and generate an early risk assessment. What used to take 30 minutes per submission now takes just 5, saving the team roughly 600 hours of cumulative processing time each day.</p>
<p>






<a href="https://posit.co/about/customer-stories/gen-re" target="_blank" rel="noopener">Check out their story</a>
.</p>
</div>
    
  
</div>

<h2 id="whats-next">What’s next?
</h2>
<p>You can join us every Tuesday at the 






<a href="https://pos.it/dslab" target="_blank" rel="noopener">Data Science Lab</a>
 and every Thursday at the 






<a href="https://pos.it/dsh" target="_blank" rel="noopener">Data Science Hangout</a>
!</p>
<ul>
<li>On May 12, Nicola Rennie will live code a TidyTuesday visualization from end-to-end and share the secrets of her craft!</li>
<li>On May 21, our DSH will be a Data Career Panel with Gabriela de Queiroz, Dan Boisvert, and Makarand Malu. Bring your career questions about the field of data, hiring, asking for promotions, and more!</li>
</ul>
<p>I’m a real person, and I would love to hear any feedback on the newsletter! 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-05-06_2026-05-glimpse/thumbnail.jpg" length="68574" type="image/jpeg" />
    </item>
    <item>
      <title>A Plotnine Skill for AI Coding Agents</title>
      <link>https://opensource.posit.co/blog/2026-05-05_plotnine-skill-announcement/</link>
      <pubDate>Tue, 05 May 2026 00:00:00 +0000</pubDate>
      <guid>https://opensource.posit.co/blog/2026-05-05_plotnine-skill-announcement/</guid>
      <dc:creator>Hassan Kibirige</dc:creator><description><![CDATA[<p>We now have a 






<a href="https://github.com/has2k1/plotnine-skill" target="_blank" rel="noopener">plotnine skill</a>
 for AI Agents.
If you write plotnine and use AI coding assistants day-to-day, this is for you.
It targets the 






<a href="https://agentskills.io" target="_blank" rel="noopener">Agent Skills</a>
 standard, so it works across any Agent that supports skills (Claude Code, Codex, and others) and helps an Agent produce consistent, runnable plotnine code.</p>
<h2 id="install">Install
</h2>
<p>Install once:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">uvx skillsmd add has2k1/plotnine-skill</span></span></code></pre></div></div>
<p>or</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">npx skills add has2k1/plotnine-skill</span></span></code></pre></div></div>
<p>With either option, you will be prompted to choose the specific agents you wish to configure.</p>
<p>For versions of <code>plotnine &gt;= 0.15.4</code>, the skill is built-in and you can install it using 






<a href="https://library-skills.io/" target="_blank" rel="noopener">library-skills</a>
.</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl"><span class="c1"># Install into .agents</span>
</span></span><span class="line"><span class="cl">uvx library-skills install -s plotnine
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Install for Claude (.claude)</span>
</span></span><span class="line"><span class="cl">uvx library-skills install --claude -s plotnine</span></span></code></pre></div></div>
<h2 id="try-it">Try it
</h2>
<p>Once installed, simply ask your Agent to use plotnine and create a plot. For example:</p>
<blockquote>
<p>Use plotnine to create a scatter plot of bill_length_mm vs bill_depth_mm from the penguins dataset, colored by species, with a colorblind-safe palette and a smoothed trend line per species.</p>
</blockquote>
<p>Compare the results for the above prompt <em>with</em> skill and <em>without</em> skill.</p>
<div class="panel-tabset">
<ul id="tabset-1" class="panel-tabset-tabby">
<li><a data-tabby-default href="#tabset-1-1">With Skill</a></li>
<li><a href="#tabset-1-2">No Skill</a></li>
</ul>
<div id="tabset-1-1">
<img src="https://opensource.posit.co/blog/2026-05-05_plotnine-skill-announcement/index_files/figure-markdown_strict/penguins-skill-output-1.png" id="penguins-skill" width="768" height="480" />
<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">plotnine</span> <span class="kn">import</span> <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_smooth</span><span class="p">,</span> <span class="n">scale_color_brewer</span><span class="p">,</span> <span class="n">labs</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">plotnine.data</span> <span class="kn">import</span> <span class="n">penguins</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></span><span class="line"><span class="cl">        <span class="n">penguins</span><span class="o">.</span><span class="n">dropna</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">x</span><span class="o">=</span><span class="s2">&#34;bill_length_mm&#34;</span><span class="p">,</span> <span class="n">y</span><span class="o">=</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="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">size</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">alpha</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">geom_smooth</span><span class="p">(</span><span class="n">method</span><span class="o">=</span><span class="s2">&#34;lm&#34;</span><span class="p">,</span> <span class="n">se</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">alpha</span><span class="o">=</span><span class="mf">0.2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">+</span> <span class="n">scale_color_brewer</span><span class="p">(</span><span class="nb">type</span><span class="o">=</span><span class="s2">&#34;qual&#34;</span><span class="p">,</span> <span class="n">palette</span><span class="o">=</span><span class="s2">&#34;Set2&#34;</span><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">x</span><span class="o">=</span><span class="s2">&#34;Bill Length (mm)&#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;Bill Depth (mm)&#34;</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;Penguin Bill Dimensions by Species&#34;</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;Species&#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>
</div>
<div id="tabset-1-2">
<img src="https://opensource.posit.co/blog/2026-05-05_plotnine-skill-announcement/index_files/figure-markdown_strict/penguins-no-skill-output-1.png" id="penguins-no-skill" width="768" height="480" />
<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">plotnine</span> <span class="kn">import</span> <span class="n">aes</span><span class="p">,</span> <span class="n">geom_point</span><span class="p">,</span> <span class="n">geom_smooth</span><span class="p">,</span> <span class="n">ggplot</span><span class="p">,</span> <span class="n">scale_color_brewer</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">plotnine.data</span> <span class="kn">import</span> <span class="n">penguins</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">=</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">penguins</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;bill_length_mm&#34;</span><span class="p">,</span> <span class="n">y</span><span class="o">=</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">geom_point</span><span class="p">(</span><span class="n">alpha</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">geom_smooth</span><span class="p">(</span><span class="n">method</span><span class="o">=</span><span class="s2">&#34;lm&#34;</span><span class="p">,</span> <span class="n">se</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">+</span> <span class="n">scale_color_brewer</span><span class="p">(</span><span class="nb">type</span><span class="o">=</span><span class="s2">&#34;qual&#34;</span><span class="p">,</span> <span class="n">palette</span><span class="o">=</span><span class="s2">&#34;Dark2&#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">p</span><span class="o">.</span><span class="n">save</span><span class="p">(</span><span class="s2">&#34;scatter_penguins.png&#34;</span><span class="p">,</span> <span class="n">width</span><span class="o">=</span><span class="mi">8</span><span class="p">,</span> <span class="n">height</span><span class="o">=</span><span class="mi">6</span><span class="p">,</span> <span class="n">dpi</span><span class="o">=</span><span class="mi">100</span><span class="p">)</span></span></span></code></pre></div></div>
</div>
</div>
<p>Note that even without a skill, the Agent (Claude Code Opus 4.6) generates readable and idiomatic code.
The differences then map directly to some of the aspects that the skill enforces. In this case they are:</p>
<ol>
<li><strong>Accessibility</strong> &mdash; axis labels with units (where possible) and a real title</li>
<li><strong>Data Completeness</strong> &mdash; <code>penguins.dropna()</code> means that the smoother isn&rsquo;t quietly skipping rows.</li>
</ol>
<p>As Agents are prone to doing, without a skill the Agent was presumptuous and generated code to save the plot to a file.
The skill makes that experience tighter with fewer surprises, fewer fixes after the fact.</p>
<h2 id="what-is-next">What is next?
</h2>
<p>We want this skill to rock, hard, and this is only the beginning.
The vibes may seem good but, with intent, we want to build a sense of where the skill reliably helps and where it does not, so that performance and improvements are grounded in more formal evidence rather than vibes.</p>
<p>Your feedback can help. If you try it, tell us how it performs; the prompts it handled poorly, those were it was surprisingly exceptional, and even those where it was good but it can be exceptional.
The show is still going on, and we will be glad to see what you show us at 






<a href="https://github.com/has2k1/plotnine-skill/issues" target="_blank" rel="noopener">the repo</a>
.</p>
]]></description>
      <enclosure url="https://opensource.posit.co/blog/2026-05-05_plotnine-skill-announcement/hero.jpg" length="271116" type="image/jpeg" />
    </item>
    <item>
      <title>roxygen2 8.0.0</title>
      <link>https://opensource.posit.co/blog/2026-05-01_roxygen2-8-0-0/</link>
      <pubDate>Fri, 01 May 2026 00:00:00 +0000</pubDate>
      <guid>https://opensource.posit.co/blog/2026-05-01_roxygen2-8-0-0/</guid>
      <dc:creator>Hadley Wickham</dc:creator><description><![CDATA[<p>I&rsquo;m pleased to announce that 






<a href="https://roxygen2.r-lib.org" target="_blank" rel="noopener">roxygen2 8.0.0</a>
 is now on CRAN.
roxygen2 turns specially formatted comments in your R code into the <code>.Rd</code> files that power R&rsquo;s help system.
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;roxygen2&#34;</span><span class="p">)</span></span></span></code></pre></div></div>
<p>This release has been a long time in the making and includes well over a hundred bug fixes and improvements.
This post covers the highlights: new support for S7, a raft of improvements to R6 documentation, a more natural way to configure roxygen2 in your <code>DESCRIPTION</code>, the changes to rendered <code>.Rd</code> files you&rsquo;re most likely to see, and some other minor improvements, and a bunch of new vignettes.
You can read the full list of changes in the 






<a href="https://github.com/r-lib/roxygen2/releases/tag/v8.0.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">roxygen2</span><span class="p">)</span></span></span></code></pre></div></div>
<h2 id="documenting-s7">Documenting S7
</h2>
<p>The headline feature is initial support for 






<a href="https://rconsortium.github.io/S7/" target="_blank" rel="noopener">S7</a>
.
S7 is a new object-oriented programming system built to be a successor to S3 and S4.
It&rsquo;s been designed and implemented collaboratively by the R Consortium Object-Oriented Programming Working Group, which includes representatives from R-Core, Bioconductor, tidyverse/Posit, and the wider R community.
S7 is still a work in progress, but it is useful today, and adding roxygen2 support makes it easier to use in packages.</p>
<p>roxygen2 now supports documenting S7 generics, classes, and methods:</p>
<ul>
<li><strong>Generics</strong> are documented just like regular functions.</li>
<li><strong>Classes</strong> are documented like regular functions (because S7 constructors are functions), but you can also use <code>@prop</code> to document properties that aren&rsquo;t constructor parameters.
If you document several related classes on the same page, <code>@prop ClassName@prop_name description</code> lets you group properties by class.</li>
<li><strong>Methods</strong> registered with <code>method(generic, class) &lt;- fn</code> are picked up automatically, and roxygen2 generates the right usage and aliases for you.</li>
</ul>
<p>Read <code>vignette(&quot;rd-S7&quot;)</code> for a full rundown, and please let us know if you discover any issues or have suggestions for improvement!</p>
<h2 id="more-ways-to-document-r6">More ways to document R6
</h2>
<p>R6 got the biggest pile of improvements in this release, driven by years (!!)
of accumulated feedback.
The most important change is that you no longer need to document all methods inside the class definition.
If you add methods with <code>$set()</code>, you can now document them 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="n">Counter</span> <span class="o">&lt;-</span> <span class="n">R6</span><span class="o">::</span><span class="nf">R6Class</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">  <span class="s">&#34;Counter&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="n">public</span> <span class="o">=</span> <span class="nf">list</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">count</span> <span class="o">=</span> <span class="m">0</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">#&#39; Increment the counter by `by`.</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&#39; @param by Number to add.</span>
</span></span><span class="line"><span class="cl"><span class="n">Counter</span><span class="o">$</span><span class="nf">set</span><span class="p">(</span><span class="s">&#34;public&#34;</span><span class="p">,</span> <span class="s">&#34;increment&#34;</span><span class="p">,</span> <span class="kr">function</span><span class="p">(</span><span class="n">by</span> <span class="o">=</span> <span class="m">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">self</span><span class="o">$</span><span class="n">count</span> <span class="o">&lt;-</span> <span class="n">self</span><span class="o">$</span><span class="n">count</span> <span class="o">+</span> <span class="n">by</span>
</span></span><span class="line"><span class="cl">  <span class="nf">invisible</span><span class="p">(</span><span class="n">self</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">})</span></span></span></code></pre></div></div>
<p>For more exotic cases where methods get attached through code that roxygen2 can&rsquo;t follow, there&rsquo;s now <code>@R6method Class$method</code>, which lets you document a method from anywhere in your package.</p>
<p>You can also now opt <em>out</em> of documenting pieces of a class.
Writing <code>@noRd</code> above an R6 method excludes it from the docs, and <code>@field name NULL</code> does the same for fields and active bindings.</p>
<p>Finally, inheritance got a significant upgrade.
When a method overrides one from a superclass, it now automatically inherits that parameter documentation &mdash; so common parameters only have to be documented once, on the base class.
The same is true for inherited fields and active bindings.
This should cut down on a lot of duplication if you have a deep class hierarchy,</p>
<h2 id="a-cleaner-home-for-configuration">A cleaner home for configuration
</h2>
<p>roxygen2 has historically been configured through a <code>Roxygen:</code> field in <code>DESCRIPTION</code>, with a separate <code>RoxygenNote</code> field recording the version.
This release introduces a more natural home using the <code>Config/roxygen2/</code> namespace that packages like testthat and lifecycle have adopted:</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"># before</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">Roxygen</span><span class="p">:</span><span class="w"> </span><span class="l">list(markdown = TRUE)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">RoxygenNote</span><span class="p">:</span><span class="w"> </span><span class="m">7.3.2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c"># after</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">Config/roxygen2/markdown</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">Config/roxygen2/version</span><span class="p">:</span><span class="w"> </span><span class="m">8.0.0</span></span></span></code></pre></div></div>
<p>The old fields still work, and roxygen2 will quietly migrate <code>RoxygenNote</code> the next time you run <code>devtools::document()</code>.
Over time, devtools and usethis will switch to the new form by default.</p>
<h2 id="rendering-tweaks">Rendering tweaks
</h2>
<p>When you re-document your package for the first time with roxygen2 8.0.0, you might notice some small differences in the rendered output.
The most commonly encountered changes are:</p>
<ul>
<li>
<p>All generated cross-reference links now go through the same code path and share a single style, e.g. <code>\code{\link[=compose]{compose()}}</code>.</p>
</li>
<li>
<p>Links to external packages now use the topic alias rather than the <code>.Rd</code> filename.
This brings roxygen2 into alignment with current CRAN best practices.</p>
</li>
<li>
<p>Tags that typically expect a single line of input now warn if you spread them across multiple lines.
This catches a common class of silent mistakes, e.g. a <code>@name</code> or <code>@rdname</code> with no content that quietly included the next line of the block.
This will create warnings for some existing legitimate uses, but I think the payoff (eliminating problems that are otherwise very hard to spot) is worth it.</p>
</li>
<li>
<p>People with both <code>&quot;aut&quot;</code> and <code>&quot;cre&quot;</code> roles now appear in both the Authors and Maintainer sections of package documentation.</p>
</li>
</ul>
<h2 id="other-important-changes">Other important changes
</h2>
<p>roxygen2 now requires R 4.1 and no longer depends on purrr, stringr, or stringi.
Those last two are the big win because it means that stringi is now gone from the complete devtools dependency graph, making it easier to install for folks on constrained Linux images.</p>
<p>There&rsquo;s a new helper, <code>needs_roxygenize()</code>, that tells you whether your <code>.Rd</code> files are out of date by comparing modification times against their sources.
It&rsquo;s much cheaper than running <code>roxygenize()</code> just to find out nothing has changed, which makes it a good fit for pre-commit hooks or CI checks.</p>
<p>Finally, parameter inheritance has gained a small but very useful feature: <code>@inheritParams</code> now supports filtering, just like <code>@inheritDotParams</code>.
If you only want to inherit a subset of arguments from another function, you can now list them explicitly:</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">#&#39; @inheritParams other_fn x y</span></span></span></code></pre></div></div>
<p>Or exclude the ones you don&rsquo;t want:</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">#&#39; @inheritParams other_fn -z</span></span></span></code></pre></div></div>
<p>Speaking of <code>@inheritDotParams</code>, it now also works more like <code>@inheritParams</code>: it inherits documented parameters rather than formal arguments.
This may introduce new false positives (replacing the old approach&rsquo;s false negatives), which you can prevent by explicitly listing the argument names to inherit.</p>
<h2 id="new-vignettes">New vignettes
</h2>
<p>roxygen2&rsquo;s own documentation has had a significant tidy-up.
The old <code>vignette(&quot;rd-other&quot;)</code> has been broken into focused vignettes &mdash; <code>vignette(&quot;rd-datasets&quot;)</code>, <code>vignette(&quot;rd-packages&quot;)</code>, <code>vignette(&quot;rd-S3&quot;)</code>, <code>vignette(&quot;rd-S4&quot;)</code>, and <code>vignette(&quot;rd-R6&quot;)</code> &mdash; with the new <code>vignette(&quot;rd-S7&quot;)</code> joining them.
The main <code>vignette(&quot;rd&quot;)</code> has been renamed to <code>vignette(&quot;rd-functions&quot;)</code>, and the &ldquo;getting started&rdquo; content has moved to <code>vignette(&quot;roxygen2&quot;)</code>.</p>
<p><code>vignette(&quot;rd-S3&quot;)</code> has also been rewritten with clearer guidance for documenting S3 generics, classes, and methods, including how to use the new 






<a href="https://doclisting.r-lib.org/" target="_blank" rel="noopener">doclisting</a>
 package to automatically list methods for a generic &mdash; a long-standing pain point for generics with methods in multiple packages.
You can also use doclisting with S4 and S7 generics.</p>
<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/achubaty" target="_blank" rel="noopener">@achubaty</a>
, 






<a href="https://github.com/adithya604" target="_blank" rel="noopener">@adithya604</a>
, 






<a href="https://github.com/akersting" target="_blank" rel="noopener">@akersting</a>
, 






<a href="https://github.com/alandipert" target="_blank" rel="noopener">@alandipert</a>
, 






<a href="https://github.com/alannearme" target="_blank" rel="noopener">@alannearme</a>
, 






<a href="https://github.com/alecw" target="_blank" rel="noopener">@alecw</a>
, 






<a href="https://github.com/alexgenin" target="_blank" rel="noopener">@alexgenin</a>
, 






<a href="https://github.com/AlexisDerumigny" target="_blank" rel="noopener">@AlexisDerumigny</a>
, 






<a href="https://github.com/AliSajid" target="_blank" rel="noopener">@AliSajid</a>
, 






<a href="https://github.com/alisonmosky" target="_blank" rel="noopener">@alisonmosky</a>
, 






<a href="https://github.com/aljabadi" target="_blank" rel="noopener">@aljabadi</a>
, 






<a href="https://github.com/allenzhuaz" target="_blank" rel="noopener">@allenzhuaz</a>
, 






<a href="https://github.com/andrew-schulman" target="_blank" rel="noopener">@andrew-schulman</a>
, 






<a href="https://github.com/andrewmarx" target="_blank" rel="noopener">@andrewmarx</a>
, 






<a href="https://github.com/aphalo" target="_blank" rel="noopener">@aphalo</a>
, 






<a href="https://github.com/apreshill" target="_blank" rel="noopener">@apreshill</a>
, 






<a href="https://github.com/arilamstein" target="_blank" rel="noopener">@arilamstein</a>
, 






<a href="https://github.com/arnaudgallou" target="_blank" rel="noopener">@arnaudgallou</a>
, 






<a href="https://github.com/ashbythorpe" target="_blank" rel="noopener">@ashbythorpe</a>
, 






<a href="https://github.com/ateucher" target="_blank" rel="noopener">@ateucher</a>
, 






<a href="https://github.com/b-niu" target="_blank" rel="noopener">@b-niu</a>
, 






<a href="https://github.com/bahadzie" target="_blank" rel="noopener">@bahadzie</a>
, 






<a href="https://github.com/balthasars" target="_blank" rel="noopener">@balthasars</a>
, 






<a href="https://github.com/BartJanvanRossum" target="_blank" rel="noopener">@BartJanvanRossum</a>
, 






<a href="https://github.com/bastistician" target="_blank" rel="noopener">@bastistician</a>
, 






<a href="https://github.com/batpigandme" target="_blank" rel="noopener">@batpigandme</a>
, 






<a href="https://github.com/beginb" target="_blank" rel="noopener">@beginb</a>
, 






<a href="https://github.com/BenEngbers" target="_blank" rel="noopener">@BenEngbers</a>
, 






<a href="https://github.com/BenWiseman" target="_blank" rel="noopener">@BenWiseman</a>
, 






<a href="https://github.com/bgctw" target="_blank" rel="noopener">@bgctw</a>
, 






<a href="https://github.com/BGWKlein" target="_blank" rel="noopener">@BGWKlein</a>
, 






<a href="https://github.com/bhagwataditya" target="_blank" rel="noopener">@bhagwataditya</a>
, 






<a href="https://github.com/billdenney" target="_blank" rel="noopener">@billdenney</a>
, 






<a href="https://github.com/Bisaloo" target="_blank" rel="noopener">@Bisaloo</a>
, 






<a href="https://github.com/bluewomble" target="_blank" rel="noopener">@bluewomble</a>
, 






<a href="https://github.com/bobjansen" target="_blank" rel="noopener">@bobjansen</a>
, 






<a href="https://github.com/boshek" target="_blank" rel="noopener">@boshek</a>
, 






<a href="https://github.com/brendanf" target="_blank" rel="noopener">@brendanf</a>
, 






<a href="https://github.com/brodieG" target="_blank" rel="noopener">@brodieG</a>
, 






<a href="https://github.com/BroVic" target="_blank" rel="noopener">@BroVic</a>
, 






<a href="https://github.com/brpetrucci" target="_blank" rel="noopener">@brpetrucci</a>
, 






<a href="https://github.com/brry" target="_blank" rel="noopener">@brry</a>
, 






<a href="https://github.com/bryanhanson" target="_blank" rel="noopener">@bryanhanson</a>
, 






<a href="https://github.com/bwiernik" target="_blank" rel="noopener">@bwiernik</a>
, 






<a href="https://github.com/cbielow" target="_blank" rel="noopener">@cbielow</a>
, 






<a href="https://github.com/cboettig" target="_blank" rel="noopener">@cboettig</a>
, 






<a href="https://github.com/cderv" target="_blank" rel="noopener">@cderv</a>
, 






<a href="https://github.com/CGMossa" target="_blank" rel="noopener">@CGMossa</a>
, 






<a href="https://github.com/chlebowa" target="_blank" rel="noopener">@chlebowa</a>
, 






<a href="https://github.com/chrarnold" target="_blank" rel="noopener">@chrarnold</a>
, 






<a href="https://github.com/ChristopherEeles" target="_blank" rel="noopener">@ChristopherEeles</a>
, 






<a href="https://github.com/chrk623" target="_blank" rel="noopener">@chrk623</a>
, 






<a href="https://github.com/chuxinyuan" target="_blank" rel="noopener">@chuxinyuan</a>
, 






<a href="https://github.com/cjyetman" target="_blank" rel="noopener">@cjyetman</a>
, 






<a href="https://github.com/coatless" target="_blank" rel="noopener">@coatless</a>
, 






<a href="https://github.com/ColinFay" target="_blank" rel="noopener">@ColinFay</a>
, 






<a href="https://github.com/courtiol" target="_blank" rel="noopener">@courtiol</a>
, 






<a href="https://github.com/cthombor" target="_blank" rel="noopener">@cthombor</a>
, 






<a href="https://github.com/d-morrison" target="_blank" rel="noopener">@d-morrison</a>
, 






<a href="https://github.com/d-sci" target="_blank" rel="noopener">@d-sci</a>
, 






<a href="https://github.com/daattali" target="_blank" rel="noopener">@daattali</a>
, 






<a href="https://github.com/DanChaltiel" target="_blank" rel="noopener">@DanChaltiel</a>
, 






<a href="https://github.com/DanielHermosilla" target="_blank" rel="noopener">@DanielHermosilla</a>
, 






<a href="https://github.com/danielvartan" target="_blank" rel="noopener">@danielvartan</a>
, 






<a href="https://github.com/DarioS" target="_blank" rel="noopener">@DarioS</a>
, 






<a href="https://github.com/davidrubinger" target="_blank" rel="noopener">@davidrubinger</a>
, 






<a href="https://github.com/DavisVaughan" target="_blank" rel="noopener">@DavisVaughan</a>
, 






<a href="https://github.com/daynefiler" target="_blank" rel="noopener">@daynefiler</a>
, 






<a href="https://github.com/dfrankow" target="_blank" rel="noopener">@dfrankow</a>
, 






<a href="https://github.com/dgkf" target="_blank" rel="noopener">@dgkf</a>
, 






<a href="https://github.com/dieghernan" target="_blank" rel="noopener">@dieghernan</a>
, 






<a href="https://github.com/dipterix" target="_blank" rel="noopener">@dipterix</a>
, 






<a href="https://github.com/dmurdoch" target="_blank" rel="noopener">@dmurdoch</a>
, 






<a href="https://github.com/dpprdan" target="_blank" rel="noopener">@dpprdan</a>
, 






<a href="https://github.com/drag05" target="_blank" rel="noopener">@drag05</a>
, 






<a href="https://github.com/dragosmg" target="_blank" rel="noopener">@dragosmg</a>
, 






<a href="https://github.com/dsweber2" target="_blank" rel="noopener">@dsweber2</a>
, 






<a href="https://github.com/dvg-p4" target="_blank" rel="noopener">@dvg-p4</a>
, 






<a href="https://github.com/dwachsmuth" target="_blank" rel="noopener">@dwachsmuth</a>
, 






<a href="https://github.com/eddelbuettel" target="_blank" rel="noopener">@eddelbuettel</a>
, 






<a href="https://github.com/eitsupi" target="_blank" rel="noopener">@eitsupi</a>
, 






<a href="https://github.com/ejosymart" target="_blank" rel="noopener">@ejosymart</a>
, 






<a href="https://github.com/elcortegano" target="_blank" rel="noopener">@elcortegano</a>
, 






<a href="https://github.com/ElsLommelen" target="_blank" rel="noopener">@ElsLommelen</a>
, 






<a href="https://github.com/espinielli" target="_blank" rel="noopener">@espinielli</a>
, 






<a href="https://github.com/FelixErnst" target="_blank" rel="noopener">@FelixErnst</a>
, 






<a href="https://github.com/florisvdh" target="_blank" rel="noopener">@florisvdh</a>
, 






<a href="https://github.com/flrd" target="_blank" rel="noopener">@flrd</a>
, 






<a href="https://github.com/gaborcsardi" target="_blank" rel="noopener">@gaborcsardi</a>
, 






<a href="https://github.com/GABurns" target="_blank" rel="noopener">@GABurns</a>
, 






<a href="https://github.com/galachad" target="_blank" rel="noopener">@galachad</a>
, 






<a href="https://github.com/gavinsimpson" target="_blank" rel="noopener">@gavinsimpson</a>
, 






<a href="https://github.com/genomaths" target="_blank" rel="noopener">@genomaths</a>
, 






<a href="https://github.com/ggrothendieck" target="_blank" rel="noopener">@ggrothendieck</a>
, 






<a href="https://github.com/ghost" target="_blank" rel="noopener">@ghost</a>
, 






<a href="https://github.com/goldingn" target="_blank" rel="noopener">@goldingn</a>
, 






<a href="https://github.com/gowerc" target="_blank" rel="noopener">@gowerc</a>
, 






<a href="https://github.com/gregorgorjanc" target="_blank" rel="noopener">@gregorgorjanc</a>
, 






<a href="https://github.com/gustavdelius" target="_blank" rel="noopener">@gustavdelius</a>
, 






<a href="https://github.com/gwd666" target="_blank" rel="noopener">@gwd666</a>
, 






<a href="https://github.com/hadley" target="_blank" rel="noopener">@hadley</a>
, 






<a href="https://github.com/harrelfe" target="_blank" rel="noopener">@harrelfe</a>
, 






<a href="https://github.com/hdarjus" target="_blank" rel="noopener">@hdarjus</a>
, 






<a href="https://github.com/HenningLorenzen-ext-bayer" target="_blank" rel="noopener">@HenningLorenzen-ext-bayer</a>
, 






<a href="https://github.com/HenrikBengtsson" target="_blank" rel="noopener">@HenrikBengtsson</a>
, 






<a href="https://github.com/hongooi73" target="_blank" rel="noopener">@hongooi73</a>
, 






<a href="https://github.com/hughjonesd" target="_blank" rel="noopener">@hughjonesd</a>
, 






<a href="https://github.com/iferres" target="_blank" rel="noopener">@iferres</a>
, 






<a href="https://github.com/IndrajeetPatil" target="_blank" rel="noopener">@IndrajeetPatil</a>
, 






<a href="https://github.com/J-Moravec" target="_blank" rel="noopener">@J-Moravec</a>
, 






<a href="https://github.com/jakubnowicki" target="_blank" rel="noopener">@jakubnowicki</a>
, 






<a href="https://github.com/jameslamb" target="_blank" rel="noopener">@jameslamb</a>
, 






<a href="https://github.com/jan-abel-inwt" target="_blank" rel="noopener">@jan-abel-inwt</a>
, 






<a href="https://github.com/JanaJarecki" target="_blank" rel="noopener">@JanaJarecki</a>
, 






<a href="https://github.com/JanMarvin" target="_blank" rel="noopener">@JanMarvin</a>
, 






<a href="https://github.com/JantekM" target="_blank" rel="noopener">@JantekM</a>
, 






<a href="https://github.com/jcubic" target="_blank" rel="noopener">@jcubic</a>
, 






<a href="https://github.com/JDenn0514" target="_blank" rel="noopener">@JDenn0514</a>
, 






<a href="https://github.com/jdprimus" target="_blank" rel="noopener">@jdprimus</a>
, 






<a href="https://github.com/jeffcraggy" target="_blank" rel="noopener">@jeffcraggy</a>
, 






<a href="https://github.com/jennybc" target="_blank" rel="noopener">@jennybc</a>
, 






<a href="https://github.com/jensmassberg" target="_blank" rel="noopener">@jensmassberg</a>
, 






<a href="https://github.com/jeroen" target="_blank" rel="noopener">@jeroen</a>
, 






<a href="https://github.com/jeroenjanssens" target="_blank" rel="noopener">@jeroenjanssens</a>
, 






<a href="https://github.com/JesseAlderliesten" target="_blank" rel="noopener">@JesseAlderliesten</a>
, 






<a href="https://github.com/jgellar" target="_blank" rel="noopener">@jgellar</a>
, 






<a href="https://github.com/jgutman" target="_blank" rel="noopener">@jgutman</a>
, 






<a href="https://github.com/JiaxiangBU" target="_blank" rel="noopener">@JiaxiangBU</a>
, 






<a href="https://github.com/Jiefei-Wang" target="_blank" rel="noopener">@Jiefei-Wang</a>
, 






<a href="https://github.com/jimhester" target="_blank" rel="noopener">@jimhester</a>
, 






<a href="https://github.com/jmbarbone" target="_blank" rel="noopener">@jmbarbone</a>
, 






<a href="https://github.com/jmpanfil" target="_blank" rel="noopener">@jmpanfil</a>
, 






<a href="https://github.com/johanneswerner" target="_blank" rel="noopener">@johanneswerner</a>
, 






<a href="https://github.com/johnbaums" target="_blank" rel="noopener">@johnbaums</a>
, 






<a href="https://github.com/JohnCoene" target="_blank" rel="noopener">@JohnCoene</a>
, 






<a href="https://github.com/JonathanUrbach" target="_blank" rel="noopener">@JonathanUrbach</a>
, 






<a href="https://github.com/jonkeane" target="_blank" rel="noopener">@jonkeane</a>
, 






<a href="https://github.com/jonocarroll" target="_blank" rel="noopener">@jonocarroll</a>
, 






<a href="https://github.com/jonthegeek" target="_blank" rel="noopener">@jonthegeek</a>
, 






<a href="https://github.com/JosiahParry" target="_blank" rel="noopener">@JosiahParry</a>
, 






<a href="https://github.com/jranke" target="_blank" rel="noopener">@jranke</a>
, 






<a href="https://github.com/JulieBlasquiz" target="_blank" rel="noopener">@JulieBlasquiz</a>
, 






<a href="https://github.com/jwijffels" target="_blank" rel="noopener">@jwijffels</a>
, 






<a href="https://github.com/kamapu" target="_blank" rel="noopener">@kamapu</a>
, 






<a href="https://github.com/karchjd" target="_blank" rel="noopener">@karchjd</a>
, 






<a href="https://github.com/karoliskoncevicius" target="_blank" rel="noopener">@karoliskoncevicius</a>
, 






<a href="https://github.com/kathi-munk" target="_blank" rel="noopener">@kathi-munk</a>
, 






<a href="https://github.com/kellijohnson-NOAA" target="_blank" rel="noopener">@kellijohnson-NOAA</a>
, 






<a href="https://github.com/kevinushey" target="_blank" rel="noopener">@kevinushey</a>
, 






<a href="https://github.com/kingaa" target="_blank" rel="noopener">@kingaa</a>
, 






<a href="https://github.com/klmr" target="_blank" rel="noopener">@klmr</a>
, 






<a href="https://github.com/Klorator" target="_blank" rel="noopener">@Klorator</a>
, 






<a href="https://github.com/kongdd" target="_blank" rel="noopener">@kongdd</a>
, 






<a href="https://github.com/kortschak" target="_blank" rel="noopener">@kortschak</a>
, 






<a href="https://github.com/kostrzewa" target="_blank" rel="noopener">@kostrzewa</a>
, 






<a href="https://github.com/kpagacz" target="_blank" rel="noopener">@kpagacz</a>
, 






<a href="https://github.com/krivit" target="_blank" rel="noopener">@krivit</a>
, 






<a href="https://github.com/krlmlr" target="_blank" rel="noopener">@krlmlr</a>
, 






<a href="https://github.com/kurt-o-sys" target="_blank" rel="noopener">@kurt-o-sys</a>
, 






<a href="https://github.com/kylebutts" target="_blank" rel="noopener">@kylebutts</a>
, 






<a href="https://github.com/lgatto" target="_blank" rel="noopener">@lgatto</a>
, 






<a href="https://github.com/lindeloev" target="_blank" rel="noopener">@lindeloev</a>
, 






<a href="https://github.com/LiNk-NY" target="_blank" rel="noopener">@LiNk-NY</a>
, 






<a href="https://github.com/lionel-" target="_blank" rel="noopener">@lionel-</a>
, 






<a href="https://github.com/llrs" target="_blank" rel="noopener">@llrs</a>
, 






<a href="https://github.com/llrs-roche" target="_blank" rel="noopener">@llrs-roche</a>
, 






<a href="https://github.com/lorenzwalthert" target="_blank" rel="noopener">@lorenzwalthert</a>
, 






<a href="https://github.com/LouisLeNezet" target="_blank" rel="noopener">@LouisLeNezet</a>
, 






<a href="https://github.com/LukasWallrich" target="_blank" rel="noopener">@LukasWallrich</a>
, 






<a href="https://github.com/lukasz-bednarz-reddeersystems" target="_blank" rel="noopener">@lukasz-bednarz-reddeersystems</a>
, 






<a href="https://github.com/m-muecke" target="_blank" rel="noopener">@m-muecke</a>
, 






<a href="https://github.com/maelle" target="_blank" rel="noopener">@maelle</a>
, 






<a href="https://github.com/malcolmbarrett" target="_blank" rel="noopener">@malcolmbarrett</a>
, 






<a href="https://github.com/mamueller" target="_blank" rel="noopener">@mamueller</a>
, 






<a href="https://github.com/math-mcshane" target="_blank" rel="noopener">@math-mcshane</a>
, 






<a href="https://github.com/maxheld83" target="_blank" rel="noopener">@maxheld83</a>
, 






<a href="https://github.com/MaximilianPi" target="_blank" rel="noopener">@MaximilianPi</a>
, 






<a href="https://github.com/mbojan" target="_blank" rel="noopener">@mbojan</a>
, 






<a href="https://github.com/mccarthy-m-g" target="_blank" rel="noopener">@mccarthy-m-g</a>
, 






<a href="https://github.com/mccroweyclinton-EPA" target="_blank" rel="noopener">@mccroweyclinton-EPA</a>
, 






<a href="https://github.com/mcol" target="_blank" rel="noopener">@mcol</a>
, 






<a href="https://github.com/mcsage" target="_blank" rel="noopener">@mcsage</a>
, 






<a href="https://github.com/MichaelChirico" target="_blank" rel="noopener">@MichaelChirico</a>
, 






<a href="https://github.com/michaelquinn32" target="_blank" rel="noopener">@michaelquinn32</a>
, 






<a href="https://github.com/mikemahoney218" target="_blank" rel="noopener">@mikemahoney218</a>
, 






<a href="https://github.com/mikkmart" target="_blank" rel="noopener">@mikkmart</a>
, 






<a href="https://github.com/mikmart" target="_blank" rel="noopener">@mikmart</a>
, 






<a href="https://github.com/MilesMcBain" target="_blank" rel="noopener">@MilesMcBain</a>
, 






<a href="https://github.com/mine-cetinkaya-rundel" target="_blank" rel="noopener">@mine-cetinkaya-rundel</a>
, 






<a href="https://github.com/MislavSag" target="_blank" rel="noopener">@MislavSag</a>
, 






<a href="https://github.com/mjsteinbaugh" target="_blank" rel="noopener">@mjsteinbaugh</a>
, 






<a href="https://github.com/mkoohafkan" target="_blank" rel="noopener">@mkoohafkan</a>
, 






<a href="https://github.com/MLopez-Ibanez" target="_blank" rel="noopener">@MLopez-Ibanez</a>
, 






<a href="https://github.com/mnazarov" target="_blank" rel="noopener">@mnazarov</a>
, 






<a href="https://github.com/mnneely" target="_blank" rel="noopener">@mnneely</a>
, 






<a href="https://github.com/monkeywithacupcake" target="_blank" rel="noopener">@monkeywithacupcake</a>
, 






<a href="https://github.com/moodymudskipper" target="_blank" rel="noopener">@moodymudskipper</a>
, 






<a href="https://github.com/mpadge" target="_blank" rel="noopener">@mpadge</a>
, 






<a href="https://github.com/mrchypark" target="_blank" rel="noopener">@mrchypark</a>
, 






<a href="https://github.com/ms609" target="_blank" rel="noopener">@ms609</a>
, 






<a href="https://github.com/msaltieri" target="_blank" rel="noopener">@msaltieri</a>
, 






<a href="https://github.com/msberends" target="_blank" rel="noopener">@msberends</a>
, 






<a href="https://github.com/mschilli87" target="_blank" rel="noopener">@mschilli87</a>
, 






<a href="https://github.com/multimeric" target="_blank" rel="noopener">@multimeric</a>
, 






<a href="https://github.com/muschellij2" target="_blank" rel="noopener">@muschellij2</a>
, 






<a href="https://github.com/musvaage" target="_blank" rel="noopener">@musvaage</a>
, 






<a href="https://github.com/naikymen" target="_blank" rel="noopener">@naikymen</a>
, 






<a href="https://github.com/nathaneastwood" target="_blank" rel="noopener">@nathaneastwood</a>
, 






<a href="https://github.com/nbenn" target="_blank" rel="noopener">@nbenn</a>
, 






<a href="https://github.com/nealrichardson" target="_blank" rel="noopener">@nealrichardson</a>
, 






<a href="https://github.com/Nelson-Gon" target="_blank" rel="noopener">@Nelson-Gon</a>
, 






<a href="https://github.com/neshvig10" target="_blank" rel="noopener">@neshvig10</a>
, 






<a href="https://github.com/netique" target="_blank" rel="noopener">@netique</a>
, 






<a href="https://github.com/ngreifer" target="_blank" rel="noopener">@ngreifer</a>
, 






<a href="https://github.com/nick-robo" target="_blank" rel="noopener">@nick-robo</a>
, 






<a href="https://github.com/NikNakk" target="_blank" rel="noopener">@NikNakk</a>
, 






<a href="https://github.com/nlneas1" target="_blank" rel="noopener">@nlneas1</a>
, 






<a href="https://github.com/nouvrita" target="_blank" rel="noopener">@nouvrita</a>
, 






<a href="https://github.com/nr0cinu" target="_blank" rel="noopener">@nr0cinu</a>
, 






<a href="https://github.com/nteetor" target="_blank" rel="noopener">@nteetor</a>
, 






<a href="https://github.com/ntguardian" target="_blank" rel="noopener">@ntguardian</a>
, 






<a href="https://github.com/oharar" target="_blank" rel="noopener">@oharar</a>
, 






<a href="https://github.com/okhoma" target="_blank" rel="noopener">@okhoma</a>
, 






<a href="https://github.com/olivroy" target="_blank" rel="noopener">@olivroy</a>
, 






<a href="https://github.com/orgadish" target="_blank" rel="noopener">@orgadish</a>
, 






<a href="https://github.com/p-carter" target="_blank" rel="noopener">@p-carter</a>
, 






<a href="https://github.com/p00ya" target="_blank" rel="noopener">@p00ya</a>
, 






<a href="https://github.com/pakjiddat" target="_blank" rel="noopener">@pakjiddat</a>
, 






<a href="https://github.com/pat-s" target="_blank" rel="noopener">@pat-s</a>
, 






<a href="https://github.com/PauloJhonny" target="_blank" rel="noopener">@PauloJhonny</a>
, 






<a href="https://github.com/PavelBal" target="_blank" rel="noopener">@PavelBal</a>
, 






<a href="https://github.com/pbreheny" target="_blank" rel="noopener">@pbreheny</a>
, 






<a href="https://github.com/phargarten2" target="_blank" rel="noopener">@phargarten2</a>
, 






<a href="https://github.com/pnacht" target="_blank" rel="noopener">@pnacht</a>
, 






<a href="https://github.com/pvanlaake" target="_blank" rel="noopener">@pvanlaake</a>
, 






<a href="https://github.com/ralmond" target="_blank" rel="noopener">@ralmond</a>
, 






<a href="https://github.com/ramiromagno" target="_blank" rel="noopener">@ramiromagno</a>
, 






<a href="https://github.com/RaphaelS1" target="_blank" rel="noopener">@RaphaelS1</a>
, 






<a href="https://github.com/retostauffer" target="_blank" rel="noopener">@retostauffer</a>
, 






<a href="https://github.com/RiboRings" target="_blank" rel="noopener">@RiboRings</a>
, 






<a href="https://github.com/richelbilderbeek" target="_blank" rel="noopener">@richelbilderbeek</a>
, 






<a href="https://github.com/rjake" target="_blank" rel="noopener">@rjake</a>
, 






<a href="https://github.com/RMHogervorst" target="_blank" rel="noopener">@RMHogervorst</a>
, 






<a href="https://github.com/robchallen" target="_blank" rel="noopener">@robchallen</a>
, 






<a href="https://github.com/Robinlovelace" target="_blank" rel="noopener">@Robinlovelace</a>
, 






<a href="https://github.com/romainfrancois" target="_blank" rel="noopener">@romainfrancois</a>
, 






<a href="https://github.com/rorynolan" target="_blank" rel="noopener">@rorynolan</a>
, 






<a href="https://github.com/rossellhayes" target="_blank" rel="noopener">@rossellhayes</a>
, 






<a href="https://github.com/rsbivand" target="_blank" rel="noopener">@rsbivand</a>
, 






<a href="https://github.com/russHyde" target="_blank" rel="noopener">@russHyde</a>
, 






<a href="https://github.com/rvernica" target="_blank" rel="noopener">@rvernica</a>
, 






<a href="https://github.com/s-fleck" target="_blank" rel="noopener">@s-fleck</a>
, 






<a href="https://github.com/saipenikalapati" target="_blank" rel="noopener">@saipenikalapati</a>
, 






<a href="https://github.com/Salatbesteck" target="_blank" rel="noopener">@Salatbesteck</a>
, 






<a href="https://github.com/salim-b" target="_blank" rel="noopener">@salim-b</a>
, 






<a href="https://github.com/sbgraves237" target="_blank" rel="noopener">@sbgraves237</a>
, 






<a href="https://github.com/sboehringer" target="_blank" rel="noopener">@sboehringer</a>
, 






<a href="https://github.com/schloerke" target="_blank" rel="noopener">@schloerke</a>
, 






<a href="https://github.com/schradj" target="_blank" rel="noopener">@schradj</a>
, 






<a href="https://github.com/sckott" target="_blank" rel="noopener">@sckott</a>
, 






<a href="https://github.com/sebffischer" target="_blank" rel="noopener">@sebffischer</a>
, 






<a href="https://github.com/setgree" target="_blank" rel="noopener">@setgree</a>
, 






<a href="https://github.com/ShixiangWang" target="_blank" rel="noopener">@ShixiangWang</a>
, 






<a href="https://github.com/simonpcouch" target="_blank" rel="noopener">@simonpcouch</a>
, 






<a href="https://github.com/simonsays1980" target="_blank" rel="noopener">@simonsays1980</a>
, 






<a href="https://github.com/simpar1471" target="_blank" rel="noopener">@simpar1471</a>
, 






<a href="https://github.com/slager" target="_blank" rel="noopener">@slager</a>
, 






<a href="https://github.com/smilberg" target="_blank" rel="noopener">@smilberg</a>
, 






<a href="https://github.com/stefanfritsch" target="_blank" rel="noopener">@stefanfritsch</a>
, 






<a href="https://github.com/stefanoborini" target="_blank" rel="noopener">@stefanoborini</a>
, 






<a href="https://github.com/stellathecat" target="_blank" rel="noopener">@stellathecat</a>
, 






<a href="https://github.com/stemangiola" target="_blank" rel="noopener">@stemangiola</a>
, 






<a href="https://github.com/stla" target="_blank" rel="noopener">@stla</a>
, 






<a href="https://github.com/strazto" target="_blank" rel="noopener">@strazto</a>
, 






<a href="https://github.com/strboul" target="_blank" rel="noopener">@strboul</a>
, 






<a href="https://github.com/sven-stodtmann" target="_blank" rel="noopener">@sven-stodtmann</a>
, 






<a href="https://github.com/swnydick" target="_blank" rel="noopener">@swnydick</a>
, 






<a href="https://github.com/t-kalinowski" target="_blank" rel="noopener">@t-kalinowski</a>
, 






<a href="https://github.com/TanguyBarthelemy" target="_blank" rel="noopener">@TanguyBarthelemy</a>
, 






<a href="https://github.com/tappek" target="_blank" rel="noopener">@tappek</a>
, 






<a href="https://github.com/tau31" target="_blank" rel="noopener">@tau31</a>
, 






<a href="https://github.com/tdhock" target="_blank" rel="noopener">@tdhock</a>
, 






<a href="https://github.com/ThierryO" target="_blank" rel="noopener">@ThierryO</a>
, 






<a href="https://github.com/tjebo" target="_blank" rel="noopener">@tjebo</a>
, 






<a href="https://github.com/TomKellyGenetics" target="_blank" rel="noopener">@TomKellyGenetics</a>
, 






<a href="https://github.com/tommarshall2" target="_blank" rel="noopener">@tommarshall2</a>
, 






<a href="https://github.com/trusch139" target="_blank" rel="noopener">@trusch139</a>
, 






<a href="https://github.com/turgeonmaxime" target="_blank" rel="noopener">@turgeonmaxime</a>
, 






<a href="https://github.com/tzakharko" target="_blank" rel="noopener">@tzakharko</a>
, 






<a href="https://github.com/uhkeller" target="_blank" rel="noopener">@uhkeller</a>
, 






<a href="https://github.com/unDocUMeantIt" target="_blank" rel="noopener">@unDocUMeantIt</a>
, 






<a href="https://github.com/vertesy" target="_blank" rel="noopener">@vertesy</a>
, 






<a href="https://github.com/VPetukhov" target="_blank" rel="noopener">@VPetukhov</a>
, 






<a href="https://github.com/wch" target="_blank" rel="noopener">@wch</a>
, 






<a href="https://github.com/wibeasley" target="_blank" rel="noopener">@wibeasley</a>
, 






<a href="https://github.com/wilcoxa" target="_blank" rel="noopener">@wilcoxa</a>
, 






<a href="https://github.com/wurli" target="_blank" rel="noopener">@wurli</a>
, 






<a href="https://github.com/wviechtb" target="_blank" rel="noopener">@wviechtb</a>
, 






<a href="https://github.com/yogat3ch" target="_blank" rel="noopener">@yogat3ch</a>
, 






<a href="https://github.com/Yunuuuu" target="_blank" rel="noopener">@Yunuuuu</a>
, 






<a href="https://github.com/yutannihilation" target="_blank" rel="noopener">@yutannihilation</a>
, 






<a href="https://github.com/zachary-foster" target="_blank" rel="noopener">@zachary-foster</a>
, 






<a href="https://github.com/zeehio" target="_blank" rel="noopener">@zeehio</a>
, 






<a href="https://github.com/zettlchen" target="_blank" rel="noopener">@zettlchen</a>
, and 






<a href="https://github.com/zkamvar" target="_blank" rel="noopener">@zkamvar</a>
.</p>
]]></description>
      <enclosure url="https://opensource.posit.co/blog/2026-05-01_roxygen2-8-0-0/balloons.jpg" length="267333" type="image/jpeg" />
    </item>
    <item>
      <title>nanoparquet 0.5.1</title>
      <link>https://opensource.posit.co/blog/2026-04-30_nanoparquet-0-5-1/</link>
      <pubDate>Thu, 30 Apr 2026 00:00:00 +0000</pubDate>
      <guid>https://opensource.posit.co/blog/2026-04-30_nanoparquet-0-5-1/</guid>
      <dc:creator>Gábor Csárdi</dc:creator><description><![CDATA[<!--
TODO:
- [x] Add image (1920×1080 PNG or JPG) and image-alt
- [x] Trim topics, software, and languages to only what applies
- [ ] Open a PR against main for a Netlify preview
-->
<p>We&rsquo;re very chuffed to announce the release of







<a href="https://nanoparquet.r-lib.org/" target="_blank" rel="noopener">nanoparquet</a>
 0.5.1 (and 0.5.0).
nanoparquet is a small, self-sufficient R package for reading and
writing Parquet files.</p>
<p>You can install it from CRAN 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;nanoparquet&#34;</span><span class="p">)</span></span></span></code></pre></div></div>
<p>This blog post will go over some of the improvements in nanoparquet 0.5.0
and 0.5.1.</p>
<p>You can see a full list of changes in the release notes







<a href="https://github.com/r-lib/nanoparquet/releases/tag/v0.5.0" target="_blank" rel="noopener">here</a>
 and







<a href="https://github.com/r-lib/nanoparquet/releases/tag/v0.5.1" target="_blank" rel="noopener">here</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">nanoparquet</span><span class="p">)</span></span></span></code></pre></div></div>
<h2 id="list-columns">List columns
</h2>
<p>Parquet has a <code>LIST</code> type for columns whose values are variable-length
sequences of scalars. nanoparquet 0.5.0 adds support for reading and
writing such columns.</p>
<blockquote>
<p><strong>Note:</strong> for now nanoparquet supports one level of nesting: each
element of a list column must be an atomic vector of a single type,
not a list of lists. All elements in a column must have the same scalar
type.</p>
</blockquote>
<p>To write a list column, put a regular R list into your data frame.
Each element must be an atomic vector (integer, double, or character),
<code>NULL</code> for a missing list, or an empty vector for an empty list.
<code>NA</code> values inside an element vector encode missing elements.</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">df</span> <span class="o">&lt;-</span> <span class="nf">data.frame</span><span class="p">(</span><span class="n">id</span> <span class="o">=</span> <span class="m">1</span><span class="o">:</span><span class="m">4</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">df</span><span class="o">$</span><span class="n">scores</span> <span class="o">&lt;-</span> <span class="nf">list</span><span class="p">(</span><span class="nf">c</span><span class="p">(</span><span class="m">80L</span><span class="p">,</span> <span class="m">95L</span><span class="p">,</span> <span class="m">70L</span><span class="p">),</span> <span class="nf">c</span><span class="p">(</span><span class="m">100L</span><span class="p">),</span> <span class="kc">NULL</span><span class="p">,</span> <span class="nf">integer</span><span class="p">(</span><span class="m">0</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="nf">write_parquet</span><span class="p">(</span><span class="n">df</span><span class="p">,</span> <span class="n">tmp</span> <span class="o">&lt;-</span> <span class="nf">tempfile</span><span class="p">(</span><span class="n">fileext</span> <span class="o">=</span> <span class="s">&#34;.parquet&#34;</span><span class="p">))</span></span></span></code></pre></div></div>
<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">read_parquet_schema</span><span class="p">(</span><span class="n">tmp</span><span class="p">)</span></span></span></code></pre></div></div>
<pre><code># A data frame: 5 × 14
  file_name  r_col name  r_type type  type_length repetition_type converted_type
  &lt;chr&gt;      &lt;int&gt; &lt;chr&gt; &lt;chr&gt;  &lt;chr&gt;       &lt;int&gt; &lt;chr&gt;           &lt;chr&gt;         
1 /var/fold…    NA sche… &lt;NA&gt;   &lt;NA&gt;           NA &lt;NA&gt;            &lt;NA&gt;          
2 /var/fold…     1 id    integ… INT32          NA REQUIRED        INT_32        
3 /var/fold…     2 scor… list(… &lt;NA&gt;           NA OPTIONAL        LIST          
4 /var/fold…     2 list  &lt;NA&gt;   &lt;NA&gt;           NA REPEATED        &lt;NA&gt;          
5 /var/fold…     2 elem… &lt;NA&gt;   INT32          NA OPTIONAL        INT_32        
# ℹ 6 more variables: logical_type &lt;I&lt;list&gt;&gt;, num_children &lt;int&gt;, scale &lt;int&gt;,
#   precision &lt;int&gt;, field_id &lt;int&gt;, children &lt;list&gt;
</code></pre>
<p><code>read_parquet()</code> reads <code>LIST</code> columns back as R list columns:</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">as.data.frame</span><span class="p">(</span><span class="nf">read_parquet</span><span class="p">(</span><span class="n">tmp</span><span class="p">))</span></span></span></code></pre></div></div>
<pre><code>  id     scores
1  1 80, 95, 70
2  2        100
3  3       NULL
4  4           
</code></pre>
<p><code>infer_parquet_schema()</code> shows how nanoparquet maps each column to a Parquet
type. For list columns, the <code>r_type</code> shows e.g. <code>list(integer)</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="nf">infer_parquet_schema</span><span class="p">(</span><span class="n">df</span><span class="p">)</span><span class="n">[2</span><span class="o">:</span><span class="m">7</span><span class="n">]</span></span></span></code></pre></div></div>
<pre><code># A data frame: 4 × 6
  r_col name    r_type        type  type_length repetition_type
  &lt;int&gt; &lt;chr&gt;   &lt;chr&gt;         &lt;chr&gt;       &lt;int&gt; &lt;chr&gt;          
1     1 id      integer       INT32          NA REQUIRED       
2     2 scores  list(integer) &lt;NA&gt;           NA OPTIONAL       
3     2 list    &lt;NA&gt;          &lt;NA&gt;           NA REPEATED       
4     2 element &lt;NA&gt;          INT32          NA OPTIONAL       
</code></pre>
<p>A <code>LIST</code> column occupies three rows in the schema: the outer list node,
a repeated group node, and the leaf element node.</p>
<p>When you need to specify the element type explicitly, you can use
<code>parquet_schema()</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">schema</span> <span class="o">&lt;-</span> <span class="nf">parquet_schema</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;INT32&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="n">scores</span> <span class="o">=</span> <span class="nf">list</span><span class="p">(</span><span class="s">&#34;LIST&#34;</span><span class="p">,</span> <span class="n">element</span> <span class="o">=</span> <span class="s">&#34;INT32&#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">write_parquet</span><span class="p">(</span><span class="n">df</span><span class="p">,</span> <span class="n">tmp2</span> <span class="o">&lt;-</span> <span class="nf">tempfile</span><span class="p">(</span><span class="n">fileext</span> <span class="o">=</span> <span class="s">&#34;.parquet&#34;</span><span class="p">),</span> <span class="n">schema</span> <span class="o">=</span> <span class="n">schema</span><span class="p">)</span></span></span></code></pre></div></div>
<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">read_parquet_schema</span><span class="p">(</span><span class="n">tmp2</span><span class="p">)</span></span></span></code></pre></div></div>
<pre><code># A data frame: 5 × 14
  file_name  r_col name  r_type type  type_length repetition_type converted_type
  &lt;chr&gt;      &lt;int&gt; &lt;chr&gt; &lt;chr&gt;  &lt;chr&gt;       &lt;int&gt; &lt;chr&gt;           &lt;chr&gt;         
1 /var/fold…    NA sche… &lt;NA&gt;   &lt;NA&gt;           NA &lt;NA&gt;            &lt;NA&gt;          
2 /var/fold…     1 id    integ… INT32          NA REQUIRED        &lt;NA&gt;          
3 /var/fold…     2 scor… list(… &lt;NA&gt;           NA OPTIONAL        LIST          
4 /var/fold…     2 list  &lt;NA&gt;   &lt;NA&gt;           NA REPEATED        &lt;NA&gt;          
5 /var/fold…     2 elem… &lt;NA&gt;   INT32          NA OPTIONAL        INT_32        
# ℹ 6 more variables: logical_type &lt;I&lt;list&gt;&gt;, num_children &lt;int&gt;, scale &lt;int&gt;,
#   precision &lt;int&gt;, field_id &lt;int&gt;, children &lt;list&gt;
</code></pre>
<h2 id="new-types">New types
</h2>
<h3 id="bit64integer64"><code>bit64::integer64</code>
</h3>
<p>Parquet&rsquo;s <code>INT64</code> type holds 64-bit integers. R&rsquo;s native <code>integer</code> is
only 32 bits, so nanoparquet has mapped <code>INT64</code> to <code>double</code> by default.
nanoparquet 0.5.1 adds support for <code>bit64::integer64</code>, which gives you true
64-bit integer arithmetic in R.</p>
<p><code>write_parquet()</code> now writes <code>bit64::integer64</code> columns as <code>INT64</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="nf">library</span><span class="p">(</span><span class="n">bit64</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">df2</span> <span class="o">&lt;-</span> <span class="nf">data.frame</span><span class="p">(</span><span class="n">id</span> <span class="o">=</span> <span class="nf">as.integer64</span><span class="p">(</span><span class="nf">c</span><span class="p">(</span><span class="m">1e18</span><span class="p">,</span> <span class="m">2e18</span><span class="p">,</span> <span class="m">3e18</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl"><span class="nf">write_parquet</span><span class="p">(</span><span class="n">df2</span><span class="p">,</span> <span class="n">tmp3</span> <span class="o">&lt;-</span> <span class="nf">tempfile</span><span class="p">(</span><span class="n">fileext</span> <span class="o">=</span> <span class="s">&#34;.parquet&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="nf">read_parquet_schema</span><span class="p">(</span><span class="n">tmp3</span><span class="p">)</span></span></span></code></pre></div></div>
<pre><code># A data frame: 2 × 14
  file_name  r_col name  r_type type  type_length repetition_type converted_type
  &lt;chr&gt;      &lt;int&gt; &lt;chr&gt; &lt;chr&gt;  &lt;chr&gt;       &lt;int&gt; &lt;chr&gt;           &lt;chr&gt;         
1 /var/fold…    NA sche… &lt;NA&gt;   &lt;NA&gt;           NA &lt;NA&gt;            &lt;NA&gt;          
2 /var/fold…     1 id    double INT64          NA REQUIRED        INT_64        
# ℹ 6 more variables: logical_type &lt;I&lt;list&gt;&gt;, num_children &lt;int&gt;, scale &lt;int&gt;,
#   precision &lt;int&gt;, field_id &lt;int&gt;, children &lt;list&gt;
</code></pre>
<p>To read <code>INT64</code> columns back as <code>bit64::integer64</code> instead of the default
<code>double</code>, use the <code>read_int64_type</code> option. The bit64 package must be
installed; if it isn&rsquo;t, nanoparquet throws a clear error.</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">read_parquet</span><span class="p">(</span><span class="n">tmp3</span><span class="p">,</span> <span class="n">options</span> <span class="o">=</span> <span class="nf">parquet_options</span><span class="p">(</span><span class="n">read_int64_type</span> <span class="o">=</span> <span class="s">&#34;integer64&#34;</span><span class="p">))</span></span></span></code></pre></div></div>
<pre><code># A data frame: 3 × 1
       id
  &lt;int64&gt;
1    0e18
2    2e18
3    3e18
</code></pre>
<h3 id="blobblob"><code>blob::blob</code>
</h3>
<p><code>read_parquet()</code> previously returned raw <code>BYTE_ARRAY</code> and
<code>FIXED_LEN_BYTE_ARRAY</code> columns (i.e. those without a string, UUID, or
decimal annotation) as plain lists of raw vectors. They are now returned as
<code>blob::blob</code> objects, which print more neatly and come with the full set of
blob helpers. <code>write_parquet()</code> now also accepts <code>blob::blob</code> columns, so
round-tripping binary data is straightforward:</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">blob</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">df3</span> <span class="o">&lt;-</span> <span class="nf">data.frame</span><span class="p">(</span><span class="n">id</span> <span class="o">=</span> <span class="m">1</span><span class="o">:</span><span class="m">3</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">df3</span><span class="o">$</span><span class="n">payload</span> <span class="o">&lt;-</span> <span class="n">blob</span><span class="o">::</span><span class="nf">blob</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">  <span class="nf">charToRaw</span><span class="p">(</span><span class="s">&#34;hello&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">  <span class="nf">charToRaw</span><span class="p">(</span><span class="s">&#34;world&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">  <span class="nf">charToRaw</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="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nf">write_parquet</span><span class="p">(</span><span class="n">df3</span><span class="p">,</span> <span class="n">tmp4</span> <span class="o">&lt;-</span> <span class="nf">tempfile</span><span class="p">(</span><span class="n">fileext</span> <span class="o">=</span> <span class="s">&#34;.parquet&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="nf">as.data.frame</span><span class="p">(</span><span class="nf">read_parquet</span><span class="p">(</span><span class="n">tmp4</span><span class="p">))</span></span></span></code></pre></div></div>
<pre><code>  id   payload
1  1 blob[5 B]
2  2 blob[5 B]
3  3 blob[1 B]
</code></pre>
<h2 id="nanoparquet-as-a-filter">nanoparquet as a filter
</h2>
<p>In Unix, a <em>filter</em> is a program that reads from standard input and writes
to standard output, making it a composable building block in shell pipelines.
<code>write_parquet()</code> now supports writing to standard output via
<code>file = &quot;:stdout:&quot;</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="nf">write_parquet</span><span class="p">(</span><span class="n">mtcars</span><span class="p">,</span> <span class="s">&#34;:stdout:&#34;</span><span class="p">)</span></span></span></code></pre></div></div>
<p>The most common use case is from the command line:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">Rscript --quiet -e <span class="s1">&#39;nanoparquet::write_parquet(mtcars, &#34;:stdout:&#34;)&#39;</span> &gt; mtcars.parquet</span></span></code></pre></div></div>
<p>You can build this into a data pipeline. For example, to convert a CSV
to Parquet, and then process Parquet with another tool in one shot,
without an intermediate <code>.parquet</code> file on the disk, you can do:</p>
<div class="code-block"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">cat data.csv <span class="p">|</span>
</span></span><span class="line"><span class="cl">  Rscript --quiet -e <span class="s1">&#39;
</span></span></span><span class="line"><span class="cl"><span class="s1">    df &lt;- read.csv(file(&#34;stdin&#34;))
</span></span></span><span class="line"><span class="cl"><span class="s1">    nanoparquet::write_parquet(df, &#34;:stdout:&#34;)
</span></span></span><span class="line"><span class="cl"><span class="s1">  &#39;</span> <span class="p">|</span> another-parquet-tool</span></span></code></pre></div></div>
<p>Since nanoparquet 0.4.0, <code>read_parquet()</code> can also read from an R
connection, so you can pipe Parquet data <em>in</em> as well:</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">url</span> <span class="o">&lt;-</span> <span class="s">&#34;https://raw.githubusercontent.com/r-lib/nanoparquet/main/inst/extdata/userdata1.parquet&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">con</span> <span class="o">&lt;-</span> <span class="nf">pipe</span><span class="p">(</span><span class="nf">paste</span><span class="p">(</span><span class="s">&#34;curl --silent&#34;</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">df</span> <span class="o">&lt;-</span> <span class="nf">read_parquet</span><span class="p">(</span><span class="n">con</span><span class="p">)</span></span></span></code></pre></div></div>
<h2 id="acknowledgements">Acknowledgements
</h2>
<p>We thank all contributors to nanoparquet so far, for opening issues,
submitting pull requests, and providing feedback:







<a href="https://github.com/Aariq" target="_blank" rel="noopener">@Aariq</a>
,







<a href="https://github.com/alvarocombo" target="_blank" rel="noopener">@alvarocombo</a>
,







<a href="https://github.com/apalacio9502" target="_blank" rel="noopener">@apalacio9502</a>
,







<a href="https://github.com/atsyplenkov" target="_blank" rel="noopener">@atsyplenkov</a>
,







<a href="https://github.com/cboettig" target="_blank" rel="noopener">@cboettig</a>
,







<a href="https://github.com/ChandlerLutz" target="_blank" rel="noopener">@ChandlerLutz</a>
,







<a href="https://github.com/cmrnp" target="_blank" rel="noopener">@cmrnp</a>
,







<a href="https://github.com/D3SL" target="_blank" rel="noopener">@D3SL</a>
,







<a href="https://github.com/damonbayer" target="_blank" rel="noopener">@damonbayer</a>
,







<a href="https://github.com/DavideMessinaARS" target="_blank" rel="noopener">@DavideMessinaARS</a>
,







<a href="https://github.com/eitsupi" target="_blank" rel="noopener">@eitsupi</a>
,







<a href="https://github.com/gksmyth" target="_blank" rel="noopener">@gksmyth</a>
,







<a href="https://github.com/hadley" target="_blank" rel="noopener">@hadley</a>
,







<a href="https://github.com/jack-davison" target="_blank" rel="noopener">@jack-davison</a>
,







<a href="https://github.com/jeroenjanssens" target="_blank" rel="noopener">@jeroenjanssens</a>
,







<a href="https://github.com/lbm364dl" target="_blank" rel="noopener">@lbm364dl</a>
,







<a href="https://github.com/lschneiderbauer" target="_blank" rel="noopener">@lschneiderbauer</a>
,







<a href="https://github.com/mrcaseb" target="_blank" rel="noopener">@mrcaseb</a>
,







<a href="https://github.com/pmarks" target="_blank" rel="noopener">@pmarks</a>
,







<a href="https://github.com/PMassicotte" target="_blank" rel="noopener">@PMassicotte</a>
,







<a href="https://github.com/r2evans" target="_blank" rel="noopener">@r2evans</a>
,







<a href="https://github.com/RealTYPICAL" target="_blank" rel="noopener">@RealTYPICAL</a>
,







<a href="https://github.com/tanho63" target="_blank" rel="noopener">@tanho63</a>
,







<a href="https://github.com/thisisnic" target="_blank" rel="noopener">@thisisnic</a>
,







<a href="https://github.com/torfason" target="_blank" rel="noopener">@torfason</a>
,







<a href="https://github.com/TurnaevEvgeny" target="_blank" rel="noopener">@TurnaevEvgeny</a>
,







<a href="https://github.com/Upipa" target="_blank" rel="noopener">@Upipa</a>
,







<a href="https://github.com/vankesteren" target="_blank" rel="noopener">@vankesteren</a>
,







<a href="https://github.com/vincentarelbundock" target="_blank" rel="noopener">@vincentarelbundock</a>
,







<a href="https://github.com/wlandau" target="_blank" rel="noopener">@wlandau</a>
,







<a href="https://github.com/YipengUva" target="_blank" rel="noopener">@YipengUva</a>
, and







<a href="https://github.com/yutannihilation" target="_blank" rel="noopener">@yutannihilation</a>
.</p>
]]></description>
      <enclosure url="https://opensource.posit.co/blog/2026-04-30_nanoparquet-0-5-1/featured.jpg" length="767877" type="image/jpeg" />
    </item>
    <item>
      <title>torch Ecosystem Updates</title>
      <link>https://opensource.posit.co/blog/2026-04-30_torch-ecosystem-updates-2026/</link>
      <pubDate>Thu, 30 Apr 2026 00:00:00 +0000</pubDate>
      <guid>https://opensource.posit.co/blog/2026-04-30_torch-ecosystem-updates-2026/</guid>
      <dc:creator>Daniel Falbel</dc:creator>
      <dc:creator>Tomasz Kalinowski</dc:creator><description><![CDATA[<p>We&rsquo;ve just published a new round of CRAN releases across the 






<a href="https://github.com/mlverse/torch" target="_blank" rel="noopener">torch</a>
 ecosystem. Here&rsquo;s a tour of what&rsquo;s new in each package.</p>
<h2 id="torch-v0170">torch v0.17.0
</h2>
<p>The most exciting experimental new feature is support for the 






<a href="https://github.com/mlverse/cudatoolkit" target="_blank" rel="noopener">cudatoolkit</a>

packages. With this, you no longer need a global CUDA toolkit installation in order to use torch on the GPU.</p>
<p>You can now do:</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></span><span class="line"><span class="cl">  <span class="s">&#34;cuda12.8&#34;</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">  <span class="n">repos</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="s">&#34;https://mlverse.r-universe.dev&#34;</span><span class="p">,</span> <span class="s">&#34;https://cloud.r-project.org&#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">install.packages</span><span class="p">(</span><span class="s">&#34;torch&#34;</span><span class="p">)</span></span></span></code></pre></div></div>
<p>The <code>{cuda12.8}</code> package bundles all the CUDA runtime libraries and torch can find it and use it by default.
See more details in the 


  
  
  





<a href="https://torch.mlverse.org/docs/articles/installation#cudatoolkit" target="_blank" rel="noopener">installation docs</a>
.</p>
<p>We also highlight the update to LibTorch v2.8.0 led by 






<a href="https://github.com/TroyHernandez" target="_blank" rel="noopener">Troy Hernandez</a>
 (






<a href="https://github.com/mlverse/torch/pull/1419" target="_blank" rel="noopener">#1419</a>
).</p>
<p>Additionally, this release includes many small bug fixes and small additions to the API. See the full release notes
in the 


  
  
  





<a href="https://torch.mlverse.org/docs/news/#torch-0170" target="_blank" rel="noopener">changelog</a>
.</p>
<h2 id="torchvision-v090">torchvision v0.9.0
</h2>
<p>






<a href="https://github.com/mlverse/torchvision" target="_blank" rel="noopener">torchvision</a>
 provides datasets, model architectures, and image transformations for computer vision. This is a big release with new models, datasets, and many improvements — largely driven by community contributors.</p>
<h3 id="new-models">New models:
</h3>
<ul>
<li><code>model_maskrcnn_resnet50_fpn()</code> and <code>model_maskrcnn_resnet50_fpn_v2()</code> for instance segmentation.</li>
<li><code>model_convnext_*_detection()</code> for object detection (tiny/small/base).</li>
<li><code>model_convnext_*_fcn()</code> and <code>model_convnext_*_upernet()</code> for semantic segmentation (tiny/small/base).</li>
</ul>
<h3 id="new-datasets-and-features">New datasets and features:
</h3>
<ul>
<li><code>vggface2_dataset()</code> for loading the VGGFace2 dataset.</li>
<li>New <code>coco_segmentation_dataset()</code>, split from <code>coco_detection_dataset()</code>, reducing memory usage by ~50%.</li>
<li>Collection dataset catalog with <code>search_collection()</code>, <code>get_collection_catalog()</code>, and <code>list_collection_datasets()</code> for discovering and exploring datasets.</li>
<li>New visualization utilities <code>draw_segmentation_masks()</code> and <code>vision_make_grid()</code>.</li>
</ul>
<p>See the full release notes in the 






<a href="https://github.com/mlverse/torchvision/releases/tag/v0.9.0" target="_blank" rel="noopener">changelog</a>
.</p>
<p>A huge thank you to the community contributors who made this release possible: 






<a href="https://github.com/cregouby" target="_blank" rel="noopener">@cregouby</a>
, 






<a href="https://github.com/ANAMASGARD" target="_blank" rel="noopener">@ANAMASGARD</a>
, 






<a href="https://github.com/Chandraveersingh1717" target="_blank" rel="noopener">@Chandraveersingh1717</a>
, 






<a href="https://github.com/DerrickUnleashed" target="_blank" rel="noopener">@DerrickUnleashed</a>
, and 






<a href="https://github.com/srishtiii28" target="_blank" rel="noopener">@srishtiii28</a>
.</p>
<h2 id="other-releases">Other releases
</h2>
<p>Most of the other packages don&rsquo;t have significant changes, and the releases add minimal improvements to docs, CI infrastructure and CRAN related updates.</p>
<ul>
<li><strong>






<a href="https://github.com/mlverse/luz/releases/tag/v0.5.2" target="_blank" rel="noopener">luz</a>
</strong> v0.5.2 — Higher-level API for torch with a Keras-like interface for training neural networks.</li>
<li><strong>






<a href="https://github.com/mlverse/hfhub/releases/tag/v0.1.2" target="_blank" rel="noopener">hfhub</a>
</strong> v0.1.2 — Download and cache files from Hugging Face Hub repositories, making it easy to use pretrained models and datasets from R.</li>
<li><strong>






<a href="https://github.com/mlverse/tok/releases/tag/v0.2.2" target="_blank" rel="noopener">tok</a>
</strong> v0.2.2 — Fast tokenizers for R, powered by the Hugging Face Tokenizers library written in Rust. Supports BPE, WordPiece, and other tokenization algorithms.</li>
<li><strong>






<a href="https://github.com/mlverse/torchdatasets/releases/tag/v0.3.2" target="_blank" rel="noopener">torchdatasets</a>
</strong> v0.3.2 — Extra ready-to-use datasets for torch, complementing the built-in datasets in torchvision.</li>
<li><strong>






<a href="https://github.com/mlverse/safetensors/releases/tag/v0.2.1" target="_blank" rel="noopener">safetensors</a>
</strong> v0.2.1 — Read and write the Safetensors file format, a safe and fast format for storing and loading tensors.</li>
<li><strong>






<a href="https://github.com/mlverse/tfevents/releases/tag/v0.0.5" target="_blank" rel="noopener">tfevents</a>
</strong> v0.0.5 — Write event files compatible with TensorBoard from R for experiment tracking and visualization.</li>
<li><strong>






<a href="https://github.com/mlverse/wav/releases/tag/v0.2.0" target="_blank" rel="noopener">wav</a>
</strong> v0.2.0 — Read and write WAV files in R.</li>
</ul>
<h2 id="new-maintainer">New maintainer
</h2>
<p>We&rsquo;re excited to welcome 






<a href="https://github.com/t-kalinowski" target="_blank" rel="noopener">Tomasz Kalinowski</a>
 as the new maintainer of torch and the broader mlverse ecosystem.</p>
]]></description>
      <enclosure url="https://opensource.posit.co/blog/2026-04-30_torch-ecosystem-updates-2026/thumbnail.svg" length="5539" type="image/svg&#43;xml" />
    </item>
    <item>
      <title>tidymodels Cheatsheets</title>
      <link>https://opensource.posit.co/blog/2026-04-29_tidymodels-cheatsheets/</link>
      <pubDate>Wed, 29 Apr 2026 00:00:00 +0000</pubDate>
      <guid>https://opensource.posit.co/blog/2026-04-29_tidymodels-cheatsheets/</guid>
      <dc:creator>Edgar Ruiz</dc:creator><description><![CDATA[<p>After almost 8 years, tidymodels finally has its first cheatsheets, and not just one, but two! The 






<a href="https://opensource.posit.co/resources/cheatsheets/ml-preprocessing-data/">first one</a>
, covering data preprocessing with <code>recipes</code>, was released a couple of months ago. Today, we are delighted to announce 






<a href="https://opensource.posit.co/resources/cheatsheets/ml-create-models/">a second cheatsheet</a>
, this time focusing on modeling with <code>parsnip</code>.</p>
<p>Both cheatsheets have a dedicated HTML version on the Posit Open Source site, so you can browse and search them without opening a PDF. In this post we&rsquo;ll walk through what each one covers, starting with the newest.</p>
<h2 id="create-models-with-parsnip">Create Models with <strong>parsnip</strong>
</h2>
<p><div class="not-prose"><figure>
    <img class="h-auto max-w-full rounded-lg"
      src="https://opensource.posit.co/blog/2026-04-29_tidymodels-cheatsheets/tidymodels-cheatsheets-parsnip.png"
      alt="Both pages of the Create Models with parsnip cheatsheet side by side, showing sections for Basics, Legends, Classification Only, Regression Only, General Use, Discriminant, Ensemble, Support Vector Machine, Feature Rules, Survival, and Operations."  title="The \&#34;Create Models with parsnip\&#34; cheatsheet — click to enlarge" 
      loading="lazy"
    ><figcaption class="text-sm text-center text-gray-500">The &quot;Create Models with parsnip&quot; cheatsheet — click to enlarge</figcaption>
  </figure></div>
</p>
<p>The cheatsheet is organized into three main parts: an introduction to parsnip&rsquo;s basics, a catalog of all models available through the package, and a hands-on operations reference for fitting and inspecting models. The basics section introduces how parsnip provides a single, unified interface for defining and fitting models, regardless of the underlying package powering them.</p>
<h3 id="model-catalog">Model catalog
</h3>
<p>The largest section of the cheatsheet catalogs all models available through parsnip, grouped by use case:</p>
<ul>
<li><strong>Classification only:</strong> models for binary and multiclass prediction. It also includes probability-based classification using Bayes&rsquo; theorem and models for ordinal responses.</li>
<li><strong>Regression only:</strong> models for predicting continuous numeric outcomes, from standard linear regression to generalized linear models for count data.</li>
<li><strong>General use:</strong> a versatile mix of model types that work for both classification and regression, including decision trees, nearest neighbors, neural networks, and spline-based approaches.</li>
<li><strong>Discriminant analysis:</strong> models that estimate the distribution of predictors separately for each class and use Bayes&rsquo; theorem to assign probabilities, available in linear, quadratic, flexible, and regularized variants.</li>
<li><strong>Ensemble methods:</strong> models that combine many individual learners into a stronger prediction, including random forests, gradient boosting, bagged trees, and Bayesian additive regression trees.</li>
<li><strong>Support Vector Machines:</strong> models that find an optimal boundary between classes, or fit a robust regression, using linear, polynomial, or radial kernel functions.</li>
<li><strong>Feature rules:</strong> models that extract simple, human-readable rules from tree ensembles and use them as the basis for prediction.</li>
<li><strong>Survival models:</strong> models for time-to-event data, covering both proportional hazards and fully parametric approaches.</li>
</ul>









  
  
    
  

  
  
    
  





  
  
    
  
    
  
  


<div class="grid gap-12 items-start mt-12 md:grid-cols-[3fr_2fr] ">
  
  
    
    
    
      <div class="prose max-w-none ">One design choice in particular makes this section much easier to navigate: <strong>pills</strong>. Each model&rsquo;s compatible engines and supported modes are shown as small, visually distinct tags, so you can see at a glance which mode a given engine supports, without having to read through the description text. Each mode is encoded in the pill with a number: Classification (1), Regression (2), Censored Regression (3), and Quantile Regression (4). A legend mapping each number to its mode is available at the top of page one.</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-04-29_tidymodels-cheatsheets/tidymodels-cheatsheets-pills.png"
        alt="A close-up of the decision_tree() entry in the parsnip cheatsheet, showing engine pills labeled partykit, rpart, and spark with mode support numbers. Annotations point out the engine name, the modes each engine supports, and the total number of engines available."  title="Engine pills show the name and supported modes of each engine at a glance" 
        loading="lazy"
      ><figcaption class="text-sm text-center text-gray-500">Engine pills show the name and supported modes of each engine at a glance</figcaption>
    </figure></div></div>
    
  
</div>

<p>And true to the R cheatsheet tradition, individual models or groups of related models are paired with <strong>small illustrations</strong>, thoughtfully designed for visual impact to aid recall. Each one attempts to accurately represent the function or functions it accompanies, making them a genuine navigation aid rather than decoration, especially when you have a vague memory of &ldquo;that tree-based ensemble that used Bayesian analysis&rdquo; and need to scan quickly.</p>
<h3 id="operations">Operations
</h3>
<p>The last section covers the practical workflow of fitting and using a model. Each function is paired with a <strong>quick runnable example</strong>, and the examples build on each other starting from the two lines of code right below the section title, making it easy to follow the full workflow from model specification to results.</p>
<div class="text-right">



























<a href="https://opensource.posit.co/resources/cheatsheets/ml-create-models/"
  class="btn-shortcode inline-flex mb-5 mr-5 items-center px-4 py-3 text-sm leading-5 gap-2 rounded-lg bg-blue-500 text-white font-semibold align-middle hover:bg-blue-600 transition no-underline">Explore the parsnip cheatsheet<span class="icon-[boxicons--arrow-right] w-5 h-5 flex-none"></span></a>

</div>
<h2 id="preprocessing-data-with-recipes">Preprocessing Data with <strong>recipes</strong>
</h2>
<p><div class="not-prose"><figure>
    <img class="h-auto max-w-full rounded-lg"
      src="https://opensource.posit.co/blog/2026-04-29_tidymodels-cheatsheets/tidymodels-cheatsheets-recipes.png"
      alt="Both pages of the Preprocessing Data with recipes cheatsheet side by side, showing sections for Basics, Filters, In-place Transformations, Imputation, Encodings, Dummy Variables, Multivariate Transformations, Date and Time, Row operations, Other, and Role and type."  title="The \&#34;Preprocessing Data with recipes\&#34; cheatsheet — click to enlarge" 
      loading="lazy"
    ><figcaption class="text-sm text-center text-gray-500">The &quot;Preprocessing Data with recipes&quot; cheatsheet — click to enlarge</figcaption>
  </figure></div>
</p>
<p>After a quick Basics section covering the core workflow, the vast majority of the cheatsheet is dedicated to <code>step_*()</code> functions, the building blocks of any recipe, before finishing with role and type management.</p>
<h3 id="step-catalog">Step catalog
</h3>
<p>The steps are organized into groups based on what they do, each listed with its arguments and a short description:</p>
<ul>
<li><strong>Filters:</strong> steps for removing variables that are sparse, zero-variance, linearly dependent, highly correlated, or missing too many values</li>
<li><strong>In-place Transformations:</strong> basis functions (splines, polynomials), discretization, and normalization steps</li>
<li><strong>Imputation:</strong> steps for filling in missing values, ranging from simple statistical substitution to model-based approaches</li>
<li><strong>Encodings:</strong> type converters (e.g. factor to string, numeric to factor), value converters, and other factor-handling steps</li>
<li><strong>Dummy Variables:</strong> one-hot and binary encoding, text pattern matching, and conversion helpers</li>
<li><strong>Multivariate Transformations:</strong> signal extraction (PCA, ICA, PLS, and friends) and centroid-based distance measures</li>
<li><strong>Date &amp; Time:</strong> steps for converting date and datetime columns into usable numeric or factor features</li>
<li><strong>Row operations:</strong> sampling, shuffling, slicing, and removing rows with missing values</li>
<li><strong>Other:</strong> interaction terms, renaming, rolling window statistics, geographic distances, and ratios</li>
</ul>
<p>As with the parsnip cheatsheet, each group of steps is paired with <strong>small, thoughtfully designed illustrations</strong> to help you visually locate a step family when scanning.</p>
<h3 id="role--type">Role &amp; type
</h3>









  
  
    
  

  
  
    
  





  
  
    
  
    
  
  


<div class="grid gap-12 items-start mt-12 md:grid-cols-[3fr_2fr] ">
  
  
    
    
    
      <div class="prose max-w-none ">The last section focuses on the selection and management of variable roles and types within the recipe. The selection side covers ways to target variables by their role (outcome, predictor, or any custom role) as well as by their type (numeric, factor, logical, and so on), including a handy set of convenience selectors for the most common combinations. The management side shows how to add, update, and remove roles, showing you how to gain fine-grained control over how each variable participates in the recipe.</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-04-29_tidymodels-cheatsheets/tidymodels-cheatsheets-selectors.png"
        alt="A close-up of the Role and type section of the recipes cheatsheet, showing the Selectors and Convenience Selectors subsections. Each convenience selector function is listed alongside colored pills indicating which variable types it targets."  title="Easily find the right selector function with this at-a-glance guide" 
        loading="lazy"
      ><figcaption class="text-sm text-center text-gray-500">Easily find the right selector function with this at-a-glance guide</figcaption>
    </figure></div></div>
    
  
</div>

<br/>
<div class="text-right">



























<a href="https://opensource.posit.co/resources/cheatsheets/ml-preprocessing-data/"
  class="btn-shortcode inline-flex mb-5 mr-5 items-center px-4 py-3 text-sm leading-5 gap-2 rounded-lg bg-blue-500 text-white font-semibold align-middle hover:bg-blue-600 transition no-underline">Explore the recipes cheatsheet<span class="icon-[boxicons--arrow-right] w-5 h-5 flex-none"></span></a>

</div>
<h2 id="need-them-on-the-go-print-them">Need them on the go? Print them!
</h2>
<p>A lot of care went into ensuring both cheatsheets hold up when printed, particularly in black and white. We know that many folks print cheatsheets to keep at their desk for quick reference, and we wanted to make sure they remain fully usable in that medium. That meant making sure font sizes and weights stay legible on paper, that the illustrations remain perceptible without color, and that contrast levels are strong enough that no text ends up too pale to read or too heavy to parse. Accessibility in print mattered to us just as much as clarity on screen.</p>
<p><div class="not-prose"><figure>
    <img class="h-auto max-w-full rounded-lg"
      src="https://opensource.posit.co/blog/2026-04-29_tidymodels-cheatsheets/tidymodels-cheatsheets-bw.png"
      alt="Both cheatsheets shown side by side in black and white, demonstrating that the text, illustrations, and layout remain clear and readable without color."  title="New tidymodels cheatsheets are fully readable when printed" 
      loading="lazy"
    ><figcaption class="text-sm text-center text-gray-500">New tidymodels cheatsheets are fully readable when printed</figcaption>
  </figure></div>
</p>
<script>
(function() {
  'use strict';
  const lightbox = document.createElement('div');
  lightbox.id = 'image-lightbox';
  lightbox.className = 'fixed inset-0 z-50 hidden items-center justify-center bg-blue-100/80 transition-opacity';
  lightbox.innerHTML = `
    <button id="lightbox-close" class="absolute top-4 right-4 text-gray-700 text-4xl font-light hover:text-gray-900 transition-colors z-10" aria-label="Close lightbox">
      <svg class="w-10 h-10" fill="none" stroke="currentColor" viewBox="0 0 24 24">
        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
      </svg>
    </button>
    <img id="lightbox-image" class="max-w-[90vw] max-h-[90vh] object-contain" alt="">
  `;
  document.body.appendChild(lightbox);
  const lightboxImg = document.getElementById('lightbox-image');
  const closeBtn = document.getElementById('lightbox-close');
  const proseImages = document.querySelectorAll('.prose img:not(a img)');
  proseImages.forEach(img => {
    img.style.cursor = 'pointer';
    img.setAttribute('role', 'button');
    img.setAttribute('tabindex', '0');
    img.addEventListener('click', function() {
      lightboxImg.src = this.src;
      lightboxImg.alt = this.alt || '';
      lightbox.classList.remove('hidden');
      lightbox.classList.add('flex');
      document.body.style.overflow = 'hidden';
    });
    img.addEventListener('keydown', function(e) {
      if (e.key === 'Enter' || e.key === ' ') {
        e.preventDefault();
        this.click();
      }
    });
  });
  function closeLightbox() {
    lightbox.classList.add('hidden');
    lightbox.classList.remove('flex');
    document.body.style.overflow = '';
  }
  closeBtn.addEventListener('click', closeLightbox);
  lightbox.addEventListener('click', function(e) {
    if (e.target === lightbox) { closeLightbox(); }
  });
  document.addEventListener('keydown', function(e) {
    if (e.key === 'Escape' && !lightbox.classList.contains('hidden')) { closeLightbox(); }
  });
})();
</script>
]]></description>
      <enclosure url="https://opensource.posit.co/blog/2026-04-29_tidymodels-cheatsheets/tidymodels-cheatsheets.png" length="1674954" type="image/png" />
    </item>
    <item>
      <title>Customize Positron with these community-curated resources</title>
      <link>https://opensource.posit.co/blog/2026-04-28_positron-community-resources/</link>
      <pubDate>Tue, 28 Apr 2026 00:00:00 +0000</pubDate>
      <guid>https://opensource.posit.co/blog/2026-04-28_positron-community-resources/</guid>
      <dc:creator>Isabella Velásquez</dc:creator>
      <dc:creator>Libby Heeren</dc:creator><description><![CDATA[<p>We’ve been having a blast at the Data Science Lab. It’s a new, experimental, and super informal weekly community call (which you can register for here: 






<a href="https://pos.it/dslab" target="_blank" rel="noopener">pos.it/dslab</a>
!). In the spirit of being bold and trying things out, Isabel Zimmerman and Davis Vaughan from the Positron dev team joined us for the very first Lab “pilot” back in November, sharing some of their favorite Positron settings and extensions.</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>
  




<p>The Lab with Isabel and Davis was so much fun that we officially started the series in December, and then, in January, we jumped at the chance to have 






<a href="https://bsky.app/profile/andrew.heiss.phd" target="_blank" rel="noopener">Andrew Heiss</a>
 from Georgia State University join us to share his favorite tips and tricks for his Positron data science workflow (and beyond). He even wrote a 






<a href="https://www.andrewheiss.com/blog/2026/01/13/dsl-positron-workflow/" target="_blank" rel="noopener">helpful roundup on his blog!</a>
 Andrew’s workflow might give you data superpowers.</p>















  

  
  
  
    
    
  

  
  










  
  
    <div class="w-full aspect-video">
      <iframe
        src="https://www.youtube.com/embed/kqYbJoebrwE"
        class="w-full h-full"
        
        frameborder="0"
        allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
        allowfullscreen></iframe>
    </div>
  




<p>Following Andrew’s Data Science Lab, Emil Hvitfeldt felt inspired to write up his 






<a href="https://emilhvitfeldt.com/post/positron-settings-extensions/" target="_blank" rel="noopener">list of extensions and settings on his blog</a>
! We love community-event-driven development 😉</p>
<p>Speaking of community: Libby posted on Bluesky about wanting Positron’s main title bar to function like RStudio’s, showing the project and git branch name, and Emil shared a setting he’d learned from Davis that does exactly that! Want to customize your own Positron title bar? 






<a href="https://bsky.app/profile/libbyheeren.bsky.social/post/3m4zaeuomgs2w" target="_blank" rel="noopener">Check out the post about the setting here</a>
 and try it for yourself. It’s in the comments!</p>
<blockquote class="bluesky-embed" data-bluesky-uri="at://did:plc:777eanw7giyjd76dwatpch3d/app.bsky.feed.post/3m4zaeuomgs2w" data-bluesky-cid="bafyreiat23fkrdjgdd76pzy62plnjc7ro6qh5jtkyjqlxaxptdfg253sva" data-bluesky-embed-color-mode="system"><p lang="en">You know how in #RStudio, it puts your branch name at the top? Like &quot;project_name - branch - RStudio&quot; (shown here)
<p>I want that in #Positron 🤔 and I haven't found a popular extension for it. (I think there is one that works in VSCode, but not in Positron!)</p>
<p>Any ideas, #rstats #databs crew?<br><br><a href="https://bsky.app/profile/did:plc:777eanw7giyjd76dwatpch3d/post/3m4zaeuomgs2w?ref_src=embed">[image or embed]</a></p>— Libby Heeren (<a href="https://bsky.app/profile/did:plc:777eanw7giyjd76dwatpch3d?ref_src=embed">@libbyheeren.bsky.social</a>) <a href="https://bsky.app/profile/did:plc:777eanw7giyjd76dwatpch3d/post/3m4zaeuomgs2w?ref_src=embed">November 6, 2025 at 10:26 PM</a></blockquote><script async src="https://embed.bsky.app/static/embed.js" charset="utf-8"></script></p>
<p>We highly recommend watching the Lab videos if you love hearing IDE thoughts and other off-the-cuff ideas, but if you are just itching to find the settings and extensions mentioned, there’s a list of resources below. Do you have your own Positron setting and extension list? Let us know all about it 💙</p>
<h3 id="keyboard-shortcuts">Keyboard shortcuts
</h3>
<ul>
<li>






<a href="https://emilhvitfeldt.com/post/positron-key-bindings/" target="_blank" rel="noopener">Emil&rsquo;s keyboard shortcut blog post</a>
</li>
<li>






<a href="https://positron.posit.co/keyboard-shortcuts.html" target="_blank" rel="noopener">Positron docs on keyboard shortcuts</a>
</li>
<li>






<a href="https://nathan-jeffery.netlify.app/blog/2025-08-26-read-rds-positron/" target="_blank" rel="noopener">Nathan Jeffery’s keybinding for reading RDS files into Positron</a>
</li>
</ul>
<h3 id="settings">Settings
</h3>
<ul>
<li>






<a href="https://lucasprag.com/posts/underrated-vscode-feature-native-tabs/" target="_blank" rel="noopener">Native Tabs</a>
</li>
</ul>
<h3 id="extensions">Extensions
</h3>
<table>
<colgroup>
<col style="width: 40%;">
<col style="width: 60%;">
</colgroup>
<thead>
<tr>
<th>Extension</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://open-vsx.org/extension/atsyplenkov/pastum">Pastum</a><br><img src="https://opensource.posit.co/blog/2026-04-28_positron-community-resources/images/image1.png" alt="Pastum extension logo with art deco style design" width="128"></td>
<td>Like <a href="https://milesmcbain.github.io/datapasta/">datapasta</a>, but for Positron</td>
</tr>
<tr>
<td><a href="https://open-vsx.org/extension/grrrck/positron-plus-1-e">positron-plus-1-e</a><br><img src="https://opensource.posit.co/blog/2026-04-28_positron-community-resources/images/image2.png" alt="Positron +1e extension logo featuring an atom symbol with +1e notation" width="128"></td>
<td>Garrick Aden-Buie’s data science extension bundle package</td>
</tr>
<tr>
<td><a href="https://open-vsx.org/extension/oderwat/indent-rainbow">indent-rainbow</a><br><img src="https://opensource.posit.co/blog/2026-04-28_positron-community-resources/images/image3.png" alt="indent-rainbow extension icon showing code with colored indent guides" width="128"></td>
<td>Change the color of the indents in front of your text</td>
</tr>
<tr>
<td><a href="https://open-vsx.org/extension/mechatroner/rainbow-csv">Rainbow CSV extension</a><br><img src="https://opensource.posit.co/blog/2026-04-28_positron-community-resources/images/image4.png" alt="Rainbow CSV extension logo with colorful CSV letters" width="128"></td>
<td>Highlight columns in distinct colors</td>
</tr>
<tr>
<td><a href="https://open-vsx.org/extension/xiangda/enter-folder">Enter Folder</a><br><img src="https://opensource.posit.co/blog/2026-04-28_positron-community-resources/images/image5.png" alt="Enter Folder extension icon showing a yellow folder with blue arrow" width="128"></td>
<td>Make your folder look like the root folder, making your Positron file explorer work more similarly to RStudio’s File Pane</td>
</tr>
<tr>
<td><a href="https://catppuccin.com/">Catppuccin themes</a><br><img src="https://opensource.posit.co/blog/2026-04-28_positron-community-resources/images/image6.png" alt="Catppuccin theme logo featuring a cat mascot on pastel rainbow background" width="128"></td>
<td>A cozy set of pastel themes used by many in the DS Lab community</td>
</tr>
<tr>
<td><a href="https://open-vsx.org/extension/aaron-bond/better-comments">Better Comments</a><br><img src="https://opensource.posit.co/blog/2026-04-28_positron-community-resources/images/image7.png" alt="Better Comments extension icon with comment symbol //!" width="128"></td>
<td>Categorize your to-dos</td>
</tr>
<tr>
<td><a href="https://open-vsx.org/extension/brunnerh/file-properties-viewer">File Properties Viewer</a><br><img src="https://opensource.posit.co/blog/2026-04-28_positron-community-resources/images/image8.png" alt="File Properties Viewer extension icon showing a document with information symbol" width="128"></td>
<td>Display a menu for file system properties</td>
</tr>
<tr>
<td><a href="https://open-vsx.org/extension/johnpapa/vscode-peacock">Peacock</a><br><img src="https://opensource.posit.co/blog/2026-04-28_positron-community-resources/images/image9.png" alt="Peacock extension logo with colorful peacock feather design" width="128"></td>
<td>Change your workspace colors, making the differences between projects more striking and noticeable</td>
</tr>
<tr>
<td><a href="https://open-vsx.org/extension/alefragnani/project-manager">Project Manager</a><br><img src="https://opensource.posit.co/blog/2026-04-28_positron-community-resources/images/image10.png" alt="Project Manager extension icon showing a blue folder with bookmark" width="128"></td>
<td>Switch between projects</td>
</tr>
<tr>
<td><a href="https://open-vsx.org/extension/ban/spellright">Spell Right</a><br><img src="https://opensource.posit.co/blog/2026-04-28_positron-community-resources/images/image11.png" alt="Spell Right extension icon showing abc with checkmark" width="128"></td>
<td>Spell check your documents</td>
</tr>
</tbody>
</table>
]]></description>
      <enclosure url="https://opensource.posit.co/blog/2026-04-28_positron-community-resources/thumbnail.png" length="2671833" type="image/png" />
    </item>
    <item>
      <title>New tidymodels Releases for April 2026</title>
      <link>https://opensource.posit.co/blog/2026-04-27_tidymodels-april-2026/</link>
      <pubDate>Mon, 27 Apr 2026 00:00:00 +0000</pubDate>
      <guid>https://opensource.posit.co/blog/2026-04-27_tidymodels-april-2026/</guid>
      <dc:creator>Max Kuhn</dc:creator>
      <dc:creator>Hannah Frick</dc:creator>
      <dc:creator>Emil Hvitfeldt</dc:creator><description><![CDATA[<p>We&rsquo;ve released a sequence of tidymodels packages over the last few weeks: dials (1.4.3), parsnip (1.5.0), tune (2.1.0), yardstick (1.4.0), and tidymodels (1.5.0). You can install them via:</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"># tidymodels installs all of the new versions</span>
</span></span><span class="line"><span class="cl"><span class="nf">require</span><span class="p">(</span><span class="n">pak</span><span class="p">)</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;tidymodels&#34;</span><span class="p">)</span></span></span></code></pre></div></div>
<p>Here are links to the NEWS files for each package:</p>
<ul>
<li>


  
  
  





<a href="https://dials.tidymodels.org/news/index.html#dials-143" target="_blank" rel="noopener">dials</a>
</li>
<li>


  
  
  





<a href="https://parsnip.tidymodels.org/news/index.html#parsnip-150" target="_blank" rel="noopener">parsnip</a>
</li>
<li>


  
  
  





<a href="https://tune.tidymodels.org/news/index.html#tune-210" target="_blank" rel="noopener">tune</a>
</li>
<li>


  
  
  





<a href="https://yardstick.tidymodels.org/news/index.html#yardstick-140" target="_blank" rel="noopener">yardstick</a>
</li>
<li>


  
  
  





<a href="https://finetune.tidymodels.org/news/index.html#finetune-130" target="_blank" rel="noopener">finetune</a>
</li>
<li>


  
  
  





<a href="https://tidymodels.tidymodels.org/news/index.html#tidymodels-150" target="_blank" rel="noopener">tidymodels</a>
</li>
</ul>
<p>Let&rsquo;s first talk about the two biggest updates enabled by this group of releases, then we&rsquo;ll cover some of the other changes for each package.</p>
<h2 id="ordered-outcomes">Ordered Outcomes
</h2>
<p>parsnip has a new model type, <code>ordinal_reg()</code>, analogous to <code>multinom_reg()</code>, for fitting various generalized linear models with ordered class levels.</p>
<p>The 






<a href="https://github.com/corybrunson/ordered" target="_blank" rel="noopener">ordered package by Cory Brunson</a>
 is now on CRAN. This contains the specific engine code for these models, including:</p>
<ul>
<li><code>ordinal_reg()</code>: three engines: <code>&quot;polr&quot;</code>, <code>&quot;ordinalNet&quot;</code>, and <code>&quot;vglm&quot;</code>.</li>
<li><code>gen_additive_mod()</code>: <code>&quot;vgam&quot;</code></li>
<li><code>decision_tree()</code>: <code>&quot;rpartScore&quot;</code></li>
<li><code>rand_forest()</code>: <code>&quot;ordinalForest&quot;</code></li>
</ul>
<p>These models can be fitted, tuned, and evaluated with tidymodels. For the evaluation, we&rsquo;ve added a specific performance metric for ordered categories: the 


  
  
  





<a href="https://aml4td.org/chapters/cls-metrics.html#sec-ordered-categories" target="_blank" rel="noopener">ranked probability score</a>
 (RPS). The function <code>ranked_prob_score()</code> is in the new yardstick release and requires an ordered factor for the outcome.</p>
<h2 id="quantile-regression">Quantile Regression
</h2>
<p>We 


  
  
  





<a href="https://tidyverse.org/blog/2025/02/tidymodels-2025-q1/#quantile-regression-in-parsnip" target="_blank" rel="noopener">previously reported</a>
 that parsnip supports quantile regression models. With the latest set of releases, 


  
  
  





<a href="https://parsnip.tidymodels.org/news/index.html#quantile-regression-1-5-0" target="_blank" rel="noopener">new boosting and neural network engines</a>
 are available, and these models can now be tuned and evaluated using a relevant metric. yardstick now includes the <em>weighted interval score</em> (






<a href="https://doi.org/10.1371/journal.pcbi.1008618" target="_blank" rel="noopener">Bracher <em>et al</em> (2021)</a>
) to evaluate the quality of the quantile predictions.</p>
<p>Here&rsquo;s a simple one-dimensional example using the Ames data; we&rsquo;ll predict the sale price as a function of latitude. To start, let&rsquo;s make a training/test split, generate some resamples, and plot the training data.</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="c1"># We&#39;ll also need the qrnn package for the neural network engine</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">set.seed</span><span class="p">(</span><span class="m">1215</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">ames_split</span> <span class="o">&lt;-</span>
</span></span><span class="line"><span class="cl">  <span class="n">ames</span> <span class="o">|&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nf">select</span><span class="p">(</span><span class="n">Latitude</span><span class="p">,</span> <span class="n">Sale_Price</span><span class="p">)</span> <span class="o">|&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nf">initial_split</span><span class="p">(</span><span class="n">strata</span> <span class="o">=</span> <span class="n">Sale_Price</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">ames_train</span> <span class="o">&lt;-</span> <span class="nf">training</span><span class="p">(</span><span class="n">ames_split</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">ames_test</span> <span class="o">&lt;-</span> <span class="nf">testing</span><span class="p">(</span><span class="n">ames_split</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">ames_rs</span> <span class="o">&lt;-</span> <span class="nf">vfold_cv</span><span class="p">(</span><span class="n">ames_train</span><span class="p">,</span> <span class="n">strata</span> <span class="o">=</span> <span class="n">Sale_Price</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">ames_train</span> <span class="o">|&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nf">ggplot</span><span class="p">(</span><span class="nf">aes</span><span class="p">(</span><span class="n">Latitude</span><span class="p">,</span> <span class="n">Sale_Price</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 class="n">alpha</span> <span class="o">=</span> <span class="m">1</span> <span class="o">/</span> <span class="m">5</span><span class="p">)</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">  <span class="nf">geom_smooth</span><span class="p">(</span><span class="n">se</span> <span class="o">=</span> <span class="kc">FALSE</span><span class="p">)</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">  <span class="nf">labs</span><span class="p">(</span><span class="n">x</span> <span class="o">=</span> <span class="s">&#34;Latitude&#34;</span><span class="p">,</span> <span class="n">y</span> <span class="o">=</span> <span class="s">&#34;Sale Price (USD)&#34;</span><span class="p">)</span></span></span></code></pre></div></div>
<img src="https://opensource.posit.co/blog/2026-04-27_tidymodels-april-2026/index.markdown_strict_files/figure-markdown_strict/split-ames-1.png" style="width:90.0%" data-fig-align="center" />
<p>Note that we almost always model these data with a log transformation on the outcome due to its inherent skewness. That helps us avoid making negative predictions, be more robust to overly influential points (i.e., locations with very large sale prices), and stabilize the variance. <em>However</em>, we don&rsquo;t necessarily have to do that with quantile regression. The objective functions used to estimate parameters do not impose requirements on the normality of the data or heterogeneity of residuals. For this analysis, let&rsquo;s stick with the original units of the outcome (USD).</p>
<p>There are a few engines for quantile regression, and we&rsquo;ll use a neural network model. To get started, the quantiles to be predicted need to be specified. We make a model specification with a few additions:</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"># Pre-defined quantiles of interest</span>
</span></span><span class="line"><span class="cl"><span class="n">qnt_lvls</span> <span class="o">&lt;-</span> <span class="nf">c</span><span class="p">(</span><span class="m">0.05</span><span class="p">,</span> <span class="m">0.25</span><span class="p">,</span> <span class="m">0.5</span><span class="p">,</span> <span class="m">0.75</span><span class="p">,</span> <span class="m">0.95</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">nnet_spec</span> <span class="o">&lt;-</span>
</span></span><span class="line"><span class="cl">  <span class="nf">mlp</span><span class="p">(</span><span class="n">hidden_units</span> <span class="o">=</span> <span class="nf">tune</span><span class="p">(),</span> <span class="n">penalty</span> <span class="o">=</span> <span class="nf">tune</span><span class="p">(),</span> <span class="n">epochs</span> <span class="o">=</span> <span class="m">10</span><span class="p">)</span> <span class="o">|&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># Set the quantile levels with the mode:</span>
</span></span><span class="line"><span class="cl">  <span class="nf">set_mode</span><span class="p">(</span><span class="s">&#34;quantile regression&#34;</span><span class="p">,</span> <span class="n">quantile_levels</span> <span class="o">=</span> <span class="n">qnt_lvls</span><span class="p">)</span> <span class="o">|&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># A new engine for quantile regression with neural networks via the</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># qrnn package. We&#39;ll add an engine argument to specify the</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># optimization method for training the model:</span>
</span></span><span class="line"><span class="cl">  <span class="nf">set_engine</span><span class="p">(</span><span class="s">&#34;qrnn&#34;</span><span class="p">,</span> <span class="n">method</span> <span class="o">=</span> <span class="s">&#34;adam&#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"># Scale the single predictor to help the model initialize its</span>
</span></span><span class="line"><span class="cl"><span class="c1"># parameters.</span>
</span></span><span class="line"><span class="cl"><span class="n">nnet_rec</span> <span class="o">&lt;-</span> <span class="nf">recipe</span><span class="p">(</span><span class="n">Sale_Price</span> <span class="o">~</span> <span class="n">.,</span> <span class="n">data</span> <span class="o">=</span> <span class="n">ames_train</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_predictors</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">nnet_wflow</span> <span class="o">&lt;-</span> <span class="nf">workflow</span><span class="p">(</span><span class="n">nnet_rec</span><span class="p">,</span> <span class="n">nnet_spec</span><span class="p">)</span></span></span></code></pre></div></div>
<p>From there, we can use any of our tuning functions to optimize the number of hidden units and the amount of weight decay. By default, the weighted interval score is used for this particular mode.</p>
<p>We&rsquo;ll consider 25 tuning parameter candidates to optimize model performance.</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">set.seed</span><span class="p">(</span><span class="m">971</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">nnet_res</span> <span class="o">&lt;-</span>
</span></span><span class="line"><span class="cl">  <span class="n">nnet_wflow</span> <span class="o">|&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nf">tune_grid</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">resamples</span> <span class="o">=</span> <span class="n">ames_rs</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">grid</span> <span class="o">=</span> <span class="m">25</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">control</span> <span class="o">=</span> <span class="nf">control_grid</span><span class="p">(</span><span class="n">save_workflow</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>We can get the performance metric and visualize which tuning parameter combinations have the smallest weighted interval score:</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">nnet_mtr</span> <span class="o">&lt;-</span> <span class="nf">collect_metrics</span><span class="p">(</span><span class="n">nnet_res</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">nnet_mtr</span> <span class="o">|&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nf">ggplot</span><span class="p">(</span><span class="nf">aes</span><span class="p">(</span><span class="n">penalty</span><span class="p">,</span> <span class="n">hidden_units</span><span class="p">,</span> <span class="n">size</span> <span class="o">=</span> <span class="n">mean</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 class="o">+</span>
</span></span><span class="line"><span class="cl">  <span class="nf">scale_x_log10</span><span class="p">()</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">  <span class="nf">coord_fixed</span><span class="p">(</span><span class="n">ratio</span> <span class="o">=</span> <span class="m">1</span><span class="p">)</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">  <span class="nf">labs</span><span class="p">(</span><span class="n">x</span> <span class="o">=</span> <span class="s">&#34;Penalty&#34;</span><span class="p">,</span> <span class="n">y</span> <span class="o">=</span> <span class="s">&#34;# Hidden Units&#34;</span><span class="p">,</span> <span class="n">size</span> <span class="o">=</span> <span class="s">&#34;WIS&#34;</span><span class="p">)</span></span></span></code></pre></div></div>
<img src="https://opensource.posit.co/blog/2026-04-27_tidymodels-april-2026/index.markdown_strict_files/figure-markdown_strict/quantile-tune-res-1.png" data-fig-align="center" width="480" />
<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">select_best</span><span class="p">(</span><span class="n">nnet_res</span><span class="p">,</span> <span class="n">metric</span> <span class="o">=</span> <span class="s">&#34;weighted_interval_score&#34;</span><span class="p">)</span></span></span></code></pre></div></div>
<pre><code># A tibble: 1 × 3
  hidden_units     penalty .config         
         &lt;int&gt;       &lt;dbl&gt; &lt;chr&gt;           
1           10 0.000000215 pre0_mod24_post0
</code></pre>
<p>The model appears to prefer a smaller penalty and more hidden units.</p>
<p>It&rsquo;s hard to conceptualize how well the model functions with just these numbers. To show that the metric does select good models, let&rsquo;s fit the best, median, and worst models and see how they look on the test set.</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">set.seed</span><span class="p">(</span><span class="m">8281</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">best_model</span> <span class="o">&lt;-</span> <span class="nf">fit_best</span><span class="p">(</span><span class="n">nnet_res</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">set.seed</span><span class="p">(</span><span class="m">8281</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">worst_model</span> <span class="o">&lt;-</span>
</span></span><span class="line"><span class="cl">  <span class="n">nnet_mtr</span> <span class="o">|&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nf">slice_max</span><span class="p">(</span><span class="n">mean</span><span class="p">,</span> <span class="n">n</span> <span class="o">=</span> <span class="m">1</span><span class="p">)</span> <span class="o">|&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nf">select</span><span class="p">(</span><span class="n">hidden_units</span><span class="p">,</span> <span class="n">penalty</span><span class="p">)</span> <span class="o">|&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nf">finalize_workflow</span><span class="p">(</span><span class="n">nnet_wflow</span><span class="p">,</span> <span class="n">parameters</span> <span class="o">=</span> <span class="n">_</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">ames_train</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">set.seed</span><span class="p">(</span><span class="m">8281</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">mid_model</span> <span class="o">&lt;-</span>
</span></span><span class="line"><span class="cl">  <span class="n">nnet_mtr</span> <span class="o">|&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># Since we have an odd number of grid points:</span>
</span></span><span class="line"><span class="cl">  <span class="nf">filter</span><span class="p">(</span><span class="n">mean</span> <span class="o">==</span> <span class="nf">median</span><span class="p">(</span><span class="n">mean</span><span class="p">))</span> <span class="o">|&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nf">select</span><span class="p">(</span><span class="n">hidden_units</span><span class="p">,</span> <span class="n">penalty</span><span class="p">)</span> <span class="o">|&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nf">finalize_workflow</span><span class="p">(</span><span class="n">nnet_wflow</span><span class="p">,</span> <span class="n">parameters</span> <span class="o">=</span> <span class="n">_</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">ames_train</span><span class="p">)</span></span></span></code></pre></div></div>
<p>Now let&rsquo;s plot the results. We&rsquo;ll color the predicted quantiles: black indicates the predicted median sale price, orange lines indicate the inner quartiles, and smoky periwinkle lines indicate the 0.05 and 0.95 quantiles (which could serve as 90% prediction intervals).</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">bind_rows</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">  <span class="n">best_model</span> <span class="o">|&gt;</span> <span class="nf">augment</span><span class="p">(</span><span class="n">ames_test</span><span class="p">)</span> <span class="o">|&gt;</span> <span class="nf">mutate</span><span class="p">(</span><span class="n">Model</span> <span class="o">=</span> <span class="s">&#34;Best Results&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">  <span class="n">mid_model</span> <span class="o">|&gt;</span> <span class="nf">augment</span><span class="p">(</span><span class="n">ames_test</span><span class="p">)</span> <span class="o">|&gt;</span> <span class="nf">mutate</span><span class="p">(</span><span class="n">Model</span> <span class="o">=</span> <span class="s">&#34;Meh Results&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">  <span class="n">worst_model</span> <span class="o">|&gt;</span> <span class="nf">augment</span><span class="p">(</span><span class="n">ames_test</span><span class="p">)</span> <span class="o">|&gt;</span> <span class="nf">mutate</span><span class="p">(</span><span class="n">Model</span> <span class="o">=</span> <span class="s">&#34;Worst Results&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span> <span class="o">|&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nf">mutate</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">.pred_quantile</span> <span class="o">=</span> <span class="nf">map</span><span class="p">(</span><span class="n">.pred_quantile</span><span class="p">,</span> <span class="o">~</span> <span class="nf">as_tibble</span><span class="p">(</span><span class="n">.x</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">  <span class="p">)</span> <span class="o">|&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nf">unnest</span><span class="p">(</span><span class="n">.pred_quantile</span><span class="p">)</span> <span class="o">|&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nf">arrange</span><span class="p">(</span><span class="n">Latitude</span><span class="p">)</span> <span class="o">|&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nf">ggplot</span><span class="p">(</span><span class="nf">aes</span><span class="p">(</span><span class="n">Latitude</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 class="nf">aes</span><span class="p">(</span><span class="n">y</span> <span class="o">=</span> <span class="n">Sale_Price</span><span class="p">),</span> <span class="n">alpha</span> <span class="o">=</span> <span class="m">1</span> <span class="o">/</span> <span class="m">30</span><span class="p">,</span> <span class="n">cex</span> <span class="o">=</span> <span class="m">3</span> <span class="o">/</span> <span class="m">4</span><span class="p">)</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">  <span class="nf">geom_path</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="nf">aes</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="n">y</span> <span class="o">=</span> <span class="n">.pred_quantile</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">group</span> <span class="o">=</span> <span class="n">.quantile_levels</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">col</span> <span class="o">=</span> <span class="nf">factor</span><span class="p">(</span><span class="n">.quantile_levels</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">show.legend</span> <span class="o">=</span> <span class="kc">FALSE</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">linewidth</span> <span class="o">=</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl">  <span class="p">)</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">  <span class="nf">scale_color_manual</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">values</span> <span class="o">=</span> <span class="nf">c</span><span class="p">(</span><span class="s">&#34;#8785B2FF&#34;</span><span class="p">,</span> <span class="s">&#34;#D95F30FF&#34;</span><span class="p">,</span> <span class="s">&#34;black&#34;</span><span class="p">,</span> <span class="s">&#34;#D95F30FF&#34;</span><span class="p">,</span> <span class="s">&#34;#8785B2FF&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">)</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">  <span class="nf">facet_wrap</span><span class="p">(</span><span class="o">~</span><span class="n">Model</span><span class="p">)</span></span></span></code></pre></div></div>
<img src="https://opensource.posit.co/blog/2026-04-27_tidymodels-april-2026/index.markdown_strict_files/figure-markdown_strict/fit-plots-1.png" data-fig-align="center" width="576" />
<p>These plots show that configurations with very large score values have poor fits (linear in this case). The &ldquo;meh&rdquo; model is nonlinear but not responsive enough to the datas&rsquo; ups and downs. The best model, with more hidden units and a low penalty, appears to be flexible enough to model the data well.</p>
<p>We&rsquo;ll have more metrics in yardstick that can use quantile predictions in the future. For example, we can extend the ones that we have, such as <code>rmse()</code> or <code>rsq()</code>, to use a predicted value from the center of the predictive distribution, such as the 0.5 quantile.</p>
<p>Now we&rsquo;ll describe various other improvements in the recently released versions.</p>
<h2 id="dials">dials
</h2>
<p>The latest dials release contains several new parameters for new-ish models in parsnip: For the <code>ordinal_reg()</code> models, dials now contains <code>ordinal_link()</code> and <code>odds_link()</code>. For the <code>tab_pfn()</code>, dials contains <code>num_estimators()</code>, <code>softmax_temperature()</code>, <code>balance_probabilities()</code>, <code>average_before_softmax()</code>, and <code>training_set_limit()</code>.</p>
<p>The other user-facing changes were related to input checking and related error messages. The most prominent example is that <code>parameters()</code> and the <code>grid_*()</code> functions now give more information in the error message when non-parameter objects are passed in: which inputs aren&rsquo;t a parameter object and what they are instead.</p>
<p>






<a href="https://github.com/corybrunson" target="_blank" rel="noopener">@corybrunson</a>
, 






<a href="https://github.com/daltonkw" target="_blank" rel="noopener">@daltonkw</a>
, 






<a href="https://github.com/hfrick" target="_blank" rel="noopener">@hfrick</a>
, 






<a href="https://github.com/jeroenjanssens" target="_blank" rel="noopener">@jeroenjanssens</a>
, 






<a href="https://github.com/topepo" target="_blank" rel="noopener">@topepo</a>
, and 






<a href="https://github.com/vmikk" target="_blank" rel="noopener">@vmikk</a>
 contributed to the package since the last release.</p>
<h2 id="yardstick">yardstick
</h2>
<p>Beyond the two new metrics <code>ranked_prob_score()</code> and <code>weighted_interval_score()</code> described above, this release adds a further 8 metrics.</p>
<p>Three new regression metrics:</p>
<ul>
<li><code>mse()</code> &mdash; mean squared error (the squared counterpart to the existing <code>rmse()</code>).</li>
<li><code>rmse_relative()</code> &mdash; root mean squared error normalized by the observed value range.</li>
<li><code>gini_coef()</code> &mdash; normalized Gini coefficient.</li>
</ul>
<p>Three new classification metrics:</p>
<ul>
<li><code>fall_out()</code> &mdash; false positive rate (1 − specificity).</li>
<li><code>miss_rate()</code> &mdash; false negative rate (1 − sensitivity).</li>
<li><code>markedness()</code> &mdash; predictive power of a classifier, computed as PPV + NPV − 1.</li>
</ul>
<p>Two new probability-based classification metrics:</p>
<ul>
<li><code>roc_dist()</code> &mdash; Euclidean distance from the perfect-classifier corner of ROC space.</li>
<li><code>sedi()</code> &mdash; Symmetric Extremal Dependence Index.</li>
</ul>
<p>In addition to these new metrics, we have also updated the documention of all metrics. Now each metric shows the formula used to calculate it, as well as the valid values it can produce.</p>
<p>We also have pages that list all metrics of the same type. These can be found with 






<a href="https://yardstick.tidymodels.org/reference/class-metrics.html" target="_blank" rel="noopener">?class-metrics</a>
, 






<a href="https://yardstick.tidymodels.org/reference/numeric-metrics.html" target="_blank" rel="noopener">?numeric-metrics</a>
 or linked within each metric documentation.</p>
<p>We are thankful to the developers who contributed to this version: 






<a href="https://github.com/abichat" target="_blank" rel="noopener">@abichat</a>
, 






<a href="https://github.com/astamm" target="_blank" rel="noopener">@astamm</a>
, 






<a href="https://github.com/corybrunson" target="_blank" rel="noopener">@corybrunson</a>
, 






<a href="https://github.com/DarioS" target="_blank" rel="noopener">@DarioS</a>
, 






<a href="https://github.com/EmilHvitfeldt" target="_blank" rel="noopener">@EmilHvitfeldt</a>
, 






<a href="https://github.com/FvD" target="_blank" rel="noopener">@FvD</a>
, 






<a href="https://github.com/hfrick" target="_blank" rel="noopener">@hfrick</a>
, 






<a href="https://github.com/JavOrraca" target="_blank" rel="noopener">@JavOrraca</a>
, 






<a href="https://github.com/jeroenjanssens" target="_blank" rel="noopener">@jeroenjanssens</a>
, 






<a href="https://github.com/jkylearmstrong-temple" target="_blank" rel="noopener">@jkylearmstrong-temple</a>
, 






<a href="https://github.com/mle2718" target="_blank" rel="noopener">@mle2718</a>
, 






<a href="https://github.com/nathant181" target="_blank" rel="noopener">@nathant181</a>
, 






<a href="https://github.com/SimonDedman" target="_blank" rel="noopener">@SimonDedman</a>
, 






<a href="https://github.com/topepo" target="_blank" rel="noopener">@topepo</a>
, and 






<a href="https://github.com/tripartio" target="_blank" rel="noopener">@tripartio</a>
</p>
<h2 id="parsnip">parsnip
</h2>
<p>Version 1.5.0 of parsnip had a variety of changes. Besides the additions for the two new model types shown above:</p>
<ul>
<li>We enabled case weight usage for the <code>&quot;nnet&quot;</code> engines of <code>mlp()</code> and <code>bag_mlp()</code> as well as for the <code>&quot;dbarts&quot;</code> engine of <code>bart()</code>.</li>
</ul>
<p>Many of the other changes are most likely to be noticed by developers:</p>
<ul>
<li>
<p>The interface for declaring tunable parameters has been simplified and is the same for main arguments as well as engine parameters. Also, these values can now be set inside extension packages.</p>
</li>
<li>
<p>We now export the generics for <code>predict_quantile()</code>, <code>predict_class()</code>, <code>predict_classprob()</code>, and <code>predict_hazard()</code>.</p>
</li>
<li>
<p><code>format_predictions()</code> is a new unified function for formatting prediction outputs, consolidating the logic from the individual <code>format_*()</code> functions. The individual functions <code>format_num()</code>, <code>format_class()</code>, <code>format_classprobs()</code>, <code>format_time()</code>, <code>format_survival()</code>, <code>format_linear_pred()</code>, and <code>format_hazard()</code> are now deprecated.</p>
</li>
</ul>
<p>Thanks to those who contributed to parsnip since the last release: 






<a href="https://github.com/CeresBarros" target="_blank" rel="noopener">@CeresBarros</a>
, 






<a href="https://github.com/corybrunson" target="_blank" rel="noopener">@corybrunson</a>
, 






<a href="https://github.com/EmilHvitfeldt" target="_blank" rel="noopener">@EmilHvitfeldt</a>
, 






<a href="https://github.com/hfrick" target="_blank" rel="noopener">@hfrick</a>
, 






<a href="https://github.com/iamYannC" target="_blank" rel="noopener">@iamYannC</a>
, 






<a href="https://github.com/jack-davison" target="_blank" rel="noopener">@jack-davison</a>
, 






<a href="https://github.com/jameslamb" target="_blank" rel="noopener">@jameslamb</a>
, 






<a href="https://github.com/martinju" target="_blank" rel="noopener">@martinju</a>
, and 






<a href="https://github.com/topepo" target="_blank" rel="noopener">@topepo</a>
.</p>
<h2 id="tune">tune
</h2>
<p>The core functionality of tune is to do all the model fitting (including pre- and postprocessing) and performance evaluation across various resamples and tuning parameter combinations. For grid search, we could take the full parameter grid, splice one parameter combination into the workflow at a time, and run with it. That can be pretty inefficient though. So what actually happens in tune are a few optimizations in how we do all that fitting and evaluating:
For preprocessing, we do it once for a resample (per preprocessing parameter combination) and then evaluate all model candidates on it. This lets us avoid unnecessarily repeating the same preprocessing multiple times.
For model fitting, we make use of what Max calls 






<a href="https://parsnip.tidymodels.org/articles/Submodels.html" target="_blank" rel="noopener">&ldquo;the submodel trick&rdquo;</a>
: For certain models, like a boosted tree, you can use <em>a submodel</em> to make predictions without having to refit the model. A boosted tree ensemble fitted with 20 trees can be used to make predictions for any number of trees up to the 20 used for fitting. That allows us to evaluate different tuning parameter candidates for, here, the number of trees, without having to refit the model. When we added postprocessing, we temporarily disabled this (to ensure we got the integration right) - now we&rsquo;ve brought it back. We make use of this speedup for both the main model as well as the calibration model.</p>
<p>Another big update is that the Gaussian process model package was changed from GPfit to GauPro because the former is no longer actively maintained. There are some differences:</p>
<ul>
<li>
<p>Fit diagnostics are computed and reported. If the fit quality is poor, an &ldquo;uncertainty sample&rdquo; that is furthest away from the existing data is used as the new candidate.</p>
</li>
<li>
<p>The GP no longer uses binary indicators for qualitative predictors. Instead, a &ldquo;categorical kernel&rdquo; is used for those parameter columns. Fewer starting values are required with this change.</p>
</li>
<li>
<p>For numeric predictors, the Matern 3/2 kernel is always used.</p>
</li>
</ul>
<p>Some other changes of note:</p>
<ul>
<li>
<p>When calculating resampling estimates, we can now use a weighted mean based on the number of rows in the assessment set thanks to Tyler Burch. You can opt-in to this using the new <code>add_resample_weights()</code> function. See <code>?calculate_resample_weights</code></p>
</li>
<li>
<p>The warning threshold when check the size of a workflow is now a parameter to the control functions and has a new default of 100MB.</p>
</li>
</ul>
<p>Some bug fixes:</p>
<ul>
<li>
<p>Models with submodel parameters would train all calibration models on predictions from a single submodel value instead of the correct value for each submodel. We sorted this out.</p>
</li>
<li>
<p>We fixed a bug for cases where we tune a grid without a model parameter but with a postprocessing parameter.</p>
</li>
<li>
<p>Another bug was fixed for <code>augment()</code> when using <code>last_fit()</code> objects</p>
</li>
</ul>
<p>Thanks to the following contributors: 






<a href="https://github.com/edgararuiz" target="_blank" rel="noopener">@edgararuiz</a>
, 






<a href="https://github.com/EmilHvitfeldt" target="_blank" rel="noopener">@EmilHvitfeldt</a>
, 






<a href="https://github.com/hfrick" target="_blank" rel="noopener">@hfrick</a>
, 






<a href="https://github.com/jeroenjanssens" target="_blank" rel="noopener">@jeroenjanssens</a>
, 






<a href="https://github.com/jjcurtin" target="_blank" rel="noopener">@jjcurtin</a>
, 






<a href="https://github.com/mikewolfe" target="_blank" rel="noopener">@mikewolfe</a>
, 






<a href="https://github.com/mthulin" target="_blank" rel="noopener">@mthulin</a>
, 






<a href="https://github.com/ncalliencsu" target="_blank" rel="noopener">@ncalliencsu</a>
, 






<a href="https://github.com/rvalieris" target="_blank" rel="noopener">@rvalieris</a>
, 






<a href="https://github.com/StevenWallaert" target="_blank" rel="noopener">@StevenWallaert</a>
, 






<a href="https://github.com/tjburch" target="_blank" rel="noopener">@tjburch</a>
, and 






<a href="https://github.com/topepo" target="_blank" rel="noopener">@topepo</a>
</p>
<h2 id="finetune">finetune
</h2>
<p>This release was mostly focused on internal changes to support the new version of tune.</p>
<h2 id="tidymodels">tidymodels
</h2>
<p>A basic release that updates the version numbers to require the latest releases of the core packages.</p>
]]></description>
      <enclosure url="https://opensource.posit.co/blog/2026-04-27_tidymodels-april-2026/2026-april-tidymodels.jpg" length="491241" type="image/jpeg" />
    </item>
    <item>
      <title>processx 3.9.0</title>
      <link>https://opensource.posit.co/blog/2026-04-27_processx-3-9-0/</link>
      <pubDate>Mon, 27 Apr 2026 00:00:00 +0000</pubDate>
      <guid>https://opensource.posit.co/blog/2026-04-27_processx-3-9-0/</guid>
      <dc:creator>Gábor Csárdi</dc:creator><description><![CDATA[<!--
TODO:
* [x] Look over / edit the post's title in the yaml
* [x] Edit (or delete) the description; note this appears in the Twitter card
* [x] Pick category and tags (see existing with [`hugodown::tidy_show_meta()`](https://rdrr.io/pkg/hugodown/man/use_tidy_post.html))
* [x] Find photo & update yaml metadata
* [x] Create `thumbnail-sq.jpg`; height and width should be equal
* [x] Create `thumbnail-wd.jpg`; width should be >5x height
* [x] [`hugodown::use_tidy_thumbnails()`](https://rdrr.io/pkg/hugodown/man/use_tidy_post.html)
* [x] Add intro sentence, e.g. the standard tagline for the package
* [x] ~~[`usethis::use_tidy_thanks()`](https://usethis.r-lib.org/reference/use_tidy_thanks.html)~~
-->
<p>We&rsquo;re happy to announce the release of 






<a href="https://processx.r-lib.org/" target="_blank" rel="noopener">processx</a>
 3.9.0. processx is an R package to run and manage system processes.</p>
<p>You can install it from CRAN with:</p>
<div class="highlight">
<pre class='chroma'><code class='language-r' data-lang='r'><span><span class='nf'><a href='https://rdrr.io/r/utils/install.packages.html'>install.packages</a></span><span class='o'>(</span><span class='s'>"processx"</span><span class='o'>)</span></span></code></pre>
</div>
<p>This blog post discusses the major new features in processx 3.9.0. You can see a full list of changes in the 






<a href="https://github.com/r-lib/processx/releases/tag/v3.9.0" target="_blank" rel="noopener">release notes</a>
.</p>
<h2 id="pipelines">Pipelines
</h2>
<p>New new <code>pipeline</code> class lets you connect two or more processes with kernel-level pipes, exactly like a Unix shell pipeline (<code>cmd1 | cmd2 | cmd3</code>): data flows directly between child processes without passing through R.</p>
<div class="highlight">
<pre class='chroma'><code class='language-r' data-lang='r'><span><span class='nv'>pl</span> <span class='o'>&lt;-</span> <span class='nv'><a href='http://processx.r-lib.org/reference/pipeline.html'>pipeline</a></span><span class='o'>$</span><span class='nf'>new</span><span class='o'>(</span></span>
<span>  <span class='nf'><a href='https://rdrr.io/r/base/list.html'>list</a></span><span class='o'>(</span><span class='nf'><a href='https://rdrr.io/r/base/c.html'>c</a></span><span class='o'>(</span><span class='s'>"sort"</span><span class='o'>)</span>, <span class='nf'><a href='https://rdrr.io/r/base/c.html'>c</a></span><span class='o'>(</span><span class='s'>"uniq"</span>, <span class='s'>"-c"</span><span class='o'>)</span>, <span class='nf'><a href='https://rdrr.io/r/base/c.html'>c</a></span><span class='o'>(</span><span class='s'>"sort"</span>, <span class='s'>"-rn"</span><span class='o'>)</span><span class='o'>)</span>,</span>
<span>  stdin <span class='o'>=</span> <span class='s'>"|"</span>,</span>
<span>  stdout <span class='o'>=</span> <span class='s'>"|"</span></span>
<span><span class='o'>)</span></span>
<span><span class='nv'>pl</span><span class='o'>$</span><span class='nf'>write_input</span><span class='o'>(</span><span class='s'>"banana\napple\nbanana\norange\napple\nbanana\n"</span><span class='o'>)</span></span>
<span><span class='nv'>pl</span><span class='o'>$</span><span class='nf'>close_input</span><span class='o'>(</span><span class='o'>)</span></span>
<span><span class='c'>#&gt; NULL</span></span>
<span></span><span><span class='nv'>pl</span><span class='o'>$</span><span class='nf'>read_all_output_lines</span><span class='o'>(</span><span class='o'>)</span></span>
<span><span class='c'>#&gt; [1] "   3 banana" "   2 apple"  "   1 orange"</span></span>
<span></span><span><span class='nv'>pl</span><span class='o'>$</span><span class='nf'>wait</span><span class='o'>(</span><span class='o'>)</span></span>
<span><span class='nv'>pl</span><span class='o'>$</span><span class='nf'>get_exit_statuses</span><span class='o'>(</span><span class='o'>)</span></span>
<span><span class='c'>#&gt; [[1]]</span></span>
<span><span class='c'>#&gt; [1] 0</span></span>
<span><span class='c'>#&gt; </span></span>
<span><span class='c'>#&gt; [[2]]</span></span>
<span><span class='c'>#&gt; [1] 0</span></span>
<span><span class='c'>#&gt; </span></span>
<span><span class='c'>#&gt; [[3]]</span></span>
<span><span class='c'>#&gt; [1] 0</span></span>
<span></span></code></pre>
</div>
<p>The <code>pipeline$new()</code> constructor takes a list of character vectors &mdash; one per command &mdash; along with the usual <code>stdin</code>, <code>stdout</code>, and <code>stderr</code> arguments. These apply to the <em>ends</em> of the pipeline: <code>stdin</code> connects to the first process, <code>stdout</code> reads from the last, and <code>stderr</code> controls all processes.</p>
<p>The key benefit over calling 






<a href="http://processx.r-lib.org/reference/run.html" target="_blank" rel="noopener"><code>run()</code></a>
 in sequence is efficiency: intermediate data never materialises in R. A pipeline processing gigabytes of log lines uses the same small kernel buffers as a shell pipeline would.</p>
<p>Because each step in the pipeline is a regular <code>process</code> object under the hood, you can access individual processes via <code>$get_processes()</code> &mdash; useful for reading per-process stderr or checking exit codes when a stage fails.</p>
<p><code>pipeline</code> works on Unix and Windows and is currently experimental: the API may still change.</p>
<h2 id="pseudo-terminal-support">Pseudo-terminal support
</h2>
<h3 id="processxrunpty--true"><code>processx::run(pty = TRUE)</code>
</h3>
<p>Many command-line tools behave differently when their output is not connected to a terminal: they disable colour, turn off progress bars, or buffer output more aggressively. The <code>pty = TRUE</code> option runs a process inside a pseudo-terminal so it sees a real terminal &mdash; colour and interactive behaviour included.</p>
<p>






<a href="http://processx.r-lib.org/reference/run.html" target="_blank" rel="noopener"><code>run()</code></a>
 now supports <code>pty = TRUE</code> directly:</p>
<div class="highlight">
<pre class='chroma'><code class='language-r' data-lang='r'><span><span class='nv'>out</span> <span class='o'>&lt;-</span> <span class='nf'><a href='http://processx.r-lib.org/reference/run.html'>run</a></span><span class='o'>(</span><span class='s'>"ls"</span>, <span class='nf'><a href='https://rdrr.io/r/base/c.html'>c</a></span><span class='o'>(</span><span class='s'>"--color"</span>, <span class='nf'><a href='https://rdrr.io/r/base/path.expand.html'>path.expand</a></span><span class='o'>(</span><span class='s'>"~/works/processx"</span><span class='o'>)</span><span class='o'>)</span>, pty <span class='o'>=</span> <span class='kc'>TRUE</span><span class='o'>)</span></span>
<span><span class='nf'><a href='https://rdrr.io/r/base/cat.html'>cat</a></span><span class='o'>(</span><span class='nv'>out</span><span class='o'>$</span><span class='nv'>stdout</span><span class='o'>)</span></span>
<span><span class='c'>#&gt; DESCRIPTION    NAMESPACE      README.md      <span style='color: #0000BB;'>inst</span>           <span style='color: #0000BB;'>tests</span></span></span>
<span><span class='c'>#&gt; LICENSE        NEWS.md        _pkgdown.yml   <span style='color: #0000BB;'>man</span>            <span style='color: #0000BB;'>tools</span></span></span>
<span><span class='c'>#&gt; LICENSE.md     <span style='color: #0000BB;'>R</span>              air.toml       processx.Rproj <span style='color: #0000BB;'>vignettes</span></span></span>
<span><span class='c'>#&gt; Makefile       README.Rmd     codecov.yml    <span style='color: #0000BB;'>src</span></span></span>
<span></span></code></pre>
</div>
<p>When <code>pty = TRUE</code>, stderr is merged into stdout (the result&rsquo;s <code>$stderr</code> is always <code>NULL</code>), because a PTY has a single stream. You can also supply a file path as <code>stdin</code>; its contents are fed to the process via the PTY master, followed by an EOF signal.</p>
<h3 id="windows-support">Windows support
</h3>
<p>processx 3.9.0 adds support for pseudo-terminals (PTYs) on Windows, starting from Windows 10 version 1809. The Windows implementation uses the ConPTY API (<code>CreatePseudoConsole</code>), loaded dynamically so processx continues to load on older Windows and emits a clear error if <code>pty = TRUE</code> is requested on an unsupported version.</p>
<h2 id="other-improvements">Other improvements
</h2>
<h3 id="new-process-cleanup-article">New process cleanup article
</h3>
<p>A new article, 






<a href="https://processx.r-lib.org/articles/cleanup.html" target="_blank" rel="noopener">Process cleanup</a>
, documents all five mechanisms processx provides for ensuring subprocesses don&rsquo;t outlive their intended scope:</p>
<ol>
<li>Explicit cleanup with 






<a href="https://rdrr.io/r/base/on.exit.html" target="_blank" rel="noopener"><code>on.exit()</code></a>
 &mdash; always deterministic.</li>
<li>Automatic cleanup on garbage collection (<code>cleanup = TRUE</code>, the default).</li>
<li>Process-tree cleanup (<code>cleanup_tree = TRUE</code>).</li>
<li>Linux parent-death signal (<code>linux_pdeathsig</code>) &mdash; Linux only, handles R crashes.</li>
<li>Supervisor process (<code>supervise = TRUE</code>) &mdash; all platforms, handles R crashes.</li>
</ol>
<h3 id="death-signal-support-on-linux">Death signal support on Linux
</h3>
<p>On Linux, you can now tell the kernel to deliver a signal to the child process automatically if the parent R process exits &mdash; even if R crashes. Set <code>linux_pdeathsig = TRUE</code> to send <code>SIGTERM</code>, or pass an integer signal number 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="n">p</span> <span class="o">&lt;-</span> <span class="n">process</span><span class="o">$</span><span class="nf">new</span><span class="p">(</span><span class="s">&#34;sleep&#34;</span><span class="p">,</span> <span class="s">&#34;100&#34;</span><span class="p">,</span> <span class="n">linux_pdeathsig</span> <span class="o">=</span> <span class="kc">TRUE</span><span class="p">)</span></span></span></code></pre></div></div>
<p>This is useful when you want child processes to clean up after an R crash, without the overhead of running a supervisor. The argument is silently ignored on macOS and Windows.</p>
<h3 id="record-the-time-when-a-process-exits">Record the time when a process exits
</h3>
<p><code>process$get_end_time()</code> returns the time when the process exited as a <code>POSIXct</code>, or <code>NULL</code> if it is still running. This makes it straightforward to measure wall-clock duration without having to record timestamps yourself:</p>
<div class="highlight">
<pre class='chroma'><code class='language-r' data-lang='r'><span><span class='nv'>p</span> <span class='o'>&lt;-</span> <span class='nv'><a href='http://processx.r-lib.org/reference/process.html'>process</a></span><span class='o'>$</span><span class='nf'>new</span><span class='o'>(</span><span class='s'>"sleep"</span>, <span class='s'>"1"</span><span class='o'>)</span></span>
<span><span class='nv'>p</span><span class='o'>$</span><span class='nf'>wait</span><span class='o'>(</span><span class='o'>)</span></span>
<span><span class='nv'>p</span><span class='o'>$</span><span class='nf'>get_end_time</span><span class='o'>(</span><span class='o'>)</span> <span class='o'>-</span> <span class='nv'>p</span><span class='o'>$</span><span class='nf'>get_start_time</span><span class='o'>(</span><span class='o'>)</span></span>
<span><span class='c'>#&gt; Time difference of 1.010295 secs</span></span>
<span></span></code></pre>
</div>
<h3 id="append-stdoutstderr-to-files">Append stdout/stderr to files
</h3>
<p><code>process$new()</code> and 






<a href="http://processx.r-lib.org/reference/run.html" target="_blank" rel="noopener"><code>run()</code></a>
 now support <code>&quot;&gt;&gt;&quot;</code> as a prefix for <code>stdout</code> and <code>stderr</code> file paths to append output instead of truncating the file:</p>
<div class="highlight">
<pre class='chroma'><code class='language-r' data-lang='r'><span><span class='nv'>log</span> <span class='o'>&lt;-</span> <span class='nf'><a href='https://rdrr.io/r/base/tempfile.html'>tempfile</a></span><span class='o'>(</span><span class='o'>)</span></span>
<span><span class='nf'><a href='http://processx.r-lib.org/reference/run.html'>run</a></span><span class='o'>(</span><span class='s'>"echo"</span>, args <span class='o'>=</span> <span class='s'>"first line"</span>, stdout <span class='o'>=</span> <span class='nv'>log</span><span class='o'>)</span></span>
<span><span class='c'>#&gt; $status</span></span>
<span><span class='c'>#&gt; [1] 0</span></span>
<span><span class='c'>#&gt; </span></span>
<span><span class='c'>#&gt; $stdout</span></span>
<span><span class='c'>#&gt; NULL</span></span>
<span><span class='c'>#&gt; </span></span>
<span><span class='c'>#&gt; $stderr</span></span>
<span><span class='c'>#&gt; [1] ""</span></span>
<span><span class='c'>#&gt; </span></span>
<span><span class='c'>#&gt; $timeout</span></span>
<span><span class='c'>#&gt; [1] FALSE</span></span>
<span></span><span><span class='nf'><a href='http://processx.r-lib.org/reference/run.html'>run</a></span><span class='o'>(</span><span class='s'>"echo"</span>, args <span class='o'>=</span> <span class='s'>"second line"</span>, stdout <span class='o'>=</span> <span class='nf'><a href='https://rdrr.io/r/base/paste.html'>paste0</a></span><span class='o'>(</span><span class='s'>"&gt;&gt;"</span>, <span class='nv'>log</span><span class='o'>)</span><span class='o'>)</span></span>
<span><span class='c'>#&gt; $status</span></span>
<span><span class='c'>#&gt; [1] 0</span></span>
<span><span class='c'>#&gt; </span></span>
<span><span class='c'>#&gt; $stdout</span></span>
<span><span class='c'>#&gt; NULL</span></span>
<span><span class='c'>#&gt; </span></span>
<span><span class='c'>#&gt; $stderr</span></span>
<span><span class='c'>#&gt; [1] ""</span></span>
<span><span class='c'>#&gt; </span></span>
<span><span class='c'>#&gt; $timeout</span></span>
<span><span class='c'>#&gt; [1] FALSE</span></span>
<span></span><span><span class='nf'><a href='https://rdrr.io/r/base/readLines.html'>readLines</a></span><span class='o'>(</span><span class='nv'>log</span><span class='o'>)</span></span>
<span><span class='c'>#&gt; [1] "first line"  "second line"</span></span>
<span></span></code></pre>
</div>
<p>This is handy when you run the same process repeatedly and want to accumulate output in a single log file.</p>
<h3 id="binary-standard-output-and-error">Binary standard output and error
</h3>
<p>






<a href="http://processx.r-lib.org/reference/run.html" target="_blank" rel="noopener"><code>run()</code></a>
 and <code>process$new()</code> now support <code>encoding = &quot;binary&quot;</code> to capture raw bytes. In binary mode, 






<a href="http://processx.r-lib.org/reference/run.html" target="_blank" rel="noopener"><code>run()</code></a>
 returns <code>stdout</code> and <code>stderr</code> as raw vectors, and <code>process$read_output()</code> / <code>process$read_error()</code> return raw vectors rather than character strings. All bytes are preserved exactly, including null bytes and non-UTF-8 sequences.</p>
<div class="highlight">
<pre class='chroma'><code class='language-r' data-lang='r'><span><span class='nv'>result</span> <span class='o'>&lt;-</span> <span class='nf'><a href='http://processx.r-lib.org/reference/run.html'>run</a></span><span class='o'>(</span><span class='s'>"cat"</span>, args <span class='o'>=</span> <span class='s'>"/bin/ls"</span>, encoding <span class='o'>=</span> <span class='s'>"binary"</span><span class='o'>)</span></span>
<span><span class='nf'><a href='https://rdrr.io/r/base/typeof.html'>typeof</a></span><span class='o'>(</span><span class='nv'>result</span><span class='o'>$</span><span class='nv'>stdout</span><span class='o'>)</span></span>
<span><span class='c'>#&gt; [1] "raw"</span></span>
<span></span><span><span class='nf'><a href='https://rdrr.io/r/base/length.html'>length</a></span><span class='o'>(</span><span class='nv'>result</span><span class='o'>$</span><span class='nv'>stdout</span><span class='o'>)</span></span>
<span><span class='c'>#&gt; [1] 154624</span></span>
<span></span></code></pre>
</div>
<p>Two new methods, <code>process$read_output_bytes()</code> and <code>process$read_error_bytes()</code>, and the 






<a href="http://processx.r-lib.org/reference/processx_connections.html" target="_blank" rel="noopener"><code>conn_read_bytes()</code></a>
 function, provide direct access to raw bytes from processx connections.</p>
<h2 id="acknowledgements">Acknowledgements
</h2>
<p>Thanks to everyone who contributed to processx 3.9.0 through code, issues, testing, and feedback:</p>
<p>






<a href="https://github.com/advieser" target="_blank" rel="noopener">@advieser</a>
, 






<a href="https://github.com/cderv" target="_blank" rel="noopener">@cderv</a>
, 






<a href="https://github.com/chwpearse" target="_blank" rel="noopener">@chwpearse</a>
, 






<a href="https://github.com/HenrikBengtsson" target="_blank" rel="noopener">@HenrikBengtsson</a>
, 






<a href="https://github.com/king-of-poppk" target="_blank" rel="noopener">@king-of-poppk</a>
, 






<a href="https://github.com/r2evans" target="_blank" rel="noopener">@r2evans</a>
, 






<a href="https://github.com/sckott" target="_blank" rel="noopener">@sckott</a>
, 






<a href="https://github.com/sda030" target="_blank" rel="noopener">@sda030</a>
, 






<a href="https://github.com/stupidpupil" target="_blank" rel="noopener">@stupidpupil</a>
, and 






<a href="https://github.com/Yunuuuu" target="_blank" rel="noopener">@Yunuuuu</a>
.</p>
]]></description>
      <enclosure url="https://opensource.posit.co/blog/2026-04-27_processx-3-9-0/thumbnail-wd.jpg" length="368455" type="image/jpeg" />
    </item>
    <item>
      <title>2026-04-24 AI Newsletter</title>
      <link>https://opensource.posit.co/blog/2026-04-24_ai-newsletter/</link>
      <pubDate>Fri, 24 Apr 2026 00:00:00 +0000</pubDate>
      <guid>https://opensource.posit.co/blog/2026-04-24_ai-newsletter/</guid>
      <dc:creator>Sara Altman</dc:creator>
      <dc:creator>Simon Couch</dc:creator><description><![CDATA[<blockquote>
<p><em>The AI newsletter has moved! From this edition forward, we&rsquo;ll be publishing here, on the Open Source website. You can read past editions on the main 






<a href="https://posit.co/blog" target="_blank" rel="noopener">Posit blog</a>
.</em></p>
</blockquote>
<h2 id="external-news">External news
</h2>
<h3 id="ai-and-cybersecurity">AI and cybersecurity
</h3>
<p>Anthropic recently announced <strong>






<a href="https://www.anthropic.com/glasswing" target="_blank" rel="noopener">Project Glasswing</a>
, an effort to bolster critical software against the threat of AI systems that are increasingly capable of finding and exploiting security vulnerabilities.</strong> Through the project, a number of organizations that maintain critical software will be allotted credits to Claude Mythos, Anthropic&rsquo;s yet-to-be-publicly-released model that reportedly found high-severity security issues in 






<a href="https://red.anthropic.com/2026/mythos-preview/" target="_blank" rel="noopener">&ldquo;every major operating system and web browser&rdquo;</a>
. The goal is to give these organizations a head start in finding and patching these vulnerabilities before bad actors have access to the same technology.</p>
<h3 id="a-step-change-for-local-coding-agents">A step change for local coding agents
</h3>
<p>One of the most common questions we receive when releasing LLM-enabled software is &ldquo;Can I use this with a local model?&rdquo; Up until this point, we&rsquo;ve largely discouraged using our products with LLMs small enough that you could run them on a laptop. 






<a href="https://posit.co/blog/local-models-are-not-there-yet" target="_blank" rel="noopener">Local models are not there (yet)</a>
 showed that, at the time, models small enough to run on a laptop failed a simple refactoring task every time, while the frontier, cloud-hosted models were largely successful.</p>
<p><strong>However, the last couple weeks have brought two important releases for the local model space—






<a href="https://blog.google/innovation-and-ai/technology/developers-tools/gemma-4/" target="_blank" rel="noopener">Gemma 4</a>
 and 






<a href="https://qwen.ai/blog?id=qwen3.6-35b-a3b" target="_blank" rel="noopener">Qwen 3.6</a>
. Both of these new models 






<a href="https://simonpcouch.com/blog/2026-04-16-local-agents-2/" target="_blank" rel="noopener">successfully completed the refactoring task in 9 of 10 attempts</a>
.</strong></p>
<p><div class="not-prose"><figure>
    <img class="h-auto max-w-full rounded-lg"
      src="https://opensource.posit.co/blog/2026-04-24_ai-newsletter/images/local-models.png"
      alt="Horizontal bar chart titled &lsquo;Agentic Coding Reliability.&rsquo; Three groups of models are compared on percent correct (0–100). Frontier models (Gemini Pro 3.1, Claude Sonnet 4.5, and GPT-4.1) all score between approximately 80 and 100 percent. Local models from 4 months ago (Qwen 3 14B, Mistral 3.1 24B, and GPT OSS 20B) all score 0 percent. Today&rsquo;s local models (Qwen 3.5 35B-A3B and Gemma 4 26B-A4B) score approximately 90 percent, comparable to the frontier models." 
      loading="lazy"
    >
  </figure></div>
</p>
<h2 id="posit-news">Posit news
</h2>
<p>Posit recently announced <strong>






<a href="https://opensource.posit.co/blog/2026-04-20_ggsql_alpha_release/" target="_blank" rel="noopener">ggsql</a>
, an implementation of the grammar of graphics for SQL.</strong> One of the motivations for ggsql is safer AI agent tooling for visualization. With ggsql, rather than giving a visualization agent a full R or Python runtime (where the agent could execute arbitrary code, write files, or make network calls), you can provide it with a SQL runtime instead. SQL limits the agent to database operations, and you can restrict even those by connecting read-only, reducing the risk that the agent will do something undesirable.</p>
<p><div class="not-prose"><figure>
    <img class="h-auto max-w-full rounded-lg"
      src="https://opensource.posit.co/blog/2026-04-24_ai-newsletter/images/ggsql.png"
      alt="A ggsql example showing a stacked bar chart of penguin counts by island, colored by species, generated from the SQL query &lsquo;VISUALIZE island AS x, species AS color FROM ggsql:penguins DRAW bar&rsquo;." 
      loading="lazy"
    >
  </figure></div>
</p>
<h2 id="terms">Terms
</h2>
<p>A <strong>dense model</strong> is one where every parameter is used to process every token. For example, if a model has 14 billion parameters, in a dense model, all 14 billion are involved in generating each token.</p>
<p>Dense models are in contrast to sparse models, a subset of which use an architecture called <strong>Mixture of Experts (MoE)</strong>. In MoE models, only a fraction of the model&rsquo;s parameters are involved at any given time. The model contains many parallel sub-networks called &ldquo;experts,&rdquo; and a small router decides which ones to activate for each token. This means a model only needs to pay the computational cost of the small active subset per token.</p>
<p>This is the architecture of the most recent local models, and partially explains the jump in ability we saw. Gemma 4 26B-A4B, for example, has 26 billion total parameters but only activates 4 billion per token (the &ldquo;A&rdquo; is for &ldquo;active parameters&rdquo;). The models we tested from the previous generation were dense, requiring you to choose between a model small enough to run on a laptop or one that was good enough to be useful. The MoE architecture helps break that tradeoff.</p>
<h2 id="learn-more">Learn more
</h2>
<ul>
<li><strong>Anthropic released 






<a href="https://www.anthropic.com/news/claude-opus-4-7" target="_blank" rel="noopener">Opus 4.7</a>
</strong>, an incremental improvement over Opus 4.6. <strong>OpenAI released 






<a href="https://openai.com/index/scaling-trusted-access-for-cyber-defense/" target="_blank" rel="noopener">GPT-5.4-Cyber</a>
</strong>, a fine-tune of GPT 5.4 for cyberdefense.</li>
<li>A 






<a href="https://arxiv.org/abs/2603.21687" target="_blank" rel="noopener">new paper</a>
 shows that <strong>vision language models excel at many image benchmarks even when the actual images aren&rsquo;t provided</strong>, because the answers are implicit in the questions.</li>
<li>Posit&rsquo;s Jeremy Allen has been keeping a 






<a href="https://www.thetreeline.pub/p/ais-transformation-of-work" target="_blank" rel="noopener">log of the ways AI is transforming work</a>
.</li>
</ul>
]]></description>
      <enclosure url="https://opensource.posit.co/blog/2026-04-24_ai-newsletter/images/featured.png" length="1641281" type="image/png" />
    </item>
  </channel>
</rss>
