<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:fh="http://purl.org/syndication/history/1.0" xmlns:nimpress="https://github.com/nimling/nimpress">
  <channel>
    <title>Changelog</title>
    <link>https://developer.samna.io/tools/nimpress/changelog</link>
    <description>Samna developer documentation</description>
    <atom:link href="https://developer.samna.io/tools/nimpress/changelog/rss.xml" rel="self" type="application/rss+xml"/>
    <lastBuildDate>Wed, 12 Aug 2026 00:00:00 GMT</lastBuildDate>
    <ttl>4874</ttl>
    <sy:updatePeriod>weekly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <nimpress:releaseCadence>292431</nimpress:releaseCadence>
    <nimpress:latestVersion>2.2.0</nimpress:latestVersion>
    <nimpress:releaseCount>14</nimpress:releaseCount>
    <item>
      <title>v2.2.0 Subscribe from the header</title>
      <link>https://developer.samna.io/tools/nimpress/changelog#v2.2.0</link>
      <guid isPermaLink="true">https://developer.samna.io/tools/nimpress/changelog#v2.2.0</guid>
      <pubDate>Wed, 12 Aug 2026 00:00:00 GMT</pubDate>
      <description>The subscribe control sits in the site header on any page that offers a feed.</description>
      <content:encoded><![CDATA[<p>Subscribing is reachable from the same place on every page that carries a feed.</p>
<h2 id="the-control-moves-to-the-header" tabindex="-1"><a class="header-anchor" href="#the-control-moves-to-the-header"><span>The control moves to the header</span></a></h2>
<p>A page that enables a feed publishes its title, feed address, and whether email subscription is offered. The header reads that and renders the subscribe control beside search, the theme toggle, and the account menu. The dialog opens from there with the same options it always carried, the feed address and the email form when the page enables it.</p>
<p>The control leaves the changelog page heading, so the heading is now the title alone. On narrow viewports the control keeps its icon and drops its label to hold the header on one line.</p>
<p>Any page type can offer the control by publishing the same feed state, so it is no longer tied to the changelog renderer.</p>
]]></content:encoded>
    </item>
    <item>
      <title>v2.1.5 Guarded module resolution</title>
      <link>https://developer.samna.io/tools/nimpress/changelog#v2.1.5</link>
      <guid isPermaLink="true">https://developer.samna.io/tools/nimpress/changelog#v2.1.5</guid>
      <pubDate>Tue, 11 Aug 2026 00:00:00 GMT</pubDate>
      <description>A guarded module loads from the address it was published to rather than a guessed one.</description>
      <content:encoded><![CDATA[<p>Guarded pages reach their modules through the address the publish step recorded.</p>
<h2 id="published-pairing" tabindex="-1"><a class="header-anchor" href="#published-pairing"><span>Published pairing</span></a></h2>
<p>A guarded bundle resolves each file against the pairing written when the bundle was published, matching on the bundle and the file it ends with, and falls back to the bundle path only when no pairing exists. A guarded module whose stored address differs from its source path now loads instead of returning nothing.</p>
]]></content:encoded>
    </item>
    <item>
      <title>v2.1.4 Svelte harness stories</title>
      <link>https://developer.samna.io/tools/nimpress/changelog#v2.1.4</link>
      <guid isPermaLink="true">https://developer.samna.io/tools/nimpress/changelog#v2.1.4</guid>
      <pubDate>Tue, 11 Aug 2026 00:00:00 GMT</pubDate>
      <description>Stories in a svelte component system mount reliably in the workshop harness.</description>
      <content:encoded><![CDATA[<p>Svelte stories mount in the workshop without failing on load.</p>
<h2 id="harness-context" tabindex="-1"><a class="header-anchor" href="#harness-context"><span>Harness context</span></a></h2>
<p>A svelte system's harness reads its context into a local before the state rune uses it, so the rune sits in a declaration initializer where svelte expects it. Stories that mounted through a svelte harness now load on the first try instead of erroring during setup.</p>
]]></content:encoded>
    </item>
    <item>
      <title>v2.1.3 Back to top and a close icon</title>
      <link>https://developer.samna.io/tools/nimpress/changelog#v2.1.3</link>
      <guid isPermaLink="true">https://developer.samna.io/tools/nimpress/changelog#v2.1.3</guid>
      <pubDate>Tue, 11 Aug 2026 00:00:00 GMT</pubDate>
      <description>The back to top button appears and returns you to the top, and the subscribe dialog closes from an icon.</description>
      <content:encoded><![CDATA[<p>Two pieces of page chrome behave the way they read.</p>
<h2 id="back-to-top" tabindex="-1"><a class="header-anchor" href="#back-to-top"><span>Back to top</span></a></h2>
<p>The button slides in once you are far enough down a page, and pressing it returns you to the top.</p>
<p>It watched for scrolling in a place the page never scrolls, so it stayed hidden no matter how far down a reader went, and pressing it moved nothing. It now watches the column the page actually scrolls in and returns that column to the top.</p>
<h2 id="closing-the-subscribe-dialog" tabindex="-1"><a class="header-anchor" href="#closing-the-subscribe-dialog"><span>Closing the subscribe dialog</span></a></h2>
<p>The subscribe dialog closes from an icon button in its header rather than a small text label. Pressing escape still closes it.</p>
]]></content:encoded>
    </item>
    <item>
      <title>v2.1.2 Live hash links and roadmap icons</title>
      <link>https://developer.samna.io/tools/nimpress/changelog#v2.1.2</link>
      <guid isPermaLink="true">https://developer.samna.io/tools/nimpress/changelog#v2.1.2</guid>
      <pubDate>Tue, 11 Aug 2026 00:00:00 GMT</pubDate>
      <description>The address bar follows the heading you are reading and roadmap rows carry an icon for their issue type.</description>
      <content:encoded><![CDATA[<p>Deep links keep up with the reader again, and a roadmap sidebar shows what kind of work each row is.</p>
<h2 id="the-hash-follows-the-reader" tabindex="-1"><a class="header-anchor" href="#the-hash-follows-the-reader"><span>The hash follows the reader</span></a></h2>
<p>Scrolling through a page updates the address bar to the heading in view, so copying the URL at any point shares the exact section on screen.</p>
<p>The page shell fills the viewport and scrolls inside its own column rather than scrolling the document, so the listener watching for scrolling never heard anything and the hash only changed when a reader clicked an entry in the right rail. It now listens where the scrolling actually happens.</p>
<p>This reaches every page that carries a table of contents: doc pages, changelog collections, roadmaps, and the API reference.</p>
<h2 id="icons-for-issue-types" tabindex="-1"><a class="header-anchor" href="#icons-for-issue-types"><span>Icons for issue types</span></a></h2>
<p>A roadmap sidebar row carries a glyph for its kind rather than the kind spelled out. A milestone shows a flag, an epic shows a box, a feature shows a sparkle, and a bug shows an alert. The row reads as an icon followed by the issue title.</p>
]]></content:encoded>
    </item>
    <item>
      <title>v2.1.1 Deeper controls and a cycle guard</title>
      <link>https://developer.samna.io/tools/nimpress/changelog#v2.1.1</link>
      <guid isPermaLink="true">https://developer.samna.io/tools/nimpress/changelog#v2.1.1</guid>
      <pubDate>Sat, 01 Aug 2026 00:00:00 GMT</pubDate>
      <description>Nested component props expand further in the workshop and a build fails on a cyclic chunk instead of shipping one.</description>
      <content:encoded><![CDATA[<p>Deeply nested props reach their real controls, and a build that would crash in the browser fails at build time instead.</p>
<h2 id="deeper-control-trees" tabindex="-1"><a class="header-anchor" href="#deeper-control-trees"><span>Deeper control trees</span></a></h2>
<p>The walk from a schema to its controls expands nested members twelve levels deep. An array of groups holding lanes holding entries holding a style block holding colors now resolves to real controls the whole way down, where it stopped at an opaque leaf before.</p>
<p>A member past the bound still becomes a raw editor and the warning names it, so a warning on a member that carries its own properties is the bound speaking rather than the schema.</p>
<h2 id="chunk-cycle-guard" tabindex="-1"><a class="header-anchor" href="#chunk-cycle-guard"><span>Chunk cycle guard</span></a></h2>
<p>Every build checks the emitted bundle for chunks that import each other, for the site and for each component system. A cycle fails the build and prints each cycle as a chunk path with the source module behind every chunk.</p>
<p>The failure this catches only appears in a deployed static site. Dev serves modules unbundled and works, while the built site throws on whichever cyclic chunk evaluates first and every page loading either chunk renders nothing. <code>nimpress lint</code> builds, so the guard runs there and in continuous integration with no configuration.</p>
<p>The fix is always on the shared side: a registry or menu that statically imports a component loads it through a dynamic import instead.</p>
]]></content:encoded>
    </item>
    <item>
      <title>v2.1.0 Sidebar icons and a steadier rail</title>
      <link>https://developer.samna.io/tools/nimpress/changelog#v2.1.0</link>
      <guid isPermaLink="true">https://developer.samna.io/tools/nimpress/changelog#v2.1.0</guid>
      <pubDate>Sun, 26 Jul 2026 00:00:00 GMT</pubDate>
      <description>Sidebar rows take an icon as text or artwork and the page rail keeps its place without a mirror column.</description>
      <content:encoded><![CDATA[<p>Sidebar rows carry real artwork, the root page can join the sidebar as a top level item, and the page rail holds its position through container queries.</p>
<h2 id="icons-on-sidebar-rows" tabindex="-1"><a class="header-anchor" href="#icons-on-sidebar-rows"><span>Icons on sidebar rows</span></a></h2>
<p>An <code>icon</code> on a <code>sidebar</code> block or on a story definition accepts three kinds of value.</p>
<ol>
<li>
<p>Literal text or ascii art, rendered verbatim in the mono font.</p>
</li>
<li>
<p>Inline <code>&lt;svg&gt;</code> markup, rendered as markup.</p>
</li>
<li>
<p>A path ending in <code>.svg</code>, resolved against the declaring file or against the content root when it starts with a slash, and inlined at build time.</p>
</li>
</ol>
<p>An unreadable path warns and drops the icon. Icons render from <code>sidebar</code> blocks and story definitions only.</p>
<h2 id="the-root-page-in-the-sidebar" tabindex="-1"><a class="header-anchor" href="#the-root-page-in-the-sidebar"><span>The root page in the sidebar</span></a></h2>
<p>The root index joins the sidebar through its own <code>sidebar</code> block placed at <code>sidebar.path</code>, so the site landing page becomes a clickable top level item rather than an entry with no row.</p>
<h2 id="page-rail" tabindex="-1"><a class="header-anchor" href="#page-rail"><span>Page rail</span></a></h2>
<p>The right rail switches to container queries on the page shell and the mirror column beside it is gone. The rail holds its place as the shell resizes instead of reacting to the viewport.</p>
<h2 id="story-rows" tabindex="-1"><a class="header-anchor" href="#story-rows"><span>Story rows</span></a></h2>
<p>A story decorates its own sidebar row through a <code>sidebar</code> object carrying <code>name</code>, <code>icon</code>, and <code>style</code>. The link stays keyed on the resolved story name, so only the label changes and the URL does not.</p>
<p>An underscore in a story file name reads as a space when the name falls back to the file name, so a file named for a case renders a readable row.</p>
<h2 id="dev-rebuilds" tabindex="-1"><a class="header-anchor" href="#dev-rebuilds"><span>Dev rebuilds</span></a></h2>
<p>Page bodies and page component modules invalidate with their registered ids, so editing a story file rebuilds the component data behind the page it belongs to.</p>
]]></content:encoded>
    </item>
    <item>
      <title>v2.0.0 One paths config</title>
      <link>https://developer.samna.io/tools/nimpress/changelog#v2.0.0</link>
      <guid isPermaLink="true">https://developer.samna.io/tools/nimpress/changelog#v2.0.0</guid>
      <pubDate>Wed, 22 Jul 2026 00:00:00 GMT</pubDate>
      <description>Every folder nimpress writes is declared in one place and the caches move under a single root.</description>
      <content:encoded><![CDATA[<p>Every folder the tool writes is declared in one optional <code>paths</code> block, each field carrying a default that keeps existing URLs unchanged.</p>
<h2 id="the-paths-block" tabindex="-1"><a class="header-anchor" href="#the-paths-block"><span>The paths block</span></a></h2>
<ol>
<li>
<p><code>paths.out</code> is the build root and defaults to <code>dist</code>.</p>
</li>
<li>
<p><code>paths.cache</code> is the single cache root and defaults to <code>node_modules/.nimpress</code>. The docs build cache, the harness cache for each component system, and the lint verification build all layer beneath it.</p>
</li>
<li>
<p><code>paths.export</code> is the folder the export command fills for the docs pipeline and defaults to <code>.nimpress</code>.</p>
</li>
<li>
<p><code>paths.modules</code> and <code>paths.guarded</code> are both the folders under the build root and the URL segments they are served at, defaulting to <code>_components</code> and <code>_guarded</code>.</p>
</li>
</ol>
<h2 id="breaking-change" tabindex="-1"><a class="header-anchor" href="#breaking-change"><span>Breaking change</span></a></h2>
<p><strong>Breaking.</strong> <code>outDir</code> folds into <code>paths.out</code>. A config still setting <code>outDir</code> needs it moved.</p>
<h2 id="lint-leaves-the-repo-root" tabindex="-1"><a class="header-anchor" href="#lint-leaves-the-repo-root"><span>Lint leaves the repo root</span></a></h2>
<p>The lint verification build writes under the cache root rather than the repo root, and cleans up after itself when the run is interrupted.</p>
<h2 id="warnings-stop-repeating" tabindex="-1"><a class="header-anchor" href="#warnings-stop-repeating"><span>Warnings stop repeating</span></a></h2>
<p>Schema coaching warnings collect into one registry keyed by position, deduplicate, and flush once, grouped per component with the source file and a best effort line and a single suggested fix. They print after the dev banner, after each rebuild, and at the end of an update, an import, and a lint.</p>
<p>The automatic schema upsert in dev is guarded by a hash of the source content, so writing a schema no longer retriggers the pass that wrote it. The rebuild loop and the warning flood it produced are gone.</p>
]]></content:encoded>
    </item>
    <item>
      <title>v1.7.0 Authored component schemas</title>
      <link>https://developer.samna.io/tools/nimpress/changelog#v1.7.0</link>
      <guid isPermaLink="true">https://developer.samna.io/tools/nimpress/changelog#v1.7.0</guid>
      <pubDate>Mon, 20 Jul 2026 00:00:00 GMT</pubDate>
      <description>A component schema becomes an authored file that seeds itself from the source and reports what is left undocumented.</description>
      <content:encoded><![CDATA[<p>The schema beside a component page turns into the one place a component is described. It seeds itself from the component types, and from there defaults, enums, descriptions, and mocks are authored by hand.</p>
<h2 id="the-schema-file" tabindex="-1"><a class="header-anchor" href="#the-schema-file"><span>The schema file</span></a></h2>
<p>Each component page carries one <code>schema.json</code> or <code>schema.yml</code> beside it. The workshop controls, the validation, and the mock selection all read from that file.</p>
<ol>
<li>
<p><code>nimpress modules update</code> upserts source changes into the schema: new props, slots, and events are added and changed type shapes refresh on existing members.</p>
</li>
<li>
<p>Authored fields are never overwritten. A member that disappears from the source stays in the file and is flagged so the author decides.</p>
</li>
<li>
<p>In <code>nimpress dev</code> the upsert runs on every component change.</p>
</li>
<li>
<p><code>data.schema</code> in page frontmatter layers over the file for a small curation that does not warrant editing it.</p>
</li>
</ol>
<h2 id="coaching-warnings" tabindex="-1"><a class="header-anchor" href="#coaching-warnings"><span>Coaching warnings</span></a></h2>
<p>Every pass reports what a reader of the library would miss: props without a description, opaque types, and schema members with no counterpart in the source. The warnings print without failing the run.</p>
<h2 id="sizing-belongs-to-the-story" tabindex="-1"><a class="header-anchor" href="#sizing-belongs-to-the-story"><span>Sizing belongs to the story</span></a></h2>
<p>A story that needs a width or a height owns it in its own markup or in a harness component it wraps itself in. The workshop frame centers whatever the story mounts and scrolls it when it is larger than the frame.</p>
<h2 id="control-surface" tabindex="-1"><a class="header-anchor" href="#control-surface"><span>Control surface</span></a></h2>
<ol>
<li>
<p>Enum options render as chips, and a long list collapses to one line that expands on hover.</p>
</li>
<li>
<p>A default button at the panel header resets every control to its schema default, and one at the events header resets every handler to the logging stub.</p>
</li>
<li>
<p>Filling a value by mock excludes options already present, so a second click lands on a fresh one.</p>
</li>
<li>
<p>Number mocks respect the minimum and maximum the schema declares.</p>
</li>
<li>
<p>The canvas pans on a middle press or a modifier click, and a modifier double click recenters it.</p>
</li>
</ol>
<h2 id="decoration-only-pages" tabindex="-1"><a class="header-anchor" href="#decoration-only-pages"><span>Decoration only pages</span></a></h2>
<p>An index file carrying frontmatter and no body decorates its sidebar group without becoming a page of its own. Such a file needs only <code>sidebar.name</code>, and content lint judges a page by its body so the file passes.</p>
]]></content:encoded>
    </item>
    <item>
      <title>v1.6.0 Page visibility and a composable harness</title>
      <link>https://developer.samna.io/tools/nimpress/changelog#v1.6.0</link>
      <guid isPermaLink="true">https://developer.samna.io/tools/nimpress/changelog#v1.6.0</guid>
      <pubDate>Thu, 16 Jul 2026 00:00:00 GMT</pubDate>
      <description>Pages declare where they appear through one visibility field and a component harness composes from primitives.</description>
      <content:encoded><![CDATA[<p>Where a page appears is now one field with three states. The component harness stops being a fixed frame and becomes a set of primitives a library arranges itself.</p>
<h2 id="page-visibility" tabindex="-1"><a class="header-anchor" href="#page-visibility"><span>Page visibility</span></a></h2>
<p><strong>Breaking.</strong> A page declares where it appears through <code>visibility</code>, which takes <code>visible</code>, <code>hidden</code>, or <code>dev-only</code>. The boolean flag that carried this before is gone from the schema.</p>
<ol>
<li>
<p><code>visible</code> is the default and needs no declaration.</p>
</li>
<li>
<p><code>hidden</code> takes the page out of the sidebar, out of search, and out of the build.</p>
</li>
<li>
<p><code>dev-only</code> keeps the page in <code>nimpress dev</code> and leaves it out of the built bundle. Its sidebar row carries a red dot so the author can see at a glance that the page does not ship.</p>
</li>
</ol>
<p>The frontmatter schema accepts unknown fields without failing, so a page still carrying the boolean builds and publishes as a normal public page. Any page meant to stay unpublished has to be moved to <code>visibility</code> to keep its intent.</p>
<h2 id="composable-harness" tabindex="-1"><a class="header-anchor" href="#composable-harness"><span>Composable harness</span></a></h2>
<p>A component system declares its frame instead of relying on discovery. <code>ComponentHarness</code>, <code>ComponentStory</code>, <code>ComponentHarnessEffects</code>, and <code>ComponentHarnessOverlay</code> import from the harness package and arrange freely, and a system that declares no harness gets the default composition.</p>
<ol>
<li>
<p>The <code>harness</code> field on a system points at the component wrapping every story.</p>
</li>
<li>
<p>The <code>harness</code> field on a single story wraps that one story inside the system harness.</p>
</li>
<li>
<p>The <code>setup</code> module bootstraps the app, installing plugins and providers on every created app before mount.</p>
</li>
</ol>
<h2 id="sidebar-groups-from-frontmatter" tabindex="-1"><a class="header-anchor" href="#sidebar-groups-from-frontmatter"><span>Sidebar groups from frontmatter</span></a></h2>
<p>A <code>sidebar</code> block nests a page under a named group without changing its URL. A declared group sits beside the physical folders rather than inside one. On a folder index the block decorates that folder's own row.</p>
<h2 id="guarded-bundles" tabindex="-1"><a class="header-anchor" href="#guarded-bundles"><span>Guarded bundles</span></a></h2>
<p>Guarded pages build into their own folders keyed by the bundle the guard function names, and the build writes the map of page to bundle alongside them.</p>
<h2 id="workshop-panels" tabindex="-1"><a class="header-anchor" href="#workshop-panels"><span>Workshop panels</span></a></h2>
<p>The props panel and the console panel each dock to the bottom or the right of the workshop and split the slot when they share it. The console streams everything the component frame logs, including window errors, and takes typed expressions that evaluate inside the frame.</p>
<ol>
<li>
<p>Events render as control rows with an always visible editor holding a real handler function.</p>
</li>
<li>
<p>A handler compiles in the frame and counts its firings on the row.</p>
</li>
</ol>
<h2 id="page-rail" tabindex="-1"><a class="header-anchor" href="#page-rail"><span>Page rail</span></a></h2>
<p>The right rail lives in a grid gutter on the page shell, so the content column stays centered and the rail stops clipping at narrow widths.</p>
<h2 id="diagrams" tabindex="-1"><a class="header-anchor" href="#diagrams"><span>Diagrams</span></a></h2>
<p>Mermaid moves into the dependencies of the package, so a consumer resolves it through nimpress rather than installing it separately.</p>
]]></content:encoded>
    </item>
    <item>
      <title>v1.5.0 Story colocation and sidebar decoration</title>
      <link>https://developer.samna.io/tools/nimpress/changelog#v1.5.0</link>
      <guid isPermaLink="true">https://developer.samna.io/tools/nimpress/changelog#v1.5.0</guid>
      <pubDate>Fri, 10 Jul 2026 00:00:00 GMT</pubDate>
      <description>Stories live beside the page they belong to and a sidebar group can carry its own icon and style.</description>
      <content:encoded><![CDATA[<p>Stories attach to the page they sit next to, and a sidebar group gains a look of its own.</p>
<h2 id="stories-sit-beside-their-page" tabindex="-1"><a class="header-anchor" href="#stories-sit-beside-their-page"><span>Stories sit beside their page</span></a></h2>
<p>A story file placed next to a component page belongs to that page. There is no separate registry and no path to configure.</p>
<h2 id="sidebar-groups-carry-decoration" tabindex="-1"><a class="header-anchor" href="#sidebar-groups-carry-decoration"><span>Sidebar groups carry decoration</span></a></h2>
<p>The <code>sidebar</code> block in frontmatter takes an <code>icon</code> and a <code>style</code> alongside its <code>name</code>, so a group row can carry a glyph and inline styling without changing any URL.</p>
<h2 id="hash-links" tabindex="-1"><a class="header-anchor" href="#hash-links"><span>Hash links</span></a></h2>
<p>Following a link that carries a hash lands on the target heading rather than at the top of the page.</p>
]]></content:encoded>
    </item>
    <item>
      <title>v1.4.0 Executable stories and library import</title>
      <link>https://developer.samna.io/tools/nimpress/changelog#v1.4.0</link>
      <guid isPermaLink="true">https://developer.samna.io/tools/nimpress/changelog#v1.4.0</guid>
      <pubDate>Fri, 10 Jul 2026 00:00:00 GMT</pubDate>
      <description>Stories run as real code in the workshop and an existing component library imports in one command.</description>
      <content:encoded><![CDATA[<p>A story stops being a bag of prop values and becomes a file that renders whatever it wants. An existing component library comes across in one command rather than page by page.</p>
<h2 id="stories-that-run" tabindex="-1"><a class="header-anchor" href="#stories-that-run"><span>Stories that run</span></a></h2>
<p>A story file exports a definition through <code>vueStory</code> or <code>svelteStory</code>. A story carrying <code>props</code> seeds the workshop controls as before. A story carrying <code>render</code> returns a mount definition that the harness runs verbatim, so markup, fixture data, and composition all live in the story itself.</p>
<h2 id="importing-a-library" tabindex="-1"><a class="header-anchor" href="#importing-a-library"><span>Importing a library</span></a></h2>
<p><code>nimpress modules import</code> walks a component source tree, reads existing story files for their groups and named cases, seeds a schema per component from its types, and writes a default story for any component that has none.</p>
<ol>
<li>
<p><code>--match</code> narrows the walk to component names matching a pattern.</p>
</li>
<li>
<p><code>--select</code> picks components interactively.</p>
</li>
<li>
<p>Re-running the import upserts and never clears authored content.</p>
</li>
</ol>
<h2 id="workshop-screen" tabindex="-1"><a class="header-anchor" href="#workshop-screen"><span>Workshop screen</span></a></h2>
<p>The workshop shell fills the viewport. The document itself does not scroll; the sidebar, the panels, and the component frame each scroll on their own.</p>
]]></content:encoded>
    </item>
    <item>
      <title>v1.3.0 Component workshop and feed subscriptions</title>
      <link>https://developer.samna.io/tools/nimpress/changelog#v1.3.0</link>
      <guid isPermaLink="true">https://developer.samna.io/tools/nimpress/changelog#v1.3.0</guid>
      <pubDate>Thu, 09 Jul 2026 00:00:00 GMT</pubDate>
      <description>Sites gain a live component workshop, release feeds readers can subscribe to, and a guarded page bundle.</description>
      <content:encoded><![CDATA[<p>A site now carries three surfaces it did not have before: a workshop that renders a component library live, a changelog that readers can follow from their own reader or inbox, and a build that splits guarded pages away from public ones.</p>
<h2 id="component-workshop" tabindex="-1"><a class="header-anchor" href="#component-workshop"><span>Component workshop</span></a></h2>
<p>A folder holding a component page and its stories becomes a live page. The component mounts inside an iframe harness, the controls drive its props, and the stories list beside it in the sidebar.</p>
<ol>
<li>
<p><code>type: component</code> on a markdown page names the system and the component through <code>data.system</code> and <code>data.component</code>.</p>
</li>
<li>
<p>The <code>modules</code> array in the config declares one system per component library, with its framework, source root, package name, and harness port.</p>
</li>
<li>
<p><code>nimpress modules</code> runs the workshop commands: a dev server per system, a static build, and a lint pass.</p>
</li>
</ol>
<h2 id="release-feeds" tabindex="-1"><a class="header-anchor" href="#release-feeds"><span>Release feeds</span></a></h2>
<p>A changelog collection publishes an RSS feed. The page carries a subscribe control that hands the reader the feed URL, and a site wired to an auth endpoint offers an email subscription against the reader's account.</p>
<ol>
<li>
<p><code>rss: true</code> in frontmatter publishes the feed for that collection.</p>
</li>
<li>
<p><code>subscribe: true</code> adds the email option beside the feed URL.</p>
</li>
<li>
<p>The <code>subscribe</code> config block names the endpoint and the app slug the subscription registers under.</p>
</li>
</ol>
<h2 id="guarded-pages" tabindex="-1"><a class="header-anchor" href="#guarded-pages"><span>Guarded pages</span></a></h2>
<p><code>gate</code> marks a page guarded. The build lifts every guarded page out of the public bundle into its own folder and writes a map of what went where, so the public site never carries the bytes of a page the viewer may not read.</p>
<ol>
<li>
<p><code>nimpress guard</code> is the command that reads and reports that map.</p>
</li>
<li>
<p>The <code>auth.guard</code> function in the config decides which bundle a gate value maps to.</p>
</li>
</ol>
<h2 id="authentication" tabindex="-1"><a class="header-anchor" href="#authentication"><span>Authentication</span></a></h2>
<p>The auth config gains callback hooks and a backend for frontend mode, so a site can run its session through a server path rather than the browser alone.</p>
<h2 id="docs-pipeline" tabindex="-1"><a class="header-anchor" href="#docs-pipeline"><span>Docs pipeline</span></a></h2>
<p>A repo keeps its docs in a <code>.nimpress</code> folder and publishes them on a version tag rather than on a branch push. One source can now map into several target folders in the docs site.</p>
]]></content:encoded>
    </item>
    <item>
      <title>v1.2.2 Config driven sites and docs sync</title>
      <link>https://developer.samna.io/tools/nimpress/changelog#v1.2.2</link>
      <guid isPermaLink="true">https://developer.samna.io/tools/nimpress/changelog#v1.2.2</guid>
      <pubDate>Mon, 29 Jun 2026 00:00:00 GMT</pubDate>
      <description>Nimpress sites now run from a single config through a CLI, with content linting and a pipeline that syncs docs from other repos.</description>
      <content:encoded><![CDATA[<h2 id="one-config" tabindex="-1"><a class="header-anchor" href="#one-config"><span>One config</span></a></h2>
<p>A site runs from a <code>nimpress.config</code> file with sensible defaults. The <code>nimpress</code> command serves, builds, previews, and scaffolds with <code>init</code>, so a consumer needs little beyond that file and its markdown.</p>
<h2 id="assets-and-styling" tabindex="-1"><a class="header-anchor" href="#assets-and-styling"><span>Assets and styling</span></a></h2>
<p>Shared assets live in a root folder served at a configurable base, while images next to a page travel with it. A site wide stylesheet named in <code>css</code> and a per page stylesheet sharing a markdown file name override the theme.</p>
<h2 id="content-linting" tabindex="-1"><a class="header-anchor" href="#content-linting"><span>Content linting</span></a></h2>
<p><code>nimpress lint</code> validates the frontmatter of every page, and a build fails on an invalid page, so a broken page cannot ship.</p>
<h2 id="docs-across-repos" tabindex="-1"><a class="header-anchor" href="#docs-across-repos"><span>Docs across repos</span></a></h2>
<p>A repo keeps its docs in a <code>.nimpress</code> folder. On change it notifies the docs site, which mirrors the folder into its tree under a mapped path, lints, and publishes.</p>
]]></content:encoded>
    </item>
  </channel>
</rss>